This commit is contained in:
37
.cursor/.prompt/new.md
Normal file
37
.cursor/.prompt/new.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
7.게시판 열 구분선 잘보이게 요청 / 모바일 최적화 요청 및 구분선,높이 조절 요청
|
||||||
|
8.글삭제기능 어드민추가
|
||||||
|
|
||||||
|
9-1.시크로드 참고 - 제휴업체 리스트, 지역별 / 제휴업체 정보 생성 요청 (제휴업체 프로필,출근부)
|
||||||
|
9-2.제휴업체 및 프로필 등록은, 권한을부여한 제휴업체가 직접 등록하도록 해야합니다
|
||||||
|
9-3.제휴업체 등록 주인이하면안됨, 시크로드 - 제휴문의 - 제휴문의글쓰기 참고요청
|
||||||
|
제휴문의 게시판 ( 관리자 승인후) → 이동
|
||||||
|
제휴업소 리스트 게시판 (노출됨)"
|
||||||
|
|
||||||
|
10.배너관리 이미지 사이즈 규격 및 제휴업체 등록시 이미지 사이즈 규격
|
||||||
|
11,게시판, 주간인기글 , 일간인기글 추가 요청
|
||||||
|
|
||||||
|
12.https://search.google.com/search-console/welcome 등록요청
|
||||||
|
13.메인페이지 제휴업체 배너 이미지 규격작게 수정요청, 한번에 4개이상 정도 보이는 정도
|
||||||
|
14.메인페이지 제휴업체 클릭시 제휴업체 카테고리 하이퍼연결 추가 요청
|
||||||
|
15.레벨별 아이콘 페이지 / https://seekrod.co.kr/bbs/page.php?hid=point 참고
|
||||||
|
16. 게시글 상단고정 기능
|
||||||
|
17. 메인화면 큰 카테고리 빼기
|
||||||
|
18. google SEO: header, meta, description 동적설정
|
||||||
|
2. 회색 배경 영역(비밀글, 글자수)이 글 작성 영역 안에 있어야 함
|
||||||
|
로그인 프로세스 수정: 로그인시 로그인 팝업 모달
|
||||||
|
로그인 로그오프 시, 메인 프로필 디자인 누락되서 추가(지금상태로 가도 무상관)
|
||||||
|
1. 게시글에서 목록돌아가는 버튼 디자인과 다름
|
||||||
|
2. 게시글에서 제목, 내용, 댓글 디자인 다름
|
||||||
|
3. 게시글에서 최하단에 게시글리스트 부분 디자인 다
|
||||||
|
4. 게시글 리스트에서 리스트버튼 하단 divider 색상이 너무 연함
|
||||||
|
5. 표시개수 필요없을 거 같은데, 그냥 고정해버리죠?
|
||||||
|
|
||||||
|
9. 게시글 이미지 배치사이즈
|
||||||
|
10. 외부접속 가입없어도 가능
|
||||||
|
11. 글뷰에서 게시글 리스트로
|
||||||
|
12. 게시글 리스트 디자인
|
||||||
|
13. 포인트 규칙
|
||||||
|
14. 게시판권한 확인
|
||||||
|
15.메인뷰 3열 사이즈 변경
|
||||||
|
16. 로그인 안됐을때 카드 휑함
|
||||||
|
17.이미지 사이즈 미리불러오기
|
||||||
@@ -217,6 +217,7 @@ model Post {
|
|||||||
reports Report[]
|
reports Report[]
|
||||||
stat PostStat?
|
stat PostStat?
|
||||||
viewLogs PostViewLog[]
|
viewLogs PostViewLog[]
|
||||||
|
dailyViews DailyPostView[]
|
||||||
|
|
||||||
@@index([boardId, status, createdAt])
|
@@index([boardId, status, createdAt])
|
||||||
@@index([boardId, isPinned, pinnedOrder])
|
@@index([boardId, isPinned, pinnedOrder])
|
||||||
@@ -438,6 +439,21 @@ model PostStat {
|
|||||||
@@map("post_stats")
|
@@map("post_stats")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 일일 게시글 조회수 (날짜별 집계)
|
||||||
|
model DailyPostView {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
postId String
|
||||||
|
date DateTime // 날짜만 사용 (시간은 00:00:00)
|
||||||
|
viewCount Int @default(0)
|
||||||
|
|
||||||
|
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([postId, date])
|
||||||
|
@@index([date, viewCount])
|
||||||
|
@@index([postId, date])
|
||||||
|
@@map("daily_post_views")
|
||||||
|
}
|
||||||
|
|
||||||
// 신고(게시글/댓글 대상)
|
// 신고(게시글/댓글 대상)
|
||||||
model Report {
|
model Report {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
|
|||||||
@@ -5,13 +5,33 @@ import { getUserIdFromRequest } from "@/lib/auth";
|
|||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
const userId = getUserIdFromRequest(req);
|
const userId = getUserIdFromRequest(req);
|
||||||
if (!userId) return NextResponse.json({ permissions: [] });
|
if (!userId) return NextResponse.json({ permissions: [] });
|
||||||
const roles = await prisma.userRole.findMany({ where: { userId }, select: { roleId: true } });
|
|
||||||
if (roles.length === 0) return NextResponse.json({ permissions: [] });
|
const user = await prisma.user.findUnique({
|
||||||
const roleIds = roles.map((r) => r.roleId);
|
where: { userId },
|
||||||
const permissions = await prisma.rolePermission.findMany({
|
select: {
|
||||||
where: { roleId: { in: roleIds }, allowed: true },
|
authLevel: true,
|
||||||
select: { resource: true, action: true },
|
userRoles: { select: { roleId: true } },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
if (!user) return NextResponse.json({ permissions: [] });
|
||||||
|
|
||||||
|
const roleIds = user.userRoles.map((r) => r.roleId);
|
||||||
|
const rolePermissions =
|
||||||
|
roleIds.length > 0
|
||||||
|
? await prisma.rolePermission.findMany({
|
||||||
|
where: { roleId: { in: roleIds }, allowed: true },
|
||||||
|
select: { resource: true, action: true },
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const hasAdminPerm = rolePermissions.some(
|
||||||
|
(perm) => perm.resource === "ADMIN" && perm.action === "ADMINISTER"
|
||||||
|
);
|
||||||
|
const permissions =
|
||||||
|
user.authLevel === "ADMIN" && !hasAdminPerm
|
||||||
|
? [{ resource: "ADMIN" as const, action: "ADMINISTER" as const }, ...rolePermissions]
|
||||||
|
: rolePermissions;
|
||||||
|
|
||||||
return NextResponse.json({ permissions });
|
return NextResponse.json({ permissions });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,25 @@ export async function POST(req: Request, context: { params: Promise<{ id: string
|
|||||||
update: { views: { increment: 1 } },
|
update: { views: { increment: 1 } },
|
||||||
create: { postId: id, views: 1 },
|
create: { postId: id, views: 1 },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 일일 조회수 업데이트 (오늘 날짜)
|
||||||
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
await tx.dailyPostView.upsert({
|
||||||
|
where: {
|
||||||
|
postId_date: {
|
||||||
|
postId: id,
|
||||||
|
date: today,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: { viewCount: { increment: 1 } },
|
||||||
|
create: {
|
||||||
|
postId: id,
|
||||||
|
date: today,
|
||||||
|
viewCount: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
counted = true;
|
counted = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
92
src/app/api/posts/popular/route.ts
Normal file
92
src/app/api/posts/popular/route.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export async function GET(req: Request) {
|
||||||
|
const { searchParams } = new URL(req.url);
|
||||||
|
const boardId = searchParams.get("boardId");
|
||||||
|
const period = searchParams.get("period") || "daily"; // daily | weekly
|
||||||
|
|
||||||
|
// 날짜 범위 계산
|
||||||
|
const now = new Date();
|
||||||
|
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
|
||||||
|
const endOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
||||||
|
const startOfWeek = new Date(now);
|
||||||
|
startOfWeek.setDate(now.getDate() - 7);
|
||||||
|
startOfWeek.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const dateFilter = period === "daily"
|
||||||
|
? {
|
||||||
|
date: {
|
||||||
|
gte: startOfToday,
|
||||||
|
lte: endOfToday,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: { date: { gte: startOfWeek } };
|
||||||
|
|
||||||
|
// 일일 조회수 테이블에서 조회수 합계 계산
|
||||||
|
const dailyViews = await prisma.dailyPostView.groupBy({
|
||||||
|
by: ["postId"],
|
||||||
|
where: dateFilter,
|
||||||
|
_sum: {
|
||||||
|
viewCount: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
_sum: {
|
||||||
|
viewCount: "desc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
take: 20, // 조회수 상위 20개만 가져와서 게시글 정보 조회
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dailyViews.length === 0) {
|
||||||
|
return NextResponse.json({ items: [], period });
|
||||||
|
}
|
||||||
|
|
||||||
|
const postIds = dailyViews.map((dv) => dv.postId);
|
||||||
|
|
||||||
|
// 게시글 정보 조회
|
||||||
|
const posts = await prisma.post.findMany({
|
||||||
|
where: {
|
||||||
|
id: { in: postIds },
|
||||||
|
status: "published",
|
||||||
|
...(boardId ? { boardId } : {}),
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
title: true,
|
||||||
|
createdAt: true,
|
||||||
|
boardId: true,
|
||||||
|
board: { select: { id: true, name: true, slug: true } },
|
||||||
|
isPinned: true,
|
||||||
|
status: true,
|
||||||
|
author: { select: { userId: true, nickname: true } },
|
||||||
|
stat: { select: { recommendCount: true, views: true, commentsCount: true } },
|
||||||
|
postTags: { select: { tag: { select: { name: true, slug: true } } } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 조회수와 게시글 매핑
|
||||||
|
const viewCountMap = new Map(
|
||||||
|
dailyViews.map((dv) => [dv.postId, dv._sum.viewCount ?? 0])
|
||||||
|
);
|
||||||
|
|
||||||
|
// 조회수 순으로 정렬 (조회수가 0보다 큰 것만)
|
||||||
|
const postsWithViews = posts
|
||||||
|
.map((post) => ({
|
||||||
|
...post,
|
||||||
|
viewCount: viewCountMap.get(post.id) ?? 0,
|
||||||
|
}))
|
||||||
|
.filter((post) => post.viewCount > 0) // 조회수가 0보다 큰 것만
|
||||||
|
.sort((a, b) => {
|
||||||
|
// 고정글 우선
|
||||||
|
if (a.isPinned && !b.isPinned) return -1;
|
||||||
|
if (!a.isPinned && b.isPinned) return 1;
|
||||||
|
// 조회수 순
|
||||||
|
if (b.viewCount !== a.viewCount) return b.viewCount - a.viewCount;
|
||||||
|
// 최신순
|
||||||
|
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
||||||
|
})
|
||||||
|
.slice(0, 5); // 상위 5개만
|
||||||
|
|
||||||
|
return NextResponse.json({ items: postsWithViews, period });
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ type Item = {
|
|||||||
stat?: { recommendCount: number; views: number; commentsCount: number } | null;
|
stat?: { recommendCount: number; views: number; commentsCount: number } | null;
|
||||||
postTags?: { tag: { name: string; slug: string } }[];
|
postTags?: { tag: { name: string; slug: string } }[];
|
||||||
author?: { nickname: string } | null;
|
author?: { nickname: string } | null;
|
||||||
|
viewCount?: number; // 일일/주간 조회수
|
||||||
};
|
};
|
||||||
|
|
||||||
type Resp = {
|
type Resp = {
|
||||||
@@ -45,6 +46,12 @@ export function PostList({ boardId, sort = "recent", q, tag, author, authorId, s
|
|||||||
const listContainerRef = useRef<HTMLDivElement | null>(null);
|
const listContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [lockedMinHeight, setLockedMinHeight] = useState<number | null>(null);
|
const [lockedMinHeight, setLockedMinHeight] = useState<number | null>(null);
|
||||||
|
|
||||||
|
// 인기글 데이터 가져오기 (board variant일 때만)
|
||||||
|
const popularDailyKey = variant === "board" ? `/api/posts/popular?${boardId ? `boardId=${boardId}&` : ""}period=daily` : null;
|
||||||
|
const popularWeeklyKey = variant === "board" ? `/api/posts/popular?${boardId ? `boardId=${boardId}&` : ""}period=weekly` : null;
|
||||||
|
const { data: dailyData } = useSWR<{ items: Item[] }>(popularDailyKey, fetcher);
|
||||||
|
const { data: weeklyData } = useSWR<{ items: Item[] }>(popularWeeklyKey, fetcher);
|
||||||
|
|
||||||
// board 변형에서는 URL에서 pageSize를 읽고, 기본값은 20
|
// board 변형에서는 URL에서 pageSize를 읽고, 기본값은 20
|
||||||
const defaultPageSize = variant === "board" ? 20 : 10;
|
const defaultPageSize = variant === "board" ? 20 : 10;
|
||||||
const pageSizeParam = sp.get("pageSize");
|
const pageSizeParam = sp.get("pageSize");
|
||||||
@@ -184,6 +191,83 @@ export function PostList({ boardId, sort = "recent", q, tag, author, authorId, s
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
{/* 인기글 섹션 (board variant일 때만 표시) */}
|
||||||
|
{variant === "board" && (dailyData?.items.length || weeklyData?.items.length) && (
|
||||||
|
<div className="mb-6 space-y-4">
|
||||||
|
{/* 일간 인기글 */}
|
||||||
|
{dailyData?.items && dailyData.items.length > 0 && (
|
||||||
|
<div className="border border-[#e6e6e6] rounded-xl overflow-hidden">
|
||||||
|
<div className="bg-[#f6f4f4] px-4 py-2 border-b border-[#e6e6e6]">
|
||||||
|
<h3 className="text-[14px] font-semibold text-[#161616]">🔥 일간 인기글</h3>
|
||||||
|
</div>
|
||||||
|
<ul className="divide-y divide-[#e6e6e6]">
|
||||||
|
{dailyData.items.map((p) => (
|
||||||
|
<li key={p.id} className="px-4 py-3 hover:bg-neutral-50 transition-colors">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div
|
||||||
|
role="link"
|
||||||
|
tabIndex={0}
|
||||||
|
className="group block truncate text-neutral-900 cursor-pointer"
|
||||||
|
onClick={() => router.push(`/posts/${p.id}`)}
|
||||||
|
onKeyDown={(e) => { if (e.key === "Enter") router.push(`/posts/${p.id}`); }}
|
||||||
|
>
|
||||||
|
<span className="text-[15px] group-hover:text-[var(--red-50,#F94B37)] group-hover:font-[700]">
|
||||||
|
{stripHtml(p.title)}
|
||||||
|
</span>
|
||||||
|
{(p.stat?.commentsCount ?? 0) > 0 && (
|
||||||
|
<span className="ml-1 text-[12px] text-[#f94b37] align-middle">[{p.stat?.commentsCount}]</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 text-xs text-[#8c8c8c] shrink-0">
|
||||||
|
<span className="inline-flex items-center gap-1"><ViewsIcon width={14} height={14} />{p.viewCount ?? 0}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 주간 인기글 */}
|
||||||
|
{weeklyData?.items && weeklyData.items.length > 0 && (
|
||||||
|
<div className="border border-[#e6e6e6] rounded-xl overflow-hidden">
|
||||||
|
<div className="bg-[#f6f4f4] px-4 py-2 border-b border-[#e6e6e6]">
|
||||||
|
<h3 className="text-[14px] font-semibold text-[#161616]">⭐ 주간 인기글</h3>
|
||||||
|
</div>
|
||||||
|
<ul className="divide-y divide-[#e6e6e6]">
|
||||||
|
{weeklyData.items.map((p) => (
|
||||||
|
<li key={p.id} className="px-4 py-3 hover:bg-neutral-50 transition-colors">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div
|
||||||
|
role="link"
|
||||||
|
tabIndex={0}
|
||||||
|
className="group block truncate text-neutral-900 cursor-pointer"
|
||||||
|
onClick={() => router.push(`/posts/${p.id}`)}
|
||||||
|
onKeyDown={(e) => { if (e.key === "Enter") router.push(`/posts/${p.id}`); }}
|
||||||
|
>
|
||||||
|
<span className="text-[15px] group-hover:text-[var(--red-50,#F94B37)] group-hover:font-[700]">
|
||||||
|
{stripHtml(p.title)}
|
||||||
|
</span>
|
||||||
|
{(p.stat?.commentsCount ?? 0) > 0 && (
|
||||||
|
<span className="ml-1 text-[12px] text-[#f94b37] align-middle">[{p.stat?.commentsCount}]</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 text-xs text-[#8c8c8c] shrink-0">
|
||||||
|
<span className="inline-flex items-center gap-1"><ViewsIcon width={14} height={14} />{p.viewCount ?? 0}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 정렬 스위치 (board 변형에서는 상단 툴바를 숨김) */}
|
{/* 정렬 스위치 (board 변형에서는 상단 툴바를 숨김) */}
|
||||||
{variant !== "board" && (
|
{variant !== "board" && (
|
||||||
<div className="px-4 py-2 border-b border-neutral-200 mb-2 flex items-center gap-2">
|
<div className="px-4 py-2 border-b border-neutral-200 mb-2 flex items-center gap-2">
|
||||||
@@ -209,7 +293,7 @@ export function PostList({ boardId, sort = "recent", q, tag, author, authorId, s
|
|||||||
|
|
||||||
{/* 리스트 테이블 헤더 (board 변형에서는 숨김) */}
|
{/* 리스트 테이블 헤더 (board 변형에서는 숨김) */}
|
||||||
{variant !== "board" && (
|
{variant !== "board" && (
|
||||||
<div className="hidden md:grid grid-cols-[20px_1fr_120px_120px_80px] items-center px-4 py-2 text-[12px] text-[#8c8c8c] bg-[#f6f4f4] border-b border-[#e6e6e6] rounded-t-xl">
|
<div className="hidden md:grid grid-cols-[20px_1fr_120px_120px_80px] items-center px-4 py-2 text-[12px] text-[#8c8c8c] bg-[#f6f4f4] border-b border-[#e6e6e6] rounded-t-xl md:divide-x md:divide-[#e6e6e6]">
|
||||||
<div />
|
<div />
|
||||||
<div>제목</div>
|
<div>제목</div>
|
||||||
<div className="text-center">작성자</div>
|
<div className="text-center">작성자</div>
|
||||||
@@ -227,10 +311,10 @@ export function PostList({ boardId, sort = "recent", q, tag, author, authorId, s
|
|||||||
|
|
||||||
{/* 아이템들 */}
|
{/* 아이템들 */}
|
||||||
<div ref={listContainerRef} style={{ minHeight: lockedMinHeight ? `${lockedMinHeight}px` : undefined }}>
|
<div ref={listContainerRef} style={{ minHeight: lockedMinHeight ? `${lockedMinHeight}px` : undefined }}>
|
||||||
<ul className="divide-y divide-[#ececec]">
|
<ul className="divide-y divide-[#bbbbbb]">
|
||||||
{items.map((p) => (
|
{items.map((p) => (
|
||||||
<li key={p.id} className={`px-4 ${variant === "board" ? "" : "py-4 md:py-4"} hover:bg-neutral-50 transition-colors`}>
|
<li key={p.id} className={`px-4 ${variant === "board" ? "py-3 md:py-4" : "py-4 md:py-4"} hover:bg-neutral-50 transition-colors min-h-[52px] md:min-h-[56px]`}>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-[20px_1fr_120px_120px_80px] items-center gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-[20px_1fr_120px_120px_80px] items-center gap-2 md:divide-x md:divide-[#eaeaea]">
|
||||||
{/* bullet/공지 아이콘 자리 */}
|
{/* bullet/공지 아이콘 자리 */}
|
||||||
<div className="hidden md:flex items-center justify-center text-[#f94b37]"></div>
|
<div className="hidden md:flex items-center justify-center text-[#f94b37]"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,23 @@ export async function checkPermission(options: {
|
|||||||
const { userId, resource, action } = options;
|
const { userId, resource, action } = options;
|
||||||
if (!userId) return false;
|
if (!userId) return false;
|
||||||
|
|
||||||
const userRoles = await prisma.userRole.findMany({
|
const user = await prisma.user.findUnique({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
select: { roleId: true },
|
select: {
|
||||||
|
authLevel: true,
|
||||||
|
userRoles: { select: { roleId: true } },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
if (userRoles.length === 0) return false;
|
if (!user) return false;
|
||||||
const roleIds = userRoles.map((r) => r.roleId);
|
|
||||||
|
// 사용자 레코드가 ADMIN 권한이면 모든 리소스/액션 허용
|
||||||
|
if (user.authLevel === "ADMIN") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const roleIds = user.userRoles.map((r) => r.roleId);
|
||||||
|
if (roleIds.length === 0) return false;
|
||||||
|
|
||||||
const has = await prisma.rolePermission.findFirst({
|
const has = await prisma.rolePermission.findFirst({
|
||||||
where: {
|
where: {
|
||||||
roleId: { in: roleIds },
|
roleId: { in: roleIds },
|
||||||
@@ -25,6 +36,7 @@ export async function checkPermission(options: {
|
|||||||
select: { id: true },
|
select: { id: true },
|
||||||
});
|
});
|
||||||
if (has) return true;
|
if (has) return true;
|
||||||
|
|
||||||
// ADMIN.ADMINISTER 이면 모든 리소스/액션 허용
|
// ADMIN.ADMINISTER 이면 모든 리소스/액션 허용
|
||||||
const isAdmin = await prisma.rolePermission.findFirst({
|
const isAdmin = await prisma.rolePermission.findFirst({
|
||||||
where: {
|
where: {
|
||||||
|
|||||||
Reference in New Issue
Block a user