From 0c27c193413cb6bce1f6fed3cbb9b619d9a8cf20 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Wed, 4 Jun 2025 10:51:11 +0900 Subject: [PATCH] refactor: modify member permission verification logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 매물 조회 권한 검증 로직 리팩토링 --- src/app/api/survey-sales/[id]/route.ts | 59 +++++++++++++++---- src/app/api/survey-sales/route.ts | 7 +-- .../survey-sale/detail/DetailForm.tsx | 11 ++-- src/hooks/useSurvey.ts | 1 - 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/app/api/survey-sales/[id]/route.ts b/src/app/api/survey-sales/[id]/route.ts index a32f053..9657f10 100644 --- a/src/app/api/survey-sales/[id]/route.ts +++ b/src/app/api/survey-sales/[id]/route.ts @@ -3,30 +3,63 @@ import { prisma } from '@/libs/prisma' import { convertToSnakeCase } from '@/utils/common-utils' import { SessionData } from '@/types/Auth' -type SessionParams = { +interface Survey { + SRL_NO: string + SUBMISSION_STATUS: boolean + SUBMISSION_TARGET_ID: string | null + STORE_ID: string | null + CONSTRUCTION_POINT_ID: string | null +} + +interface SessionParams { role: string | null storeId: string | null builderNo: string | null isLoggedIn: string | null } -const checkRole = (survey: any, sessionParams: SessionParams) => { +const checkT01Role = (survey: Survey): boolean => survey.SRL_NO !== '一時保存' + +const checkAdminRole = (survey: Survey, storeId: string | null): boolean => { + if (!storeId) return false + + if (survey.SUBMISSION_STATUS) { + return survey.SUBMISSION_TARGET_ID === storeId || survey.STORE_ID === storeId + } + return survey.STORE_ID === storeId +} + +const checkAdminSubRole = (survey: Survey, storeId: string | null): boolean => { + if (!storeId) return false + + if (survey.SUBMISSION_STATUS) { + return survey.SUBMISSION_TARGET_ID === storeId || (survey.STORE_ID === storeId && survey.CONSTRUCTION_POINT_ID === null) + } + return survey.STORE_ID === storeId && survey.CONSTRUCTION_POINT_ID === null +} + +const checkPartnerOrBuilderRole = (survey: Survey, builderNo: string | null): boolean => { + if (!builderNo) return false + return survey.CONSTRUCTION_POINT_ID === builderNo +} + +const checkRole = (survey: Survey, sessionParams: SessionParams): boolean => { + if (!survey || !sessionParams.role) return false + switch (sessionParams.role) { case 'T01': - return true + return checkT01Role(survey) + // T01 이외 1차점 case 'Admin': - if (survey.SUBMISSION_STATUS) { - return survey.SUBMISSION_TARGET_ID === sessionParams.storeId - } - return survey.STORE_ID === sessionParams.storeId + return checkAdminRole(survey, sessionParams.storeId) + // 2차점 case 'Admin_Sub': - if (survey.SUBMISSION_STATUS) { - return survey.SUBMISSION_TARGET_ID === sessionParams.builderNo - } - return survey.STORE_ID === sessionParams.storeId && survey.CONSTRUCTION_POINT_ID === sessionParams.builderNo + return checkAdminSubRole(survey, sessionParams.storeId) + // partner case 'Partner': + // 2차점 시공권한 user case 'Builder': - return survey.CONSTRUCTION_POINT_ID === sessionParams.builderNo + return checkPartnerOrBuilderRole(survey, sessionParams.builderNo) default: return false } @@ -54,6 +87,8 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ }) if (checkRole(survey, sessionParams)) { return NextResponse.json(survey) + } else { + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) } } catch (error) { console.error('Error fetching survey:', error) diff --git a/src/app/api/survey-sales/route.ts b/src/app/api/survey-sales/route.ts index 49db086..6d01bb0 100644 --- a/src/app/api/survey-sales/route.ts +++ b/src/app/api/survey-sales/route.ts @@ -90,12 +90,7 @@ const createMemberRoleCondition = (params: SearchParams): WhereCondition => { where.OR = [ { // MUSUBI (시공권한 X) 같은 판매점에서 작성한 제출/제출되지 않은 매물 - AND: [ - { STORE_ID: { equals: params.storeId } }, - { - OR: [{ CONSTRUCTION_POINT_ID: { equals: null } }, { CONSTRUCTION_POINT_ID: { equals: '' } }], - }, - ], + AND: [{ STORE_ID: { equals: params.storeId } }, { CONSTRUCTION_POINT_ID: { equals: params.builderNo } }], }, { // MUSUBI (시공권한 O) 가 MUSUBI 에 제출한 매물 + PARTNER 가 제출한 매물 diff --git a/src/components/survey-sale/detail/DetailForm.tsx b/src/components/survey-sale/detail/DetailForm.tsx index f935718..ef88e75 100644 --- a/src/components/survey-sale/detail/DetailForm.tsx +++ b/src/components/survey-sale/detail/DetailForm.tsx @@ -5,7 +5,7 @@ import { useEffect, useState } from 'react' import ButtonForm from './ButtonForm' import BasicForm from './BasicForm' import RoofForm from './RoofForm' -import { useParams, useSearchParams } from 'next/navigation' +import { useParams, useSearchParams, useRouter } from 'next/navigation' import { useSurvey } from '@/hooks/useSurvey' import { useSessionStore } from '@/store/session' @@ -71,10 +71,12 @@ export default function DetailForm() { const idParam = useSearchParams().get('id') const routeId = useParams().id + const router = useRouter() + const modeset = Number(routeId) ? 'READ' : idParam ? 'EDIT' : 'CREATE' const id = Number(routeId) ? Number(routeId) : Number(idParam) - const { surveyDetail, validateSurveyDetail } = useSurvey(Number(id)) + const { surveyDetail, isLoadingSurveyDetail, validateSurveyDetail } = useSurvey(Number(id)) const { session } = useSessionStore() const [mode, setMode] = useState(modeset) @@ -82,9 +84,10 @@ export default function DetailForm() { const [roofInfoData, setRoofInfoData] = useState(roofInfoForm) useEffect(() => { - if (Number(idParam) !== 0 || surveyDetail === null) { + if (isLoadingSurveyDetail) return + if (surveyDetail === null) { alert('権限がありません。') - window.location.href = '/survey-sale' + router.replace('/survey-sale') } if (surveyDetail && (mode === 'EDIT' || mode === 'READ')) { const { id, uptDt, regDt, detailInfo, ...rest } = surveyDetail diff --git a/src/hooks/useSurvey.ts b/src/hooks/useSurvey.ts index 02a0124..0fbf69a 100644 --- a/src/hooks/useSurvey.ts +++ b/src/hooks/useSurvey.ts @@ -5,7 +5,6 @@ import { useSurveyFilterStore } from '@/store/surveyFilterStore' import { useSessionStore } from '@/store/session' import { useAxios } from './useAxios' import { queryStringFormatter } from '@/utils/common-utils' -import { AxiosError } from 'axios' export const requiredFields = [ {