This commit is contained in:
@@ -61,15 +61,27 @@ export async function POST(req: Request) {
|
||||
});
|
||||
isAdmin = !!hasAdmin;
|
||||
}
|
||||
// 추가 안전장치: 사용자 레코드의 authLevel이 ADMIN이면 관리자 취급
|
||||
if (!isAdmin && user.authLevel === "ADMIN") {
|
||||
isAdmin = true;
|
||||
}
|
||||
|
||||
const res = NextResponse.json({ ok: true, user: { userId: user.userId, nickname: user.nickname } });
|
||||
// HTTPS 요청에서만 Secure 속성 부여 (HTTP 환경에서는 생략하여 로컬 start에서도 동작)
|
||||
let secureAttr = "";
|
||||
try {
|
||||
const isHttps = new URL(req.url).protocol === "https:";
|
||||
secureAttr = isHttps ? "; Secure" : "";
|
||||
} catch {
|
||||
secureAttr = "";
|
||||
}
|
||||
res.headers.append(
|
||||
"Set-Cookie",
|
||||
`uid=${encodeURIComponent(user.userId)}; Path=/; HttpOnly; SameSite=Lax`
|
||||
`uid=${encodeURIComponent(user.userId)}; Path=/; HttpOnly; SameSite=Lax${secureAttr}`
|
||||
);
|
||||
res.headers.append(
|
||||
"Set-Cookie",
|
||||
`isAdmin=${isAdmin ? "1" : "0"}; Path=/; HttpOnly; SameSite=Lax`
|
||||
`isAdmin=${isAdmin ? "1" : "0"}; Path=/; HttpOnly; SameSite=Lax${secureAttr}`
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import prisma from "@/lib/prisma";
|
||||
export async function GET(req: Request) {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const category = searchParams.get("category"); // slug or id
|
||||
const where: any = {};
|
||||
const where: any = { status: "active" };
|
||||
if (category) {
|
||||
if (category.length === 25 || category.length === 24) {
|
||||
where.categoryId = category;
|
||||
|
||||
@@ -4,6 +4,7 @@ import Link from "next/link";
|
||||
import { BoardToolbar } from "@/app/components/BoardToolbar";
|
||||
import { headers } from "next/headers";
|
||||
import prisma from "@/lib/prisma";
|
||||
import { notFound } from "next/navigation";
|
||||
import { UserAvatar } from "@/app/components/UserAvatar";
|
||||
import { RankIcon1st } from "@/app/components/RankIcon1st";
|
||||
import { RankIcon2nd } from "@/app/components/RankIcon2nd";
|
||||
@@ -33,7 +34,10 @@ export default async function BoardDetail({ params, searchParams }: { params: an
|
||||
const res = await fetch(new URL("/api/boards", base).toString(), { cache: "no-store" });
|
||||
const { boards } = await res.json();
|
||||
const board = (boards || []).find((b: any) => b.slug === idOrSlug || b.id === idOrSlug);
|
||||
const id = board?.id as string;
|
||||
if (!board) {
|
||||
return notFound();
|
||||
}
|
||||
const id = board.id as string;
|
||||
const siblingBoards = (boards || []).filter((b: any) => b.category?.id && b.category.id === board?.category?.id);
|
||||
const categoryName = board?.category?.name ?? "";
|
||||
// 메인배너 표시 설정
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { RankIcon1st } from "./RankIcon1st";
|
||||
import { RankIcon2nd } from "./RankIcon2nd";
|
||||
@@ -52,6 +52,23 @@ export function BoardPanelClient({
|
||||
}) {
|
||||
const [selectedBoardId, setSelectedBoardId] = useState(initialBoardId);
|
||||
|
||||
// 데이터가 비어있을 때 안전 처리
|
||||
if (!boardsData || boardsData.length === 0) {
|
||||
return (
|
||||
<div className="h-full min-h-0 flex items-center justify-center rounded-xl bg-white text-sm text-neutral-500">
|
||||
선택된 게시판이 없습니다.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 선택된 보드가 목록에 없으면 첫 번째 보드로 동기화
|
||||
useEffect(() => {
|
||||
const exists = boardsData.some((bd) => bd.board.id === selectedBoardId);
|
||||
if (!exists) {
|
||||
setSelectedBoardId(boardsData[0].board.id);
|
||||
}
|
||||
}, [boardsData, selectedBoardId]);
|
||||
|
||||
// 선택된 게시판 데이터 찾기
|
||||
const selectedBoardData = boardsData.find(bd => bd.board.id === selectedBoardId) || boardsData[0];
|
||||
const { board, categoryName, siblingBoards } = selectedBoardData;
|
||||
|
||||
@@ -77,7 +77,7 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s
|
||||
// 보드 메타데이터 (메인뷰 타입 포함)
|
||||
const boardsMeta = visibleBoardIds.length
|
||||
? await prisma.board.findMany({
|
||||
where: { id: { in: visibleBoardIds } },
|
||||
where: { id: { in: visibleBoardIds }, status: "active" },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
@@ -116,14 +116,26 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s
|
||||
// 게시판 패널 데이터 수집 함수 (모든 sibling boards 포함)
|
||||
const prepareBoardPanelData = async (board: { id: string; name: string; slug: string; categoryId: string | null; categoryName: string; mainTypeKey?: string }) => {
|
||||
// 같은 카테고리의 소분류(게시판) 탭 목록
|
||||
const siblingBoards = board.categoryId
|
||||
let siblingBoards: any[] = board.categoryId
|
||||
? await prisma.board.findMany({
|
||||
where: { categoryId: board.categoryId },
|
||||
where: { categoryId: board.categoryId, status: "active" },
|
||||
select: { id: true, name: true, slug: true, mainPageViewType: { select: { key: true } } },
|
||||
orderBy: [{ sortOrder: "asc" }, { name: "asc" }],
|
||||
})
|
||||
: [];
|
||||
|
||||
// 카테고리가 없거나 동료 게시판이 없을 때, 선택된 보드 단독 구성으로 대체
|
||||
if (siblingBoards.length === 0) {
|
||||
siblingBoards = [
|
||||
{
|
||||
id: board.id,
|
||||
name: board.name,
|
||||
slug: board.slug,
|
||||
mainPageViewType: { key: board.mainTypeKey },
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const boardsData = [];
|
||||
|
||||
for (const sb of siblingBoards) {
|
||||
|
||||
@@ -17,7 +17,8 @@ export async function middleware(req: NextRequest) {
|
||||
}
|
||||
|
||||
// uid 쿠키가 있어도 실제 유저가 존재하지 않으면 비로그인으로 간주하고 쿠키 정리
|
||||
let authenticated = false;
|
||||
// 기본값은 쿠키 존재 여부 기준(네트워크 오류 시 과도한 로그아웃 방지)
|
||||
let authenticated = !!uid;
|
||||
if (uid) {
|
||||
try {
|
||||
const verifyUrl = new URL("/api/auth/session", req.url);
|
||||
@@ -30,9 +31,13 @@ export async function middleware(req: NextRequest) {
|
||||
if (verifyRes.ok) {
|
||||
const data = await verifyRes.json().catch(() => ({ ok: false }));
|
||||
authenticated = !!data?.ok;
|
||||
} else {
|
||||
// 검증 API가 200이 아니면 기존 판단 유지(일시적 오류 대비)
|
||||
authenticated = !!uid;
|
||||
}
|
||||
} catch {
|
||||
authenticated = false;
|
||||
// 네트워크/런타임 오류 시 기존 판단 유지(쿠키 있으면 로그인 간주)
|
||||
authenticated = !!uid;
|
||||
}
|
||||
// 유효하지 않은 uid 쿠키는 즉시 제거
|
||||
if (!authenticated) {
|
||||
|
||||
Reference in New Issue
Block a user