Compare commits
No commits in common. "fcf87c153ec46c6cf802c3a829d0917ae95591d2" and "855dedce44732ea1da3123cba2d717bc7fbfaf8a" have entirely different histories.
fcf87c153e
...
855dedce44
6
.gitignore
vendored
6
.gitignore
vendored
@ -45,8 +45,4 @@ next-env.d.ts
|
||||
|
||||
bun.lockb
|
||||
pnpm-lock.yaml
|
||||
pnpm-workspace.yaml
|
||||
|
||||
# logs
|
||||
logs/
|
||||
*.log
|
||||
pnpm-workspace.yaml
|
||||
@ -6,12 +6,7 @@ const nextConfig: NextConfig = {
|
||||
sassOptions: {
|
||||
includePaths: [path.join(__dirname, './src/styles')],
|
||||
},
|
||||
serverExternalPackages: ['@react-pdf/renderer', 'pino'],
|
||||
logging: {
|
||||
fetches: {
|
||||
fullUrl: true,
|
||||
},
|
||||
},
|
||||
serverExternalPackages: ['@react-pdf/renderer'],
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
|
||||
@ -29,7 +29,6 @@
|
||||
"next": "15.2.4",
|
||||
"nodemailer": "^7.0.3",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pino": "^9.7.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-to-pdf": "^2.0.0",
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
import { axiosInstance } from '@/libs/axios'
|
||||
|
||||
async function setChgPwd(req: Request): Promise<NextResponse> {
|
||||
export async function POST(req: Request) {
|
||||
const { loginId, email, pwd, chgPwd } = await req.json()
|
||||
console.log('🚀 ~ POST ~ loginId:', loginId)
|
||||
console.log('🚀 ~ POST ~ email:', email)
|
||||
@ -20,5 +19,3 @@ async function setChgPwd(req: Request): Promise<NextResponse> {
|
||||
|
||||
return NextResponse.json({ code: 200, data: result.data })
|
||||
}
|
||||
|
||||
export const POST = loggerWrapper(setChgPwd)
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import type { CommCode } from '@/types/CommCode'
|
||||
|
||||
async function getCommCode(request: NextRequest): Promise<NextResponse> {
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams
|
||||
const headCode = searchParams.get('headCode')
|
||||
@ -21,26 +20,24 @@ async function getCommCode(request: NextRequest): Promise<NextResponse> {
|
||||
if (!headCd) {
|
||||
return NextResponse.json({ error: `${headCode}를 찾을 수 없습니다` }, { status: 404 })
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const roofMaterials: CommCode[] = await prisma.BC_COMM_L.findMany({
|
||||
where: {
|
||||
HEAD_CD: headCd.HEAD_CD,
|
||||
},
|
||||
select: {
|
||||
HEAD_CD: true,
|
||||
CODE: true,
|
||||
CODE_JP: true,
|
||||
},
|
||||
orderBy: {
|
||||
CODE: 'asc',
|
||||
},
|
||||
})
|
||||
if (headCode === 'SALES_OFFICE_CD') {
|
||||
return getSaleOffice(headCd.HEAD_CD)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const roofMaterials: CommCode[] = await prisma.BC_COMM_L.findMany({
|
||||
where: {
|
||||
HEAD_CD: headCd.HEAD_CD,
|
||||
},
|
||||
select: {
|
||||
HEAD_CD: true,
|
||||
CODE: true,
|
||||
CODE_JP: true,
|
||||
},
|
||||
orderBy: {
|
||||
CODE: 'asc',
|
||||
},
|
||||
})
|
||||
return NextResponse.json(roofMaterials)
|
||||
}
|
||||
return NextResponse.json(roofMaterials)
|
||||
} catch (error) {
|
||||
console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error)
|
||||
return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 })
|
||||
@ -63,5 +60,3 @@ const getSaleOffice = async (headCode: string) => {
|
||||
})
|
||||
return NextResponse.json(commCodeSaleOffice)
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getCommCode)
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { queryStringFormatter } from '@/utils/common-utils'
|
||||
import axios from 'axios'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
|
||||
async function getQnaDetail(request: Request): Promise<NextResponse> {
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const params = {
|
||||
compCd: searchParams.get('compCd'),
|
||||
@ -23,5 +22,3 @@ async function getQnaDetail(request: Request): Promise<NextResponse> {
|
||||
return NextResponse.json({ error: 'route error' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getQnaDetail)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import axios from 'axios'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
|
||||
// export async function GET(request: Request) {
|
||||
// const { searchParams } = new URL(request.url)
|
||||
@ -39,7 +38,7 @@ import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
// }
|
||||
// }
|
||||
|
||||
async function downloadFile(request: Request): Promise<NextResponse> {
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const encodeFileNo = searchParams.get('encodeFileNo')
|
||||
const srcFileNm = searchParams.get('srcFileNm') || 'downloaded-file'
|
||||
@ -72,5 +71,3 @@ async function downloadFile(request: Request): Promise<NextResponse> {
|
||||
return NextResponse.json({ error: error.response?.data || 'Failed to download file' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(downloadFile)
|
||||
|
||||
@ -3,11 +3,10 @@ import { NextResponse } from 'next/server'
|
||||
import { queryStringFormatter } from '@/utils/common-utils'
|
||||
import { getIronSession } from 'iron-session'
|
||||
import { cookies } from 'next/headers'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
import { sessionOptions } from '@/libs/session'
|
||||
import { SessionData } from '@/types/Auth'
|
||||
|
||||
async function getQnaList(request: Request): Promise<NextResponse> {
|
||||
export async function GET(request: Request) {
|
||||
const cookieStore = await cookies()
|
||||
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
||||
|
||||
@ -38,5 +37,3 @@ async function getQnaList(request: Request): Promise<NextResponse> {
|
||||
return NextResponse.json({ error: 'route error' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getQnaList)
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import axios from 'axios'
|
||||
import { CommonCode } from '@/types/Inquiry'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
|
||||
async function getCommonCodeListData(request: Request): Promise<NextResponse> {
|
||||
export async function GET() {
|
||||
const response = await axios.get(`${process.env.NEXT_PUBLIC_INQUIRY_API_URL}/api/system/commonCodeListData`)
|
||||
const codeList: CommonCode[] = []
|
||||
response.data.data.apiCommCdList.forEach((item: any) => {
|
||||
@ -18,5 +17,3 @@ async function getCommonCodeListData(request: Request): Promise<NextResponse> {
|
||||
})
|
||||
return NextResponse.json({ data: codeList })
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getCommonCodeListData)
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import axios from 'axios'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
|
||||
async function setQna(request: Request): Promise<NextResponse> {
|
||||
export async function POST(request: Request) {
|
||||
const formData = await request.formData()
|
||||
console.log(formData)
|
||||
try {
|
||||
@ -20,5 +19,3 @@ async function setQna(request: Request): Promise<NextResponse> {
|
||||
return NextResponse.json({ error: 'Failed to save qna' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const POST = loggerWrapper(setQna)
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
import { SubmitTargetResponse } from '@/types/Survey'
|
||||
|
||||
// 2차점이 자신에게 매핑 된 1차 판매점과 관리자 정보 조회
|
||||
async function getSubMissionAdminSub(request: NextRequest): Promise<NextResponse> {
|
||||
type AdminSubPerson = {
|
||||
storeId: string
|
||||
userId: string
|
||||
eMail: string
|
||||
authority: string
|
||||
}
|
||||
// 2차점이 자신에게 매핑 된 1차 판매점과 관리자 정보 조회
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const id = searchParams.get('id')
|
||||
@ -28,12 +32,11 @@ async function getSubMissionAdminSub(request: NextRequest): Promise<NextResponse
|
||||
AND MCS.DEL_YN = 'N';
|
||||
CLOSE SYMMETRIC KEY SYMMETRICKEY;
|
||||
`
|
||||
const data: SubmitTargetResponse[] = await prisma.$queryRawUnsafe(query)
|
||||
return NextResponse.json(data)
|
||||
const suitable: AdminSubPerson[] = await prisma.$queryRawUnsafe(query)
|
||||
|
||||
return NextResponse.json({ message: 'Hello, world!' })
|
||||
} catch (error) {
|
||||
console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error)
|
||||
return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 })
|
||||
return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getSubMissionAdminSub)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
|
||||
type SuperPerson = {
|
||||
storeId: string
|
||||
@ -9,7 +8,7 @@ type SuperPerson = {
|
||||
toEmail: string
|
||||
}
|
||||
|
||||
async function getSubmissionAdmin(request: NextRequest): Promise<NextResponse> {
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const id = searchParams.get('id')
|
||||
@ -37,12 +36,11 @@ async function getSubmissionAdmin(request: NextRequest): Promise<NextResponse> {
|
||||
;
|
||||
CLOSE SYMMETRIC KEY SYMMETRICKEY;
|
||||
`
|
||||
const data: SuperPerson[] = await prisma.$queryRawUnsafe(query)
|
||||
return NextResponse.json(data)
|
||||
const suitable: SuperPerson[] = await prisma.$queryRawUnsafe(query)
|
||||
|
||||
return NextResponse.json({ message: 'Hello, world!' })
|
||||
} catch (error) {
|
||||
console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error)
|
||||
return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 })
|
||||
return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getSubmissionAdmin)
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { SubmitTargetResponse } from '@/types/Survey'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
|
||||
type BuilderPerson = {
|
||||
agencyStoreId: string
|
||||
userId: string
|
||||
eMail: string
|
||||
userAuthCd: string
|
||||
}
|
||||
|
||||
// 2차점의 시공권한 user가 해당 판매점의 관리자 정보 조회
|
||||
// N == 일반유저, S == 수퍼유저, B == 시공권한유저
|
||||
async function getSubmissionBuilder(request: NextRequest): Promise<NextResponse> {
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const id = searchParams.get('id')
|
||||
@ -30,12 +36,13 @@ async function getSubmissionBuilder(request: NextRequest): Promise<NextResponse>
|
||||
AND MCAS.DEL_YN = 'N';
|
||||
CLOSE SYMMETRIC KEY SYMMETRICKEY;
|
||||
`
|
||||
// const suitable: BuilderPerson[] = await prisma.$queryRawUnsafe(query)
|
||||
|
||||
// return NextResponse.json({ message: 'Hello, world!' })
|
||||
const data: SubmitTargetResponse[] = await prisma.$queryRawUnsafe(query)
|
||||
return NextResponse.json(data)
|
||||
} catch (error) {
|
||||
console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error)
|
||||
return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 })
|
||||
return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getSubmissionBuilder)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
|
||||
type SuperPerson = {
|
||||
storeId: string
|
||||
@ -8,7 +7,7 @@ type SuperPerson = {
|
||||
eMail: string
|
||||
}
|
||||
|
||||
async function getSubmissionSuper(request: NextRequest): Promise<NextResponse> {
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const id = searchParams.get('id')
|
||||
@ -30,12 +29,11 @@ async function getSubmissionSuper(request: NextRequest): Promise<NextResponse> {
|
||||
AND MCSA.DEL_YN = 'N';
|
||||
CLOSE SYMMETRIC KEY SYMMETRICKEY;
|
||||
`
|
||||
const data: SuperPerson[] = await prisma.$queryRawUnsafe(query)
|
||||
return NextResponse.json(data)
|
||||
const suitable: SuperPerson[] = await prisma.$queryRawUnsafe(query)
|
||||
|
||||
return NextResponse.json({ message: 'Hello, world!' })
|
||||
} catch (error) {
|
||||
console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error);
|
||||
return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 });
|
||||
console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error)
|
||||
return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getSubmissionSuper)
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { HttpStatusCode } from 'axios'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { type Suitable } from '@/types/Suitable'
|
||||
|
||||
@ -43,7 +41,7 @@ import { type Suitable } from '@/types/Suitable'
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
async function getSuitableList(request: NextRequest): Promise<NextResponse> {
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams
|
||||
const pageNumber = parseInt(searchParams.get('pageNumber') || '0')
|
||||
@ -53,7 +51,7 @@ async function getSuitableList(request: NextRequest): Promise<NextResponse> {
|
||||
|
||||
/* 파라미터 체크 */
|
||||
if (pageNumber === 0 || itemPerPage === 0) {
|
||||
return NextResponse.json({ error: '페이지 번호와 페이지당 아이템 수가 필요합니다' }, { status: HttpStatusCode.BadRequest })
|
||||
return NextResponse.json({ error: '페이지 번호와 페이지당 아이템 수가 필요합니다' }, { status: 400 })
|
||||
}
|
||||
|
||||
let query = `
|
||||
@ -110,12 +108,9 @@ async function getSuitableList(request: NextRequest): Promise<NextResponse> {
|
||||
headers: {
|
||||
'spinner-state': 'true',
|
||||
},
|
||||
status: HttpStatusCode.Ok,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(`데이터 조회 중 오류가 발생했습니다: ${error}`)
|
||||
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: HttpStatusCode.InternalServerError })
|
||||
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getSuitableList)
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import React from 'react'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { HttpStatusCode } from 'axios'
|
||||
import { pdf, Document } from '@react-pdf/renderer'
|
||||
import { PDFDocument } from 'pdf-lib'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { type Suitable } from '@/types/Suitable'
|
||||
import SuitablePdf from '@/components/pdf/SuitablePdf'
|
||||
@ -30,7 +28,7 @@ import SuitablePdf from '@/components/pdf/SuitablePdf'
|
||||
*
|
||||
* @apiSuccess {File} 지붕재 적합성 PDF 파일
|
||||
*/
|
||||
async function createSuitablePdf(request: NextRequest): Promise<NextResponse> {
|
||||
export async function POST(request: NextRequest) {
|
||||
const formData = await request.formData()
|
||||
const ids = formData.get('ids') as string
|
||||
const detailIds = formData.get('detailIds') as string
|
||||
@ -38,7 +36,7 @@ async function createSuitablePdf(request: NextRequest): Promise<NextResponse> {
|
||||
|
||||
/* 파라미터 체크 */
|
||||
if (ids === '' || detailIds === '' || fileTitle === '') {
|
||||
return NextResponse.json({ error: '필수 파라미터가 누락되었습니다' }, { status: HttpStatusCode.BadRequest })
|
||||
return NextResponse.json({ error: '필수 파라미터가 누락되었습니다' }, { status: 400 })
|
||||
}
|
||||
|
||||
try {
|
||||
@ -123,7 +121,7 @@ async function createSuitablePdf(request: NextRequest): Promise<NextResponse> {
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(`데이터 조회 중 오류가 발생했습니다: ${error}`)
|
||||
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: HttpStatusCode.InternalServerError })
|
||||
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,5 +142,3 @@ async function mergePdfBuffers(buffers: Uint8Array[]) {
|
||||
const mergedPdfBytes = await mergedPdf.save()
|
||||
return mergedPdfBytes
|
||||
}
|
||||
|
||||
export const POST = loggerWrapper(createSuitablePdf)
|
||||
|
||||
@ -1,25 +1,23 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { HttpStatusCode } from 'axios'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
|
||||
/**
|
||||
* @api {get} /api/suitable/pick 지붕재 적합성 데이터 아이디 조회 API
|
||||
* @apiName GetSuitablePick
|
||||
* @apiGroup Suitable
|
||||
*
|
||||
*
|
||||
* @apiDescription
|
||||
* 지붕재 적합성 데이터의 검색 조건에 맞는 main_id와 detail_id를 조회
|
||||
*
|
||||
*
|
||||
* @apiParam {String} [category] 지붕재그룹코드 (예: RMG001)
|
||||
* @apiParam {String} [keyword] 검색키워드
|
||||
*
|
||||
*
|
||||
* @apiExample {curl} Example usage:
|
||||
* curl -X GET \
|
||||
* -G "category=RMG001" \
|
||||
* -G "keyword=검색키워드" \
|
||||
* http://localhost:3000/api/suitable/pick
|
||||
*
|
||||
*
|
||||
* @apiSuccess {Array} suitableIdSet 지붕재 적합성 데이터의 아이디 목록
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* [
|
||||
@ -29,7 +27,7 @@ import { prisma } from '@/libs/prisma'
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
async function getSuitablePick(request: NextRequest): Promise<NextResponse> {
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const searchParams = request.nextUrl.searchParams
|
||||
const category = searchParams.get('category')
|
||||
@ -74,8 +72,6 @@ async function getSuitablePick(request: NextRequest): Promise<NextResponse> {
|
||||
return NextResponse.json(suitableIdSet)
|
||||
} catch (error) {
|
||||
console.error(`데이터 조회 중 오류가 발생했습니다: ${error}`)
|
||||
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: HttpStatusCode.InternalServerError })
|
||||
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getSuitablePick)
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { HttpStatusCode } from 'axios'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { Suitable } from '@/types/Suitable'
|
||||
|
||||
@ -42,7 +40,7 @@ import { Suitable } from '@/types/Suitable'
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
async function getSuitable(request: NextRequest): Promise<NextResponse> {
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body: Record<string, string> = await request.json()
|
||||
const ids = body.ids
|
||||
@ -50,7 +48,7 @@ async function getSuitable(request: NextRequest): Promise<NextResponse> {
|
||||
|
||||
/* 파라미터 체크 */
|
||||
if (ids === '') {
|
||||
return NextResponse.json({ error: '필수 파라미터가 누락되었습니다' }, { status: HttpStatusCode.BadRequest })
|
||||
return NextResponse.json({ error: '필수 파라미터가 누락되었습니다' }, { status: 400 })
|
||||
}
|
||||
|
||||
let query = `
|
||||
@ -98,8 +96,6 @@ async function getSuitable(request: NextRequest): Promise<NextResponse> {
|
||||
return NextResponse.json(suitable)
|
||||
} catch (error) {
|
||||
console.error(`데이터 조회 중 오류가 발생했습니다: ${error}`)
|
||||
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: HttpStatusCode.InternalServerError })
|
||||
return NextResponse.json({ error: `데이터 조회 중 오류가 발생했습니다: ${error}` }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const POST = loggerWrapper(getSuitable)
|
||||
|
||||
@ -6,7 +6,6 @@ import { sessionOptions } from '@/libs/session'
|
||||
import { cookies } from 'next/headers'
|
||||
import type { SessionData } from '@/types/Auth'
|
||||
import { Prisma } from '@prisma/client'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
|
||||
/**
|
||||
* @description 조사 매물 조회 에러 메시지
|
||||
@ -129,11 +128,11 @@ const fetchSurvey = async (id: number) => {
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
async function getSurveySaleDetail(request: NextRequest): Promise<NextResponse> {
|
||||
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
try {
|
||||
const cookieStore = await cookies()
|
||||
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
||||
const id = request.nextUrl.pathname.split('/').pop() ?? ''
|
||||
const { id } = await params
|
||||
const { searchParams } = new URL(request.url)
|
||||
const isPdf = searchParams.get('isPdf') === 'true'
|
||||
|
||||
@ -229,9 +228,9 @@ const getNewSrlNo = async (srlNo: string, storeId: string, role: string) => {
|
||||
* ...
|
||||
* }
|
||||
* */
|
||||
async function updateSurveySaleDetail(request: NextRequest): Promise<NextResponse> {
|
||||
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
try {
|
||||
const id = request.nextUrl.pathname.split('/').pop() ?? ''
|
||||
const { id } = await params
|
||||
const body = await request.json()
|
||||
const { detailInfo, ...basicInfo } = body.survey
|
||||
|
||||
@ -258,7 +257,6 @@ async function updateSurveySaleDetail(request: NextRequest): Promise<NextRespons
|
||||
return NextResponse.json({ error: 'データ保存に失敗しました。' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {DELETE} /api/survey-sales/:id 조사 매물 삭제 API
|
||||
* @apiName DELETE /api/survey-sales/:id
|
||||
@ -277,9 +275,9 @@ async function updateSurveySaleDetail(request: NextRequest): Promise<NextRespons
|
||||
* {
|
||||
* "message": "success"
|
||||
*/
|
||||
async function deleteSurveySaleDetail(request: NextRequest): Promise<NextResponse> {
|
||||
export async function DELETE(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
try {
|
||||
const id = request.nextUrl.pathname.split('/').pop() ?? ''
|
||||
const { id } = await params
|
||||
|
||||
await prisma.$transaction(async (tx: Prisma.TransactionClient) => {
|
||||
// @ts-ignore
|
||||
@ -345,9 +343,9 @@ async function deleteSurveySaleDetail(request: NextRequest): Promise<NextRespons
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
async function submitSurveySaleDetail(request: NextRequest): Promise<NextResponse> {
|
||||
export async function PATCH(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
try {
|
||||
const id = request.nextUrl.pathname.split('/').pop() ?? ''
|
||||
const { id } = await params
|
||||
const body = await request.json()
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.update({
|
||||
@ -366,8 +364,3 @@ async function submitSurveySaleDetail(request: NextRequest): Promise<NextRespons
|
||||
return NextResponse.json({ error: 'データ保存に失敗しました。' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getSurveySaleDetail)
|
||||
export const PUT = loggerWrapper(updateSurveySaleDetail)
|
||||
export const DELETE = loggerWrapper(deleteSurveySaleDetail)
|
||||
export const PATCH = loggerWrapper(submitSurveySaleDetail)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { convertToSnakeCase } from '@/utils/common-utils'
|
||||
import { loggerWrapper } from '@/libs/api-wrapper'
|
||||
/**
|
||||
* @description 검색 파라미터 타입
|
||||
*/
|
||||
@ -193,7 +192,7 @@ const checkSession = (params: SearchParams) => {
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async function getSurveySales(request: Request) {
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
/** URL 파라미터 파싱 */
|
||||
const { searchParams } = new URL(request.url)
|
||||
@ -274,7 +273,7 @@ async function getSurveySales(request: Request) {
|
||||
*
|
||||
* @apiError {Number} 500 서버 오류
|
||||
*/
|
||||
async function updateSurveySales(request: Request) {
|
||||
export async function PUT(request: Request) {
|
||||
try {
|
||||
/** 요청 바디 파싱 */
|
||||
const body = await request.json()
|
||||
@ -336,7 +335,7 @@ async function updateSurveySales(request: Request) {
|
||||
*
|
||||
* @apiError {Number} 500 서버 오류
|
||||
*/
|
||||
async function createSurveySales(request: Request) {
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
|
||||
@ -394,7 +393,3 @@ async function createSurveySales(request: Request) {
|
||||
return NextResponse.json({ error: 'データ保存に失敗しました。' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = loggerWrapper(getSurveySales)
|
||||
export const PUT = loggerWrapper(updateSurveySales)
|
||||
export const POST = loggerWrapper(createSurveySales)
|
||||
@ -9,7 +9,7 @@ import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/
|
||||
|
||||
export default function SuitableDetailPopup() {
|
||||
const popupController = usePopupController()
|
||||
const { toCodeName, toSuitableDetail, suitableCheckIcon, suitableCheckMemo, getSelectedSuitables, suitableErrorAlert } = useSuitable()
|
||||
const { toCodeName, toSuitableDetail, suitableCheckIcon, suitableCheckMemo, getSelectedSuitables } = useSuitable()
|
||||
|
||||
const [openItems, setOpenItems] = useState<Set<number>>(new Set())
|
||||
const [selectedSuitables, setSelectedSuitables] = useState<Suitable[]>([])
|
||||
@ -24,13 +24,9 @@ export default function SuitableDetailPopup() {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
getSelectedSuitables()
|
||||
.then((res) => {
|
||||
setSelectedSuitables(res)
|
||||
})
|
||||
.catch(() => {
|
||||
suitableErrorAlert()
|
||||
})
|
||||
getSelectedSuitables().then((res) => {
|
||||
setSelectedSuitables(res)
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
||||
@ -48,43 +48,34 @@ export default function SurveySaleSubmitPopup() {
|
||||
})
|
||||
|
||||
const [commCodeList, setCommCodeList] = useState<CommCode[]>([])
|
||||
/** 제출 타겟 데이터 조회 및 제출 폼 데이터 삽입 */
|
||||
useEffect(() => {
|
||||
if (!session?.isLoggedIn || !surveyDetail?.id) return
|
||||
const baseUpdate = {
|
||||
sender: session?.email ?? '',
|
||||
title: '[HANASYS現地調査] 調査物件が提出. (' + surveyDetail?.srlNo + ')',
|
||||
}
|
||||
|
||||
if (session?.role === 'Admin') {
|
||||
getCommCode('SALES_OFFICE_CD').then((codes) => {
|
||||
setCommCodeList(codes)
|
||||
})
|
||||
setSubmitData((prev) => ({
|
||||
...prev,
|
||||
...baseUpdate,
|
||||
}))
|
||||
} else if (session?.role === 'Builder' || session?.role === 'Admin_Sub') {
|
||||
getSubmitTarget({ storeId: surveyDetail?.storeId ?? '', role: session?.role ?? '' }).then((data) => {
|
||||
if (!data) return
|
||||
if (data && data.length > 0) {
|
||||
const updateData: Partial<SubmitFormData> = {
|
||||
sender: session?.email ?? '',
|
||||
title: '[HANASYS現地調査] 調査物件が提出. (' + surveyDetail?.srlNo + ')',
|
||||
targetId: data[0]?.targetStoreId ?? '',
|
||||
targetNm: data[0]?.targetStoreNm ?? '',
|
||||
receiver: data.filter((item) => item.auth === 'S').map((item) => item.repUserEmail),
|
||||
reference: data.filter((item) => item.auth === 'N').map((item) => item.repUserEmail),
|
||||
saleBase: null,
|
||||
contents: '',
|
||||
}
|
||||
setSubmitData((prev) => ({
|
||||
...prev,
|
||||
...updateData,
|
||||
}))
|
||||
if (data) {
|
||||
setSubmitData({
|
||||
...submitData,
|
||||
targetId: data[0].targetStoreId,
|
||||
targetNm: data[0].targetStoreNm,
|
||||
})
|
||||
data.length > 1 &&
|
||||
setSubmitData({
|
||||
...submitData,
|
||||
receiver: data.filter((item) => item.auth === 'S').map((item) => item.repUserEmail),
|
||||
reference: data.filter((item) => item.auth === 'N').map((item) => item.repUserEmail),
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
setSubmitData({
|
||||
...submitData,
|
||||
sender: session?.email ?? '',
|
||||
title: '[HANASYS現地調査] 調査物件が提出. (' + surveyDetail?.srlNo + ')',
|
||||
})
|
||||
}, [session, surveyDetail])
|
||||
|
||||
const FORM_FIELDS: FormField[] = [
|
||||
@ -127,7 +118,6 @@ export default function SurveySaleSubmitPopup() {
|
||||
setIsShow(true)
|
||||
sendEmail({
|
||||
to: submitData.receiver,
|
||||
cc: submitData.reference ?? '',
|
||||
subject: submitData.title,
|
||||
content: contentsRef.current?.innerHTML ?? '',
|
||||
})
|
||||
|
||||
@ -6,16 +6,12 @@ import { useSuitableStore } from '@/store/useSuitableStore'
|
||||
|
||||
export default function SuitableButton() {
|
||||
const popupController = usePopupController()
|
||||
const { getSuitableIds, clearSuitableStore, downloadSuitablePdf, suitableErrorAlert } = useSuitable()
|
||||
const { getSuitableIds, clearSuitableStore, downloadSuitablePdf } = useSuitable()
|
||||
const { selectedItems, addAllSelectedItem } = useSuitableStore()
|
||||
|
||||
/* 데이터 전체 선택 */
|
||||
const handleSelectAll = async () => {
|
||||
try {
|
||||
addAllSelectedItem(await getSuitableIds())
|
||||
} catch (error) {
|
||||
suitableErrorAlert()
|
||||
}
|
||||
addAllSelectedItem(await getSuitableIds())
|
||||
}
|
||||
|
||||
/* 상세 팝업 열기 */
|
||||
|
||||
@ -19,9 +19,7 @@ export default function SuitableList() {
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
isLoading,
|
||||
isError,
|
||||
suitableCheckIcon,
|
||||
suitableErrorAlert,
|
||||
} = useSuitable()
|
||||
const { selectedItems, addSelectedItem, removeSelectedItem } = useSuitableStore()
|
||||
const [openItems, setOpenItems] = useState<Set<number>>(new Set())
|
||||
@ -146,11 +144,6 @@ export default function SuitableList() {
|
||||
useSpinnerStore.getState().setIsShow(isLoading || isFetchingNextPage)
|
||||
}, [isLoading, isFetchingNextPage])
|
||||
|
||||
/* 조회 데이터 오류 처리 */
|
||||
useEffect(() => {
|
||||
if (isError) suitableErrorAlert()
|
||||
}, [isError])
|
||||
|
||||
/* 조회 데이터 없는 경우 */
|
||||
if (!suitableList.length) return <SuitableNoData />
|
||||
|
||||
|
||||
@ -90,9 +90,10 @@ export default function ButtonForm({ mode, setMode, data }: ButtonFormProps) {
|
||||
/** 저장 로직 */
|
||||
const handleSave = (isTemporary: boolean, isSubmitProcess: boolean) => {
|
||||
const emptyField = validateSurveyDetail(data.roof)
|
||||
const hasEmptyField = emptyField?.trim() !== ''
|
||||
|
||||
if (isTemporary) {
|
||||
tempSaveProcess()
|
||||
hasEmptyField ? tempSaveProcess() : saveProcess(emptyField, false)
|
||||
} else {
|
||||
saveProcess(emptyField, isSubmitProcess)
|
||||
}
|
||||
|
||||
@ -94,14 +94,6 @@ export default function DetailForm() {
|
||||
}))
|
||||
const [roofInfoData, setRoofInfoData] = useState<SurveyDetailRequest>(roofInfoForm)
|
||||
|
||||
/** route 에러 처리 - 잘못된 URL 접근 시 생성 페이지로 리다이렉트 */
|
||||
useEffect(() => {
|
||||
if (modeset === 'CREATE' && pathname !== '/survey-sale/regist') {
|
||||
router.replace('/survey-sale/regist')
|
||||
}
|
||||
return
|
||||
}, [modeset, id, pathname])
|
||||
|
||||
/** 제출 팝업 처리 - createSurvey 이후 popup 처리 시 노드 삽입 오류로 인해 별도 처리 */
|
||||
useEffect(() => {
|
||||
const show = searchParams.get('show')
|
||||
|
||||
@ -9,7 +9,7 @@ export function useCommCode() {
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error(`common code (${headCode}) load failed:`, error)
|
||||
throw error
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -48,9 +48,7 @@ export function useInquiry(
|
||||
*/
|
||||
const errorRouter = (error: any) => {
|
||||
const status = error.response?.status
|
||||
if (error.response?.data.error) {
|
||||
alert(error.response?.data.error)
|
||||
}
|
||||
alert(error.response?.data.error)
|
||||
switch (status) {
|
||||
// session 없는 경우
|
||||
case 401:
|
||||
|
||||
@ -51,7 +51,7 @@ export function useSuitable() {
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error(`지붕재 적합성 데이터 조회 실패: ${error}`)
|
||||
throw error
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ export function useSuitable() {
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error(`지붕재 적합성 데이터 아이디 조회 실패: ${error}`)
|
||||
throw error
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ export function useSuitable() {
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error(`지붕재 적합성 상세 데이터 조회 실패: ${error}`)
|
||||
throw error
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,13 +101,9 @@ export function useSuitable() {
|
||||
const getSuitableCommCode = (): void => {
|
||||
const headCodes = Object.values(SUITABLE_HEAD_CODE) as SUITABLE_HEAD_CODE[]
|
||||
for (const code of headCodes) {
|
||||
getCommCode(code)
|
||||
.then((res) => {
|
||||
setSuitableCommCode(code, res)
|
||||
})
|
||||
.catch(() => {
|
||||
suitableErrorAlert()
|
||||
})
|
||||
getCommCode(code).then((res) => {
|
||||
setSuitableCommCode(code, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,8 +169,8 @@ export function useSuitable() {
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
// isError,
|
||||
// error,
|
||||
} = useInfiniteQuery<Suitable[]>({
|
||||
queryKey: ['suitables', 'list', searchCategory, searchKeyword],
|
||||
queryFn: async (context) => {
|
||||
@ -193,8 +189,6 @@ export function useSuitable() {
|
||||
staleTime: 1000 * 60 * 10,
|
||||
gcTime: 1000 * 60 * 10,
|
||||
enabled: searchCategory !== '' || searchKeyword !== '',
|
||||
retry: 1,
|
||||
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
||||
})
|
||||
|
||||
/**
|
||||
@ -269,12 +263,8 @@ export function useSuitable() {
|
||||
* @returns {Promise<Suitable[]>} 선택된 지붕재 적합성 데이터
|
||||
*/
|
||||
const getSelectedSuitables = async (): Promise<Suitable[]> => {
|
||||
try {
|
||||
const { ids, detailIds } = serializeSelectedItems()
|
||||
return await getSuitableDetails(ids, detailIds)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
const { ids, detailIds } = serializeSelectedItems()
|
||||
return await getSuitableDetails(ids, detailIds)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -318,20 +308,11 @@ export function useSuitable() {
|
||||
document.body.removeChild(form)
|
||||
} catch (error) {
|
||||
console.error(`지붕재 적합성 상세 데이터 pdf 다운로드 실패: ${error}`)
|
||||
suitableErrorAlert()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 지붕재 적합성 오류 알림 표시
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
const suitableErrorAlert = (): void => {
|
||||
alert('一時的なエラーが発生しました。 継続的な場合は、管理者に連絡してください。')
|
||||
}
|
||||
|
||||
return {
|
||||
getSuitables,
|
||||
getSuitableIds,
|
||||
getSuitableCommCode,
|
||||
toCodeName,
|
||||
@ -342,13 +323,10 @@ export function useSuitable() {
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
isLoading,
|
||||
isError,
|
||||
error,
|
||||
getSelectedSuitables,
|
||||
clearSuitableStore,
|
||||
suitableCheckIcon,
|
||||
suitableCheckMemo,
|
||||
downloadSuitablePdf,
|
||||
suitableErrorAlert,
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,9 +108,7 @@ export function useSurvey(
|
||||
*/
|
||||
const errorRouter = (error: any) => {
|
||||
const status = error.response?.status
|
||||
if (error.response?.data.error) {
|
||||
alert(error.response?.data.error)
|
||||
}
|
||||
alert(error.response?.data.error)
|
||||
switch (status) {
|
||||
/** session 없는 경우 */
|
||||
case 401:
|
||||
@ -261,9 +259,6 @@ export function useSurvey(
|
||||
queryClient.invalidateQueries({ queryKey: ['survey', id] })
|
||||
queryClient.invalidateQueries({ queryKey: ['survey', 'list'] })
|
||||
},
|
||||
onError: (error: any) => {
|
||||
alert(error.response?.data.error)
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
@ -274,7 +269,7 @@ export function useSurvey(
|
||||
*
|
||||
* @example
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
const { mutateAsync: deleteSurvey, isPending: isDeletingSurvey } = useMutation({
|
||||
mutationFn: async () => {
|
||||
@ -375,9 +370,8 @@ export function useSurvey(
|
||||
`https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter({ zipcode: zipCode.trim() })}`,
|
||||
)
|
||||
return data.results
|
||||
} catch (error: any) {
|
||||
console.error('Failed to fetch zipcode data:', error)
|
||||
alert(error.response?.data.error)
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch zipcode data:', e)
|
||||
throw new Error('Failed to fetch zipcode data')
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
export const useTitle = () => {
|
||||
const getTitle = (pathname: string) => {
|
||||
// Handle dynamic routes first
|
||||
if (pathname.startsWith('/survey-sale/') && pathname !== '/survey-sale/regist') {
|
||||
return '調査物件詳細'
|
||||
if (pathname.startsWith('/survey-sale/') && pathname !== '/survey-sale/basic-info' && pathname !== '/survey-sale/roof-info') {
|
||||
return '調査物件一覧'
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/inquiry/') && pathname !== '/inquiry/list' && pathname !== '/inquiry/regist') {
|
||||
@ -17,8 +17,10 @@ export const useTitle = () => {
|
||||
return '屋根材適合性の確認'
|
||||
case '/survey-sale':
|
||||
return '調査物件一覧'
|
||||
case '/survey-sale/regist':
|
||||
case '/survey-sale/basic-info':
|
||||
return '調査物件登録'
|
||||
case '/survey-sale/roof-info':
|
||||
return '調査物件新規登録'
|
||||
case '/inquiry/list':
|
||||
return '1:1お問い合わせ'
|
||||
case '/inquiry/regist':
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { writeApiLog } from './logger'
|
||||
|
||||
export function loggerWrapper(handler: (req: NextRequest) => Promise<NextResponse>): (req: NextRequest) => Promise<NextResponse> {
|
||||
return async function (req: NextRequest) {
|
||||
const reqClone = req.clone()
|
||||
|
||||
const response = await handler(req)
|
||||
|
||||
await writeApiLog(
|
||||
new NextRequest(req.url, {
|
||||
method: req.method,
|
||||
headers: req.headers,
|
||||
body: reqClone.body ? await reqClone.text() : undefined,
|
||||
}),
|
||||
response.status,
|
||||
)
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
import { NextRequest } from 'next/server'
|
||||
import { join } from 'path'
|
||||
import pino from 'pino'
|
||||
|
||||
/* 실행 모드 */
|
||||
const isProduction = process.env.NODE_ENV === 'production'
|
||||
|
||||
/* 로그 데이터 인터페이스 */
|
||||
interface ApiLogData {
|
||||
responseStatus: number
|
||||
method: string
|
||||
url: string
|
||||
// headers: { [k: string]: string }
|
||||
query: { [k: string]: string }
|
||||
body: string | undefined
|
||||
}
|
||||
|
||||
/* 날짜별 로그 파일 경로 생성 함수 */
|
||||
const getLogFilePath = (): string => {
|
||||
const today = new Date().toISOString().split('T')[0] // YYYY-MM-DD 형식
|
||||
return join(process.cwd(), 'logs', `onsite-survey-${today}.log`)
|
||||
}
|
||||
|
||||
/* 날짜별 로거 생성 클래스 */
|
||||
class DailyLogger {
|
||||
private currentDate: string
|
||||
private logger: pino.Logger
|
||||
private destination: ReturnType<typeof pino.destination>
|
||||
|
||||
constructor() {
|
||||
this.currentDate = new Date().toISOString().split('T')[0]
|
||||
this.destination = pino.destination({
|
||||
dest: getLogFilePath(),
|
||||
mkdir: true,
|
||||
sync: false,
|
||||
})
|
||||
this.logger = this.createLogger()
|
||||
|
||||
/* kill signal 핸들러 등록 */
|
||||
process.on('SIGTERM', this.handleShutdown.bind(this))
|
||||
process.on('SIGINT', this.handleShutdown.bind(this))
|
||||
}
|
||||
|
||||
private async handleShutdown(): Promise<void> {
|
||||
this.destination.flushSync()
|
||||
this.destination.end()
|
||||
}
|
||||
|
||||
private createLogger(): pino.Logger {
|
||||
return pino(
|
||||
{
|
||||
level: isProduction ? 'info' : 'silent',
|
||||
timestamp: pino.stdTimeFunctions.isoTime,
|
||||
},
|
||||
this.destination,
|
||||
)
|
||||
}
|
||||
|
||||
public info(obj: any, msg?: string): void {
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
|
||||
if (today !== this.currentDate) {
|
||||
/* 기존 destination 종료 */
|
||||
this.destination.flushSync()
|
||||
this.destination.end()
|
||||
|
||||
/* 새로운 destination 생성 */
|
||||
this.destination = pino.destination({
|
||||
dest: getLogFilePath(),
|
||||
mkdir: true,
|
||||
sync: false,
|
||||
})
|
||||
this.currentDate = today
|
||||
this.logger = this.createLogger()
|
||||
}
|
||||
|
||||
this.logger.info(obj, msg)
|
||||
}
|
||||
}
|
||||
|
||||
/* 로거 인스턴스 */
|
||||
const dailyLogger = new DailyLogger()
|
||||
|
||||
/* API 로그 기록 함수 */
|
||||
export const writeApiLog = async (request: NextRequest, responseStatus: number): Promise<void> => {
|
||||
const logData: ApiLogData = {
|
||||
responseStatus: responseStatus,
|
||||
method: request.method,
|
||||
url: request.url,
|
||||
// headers: Object.fromEntries(request.headers),
|
||||
query: Object.fromEntries(new URL(request.url).searchParams),
|
||||
body: request.body ? await request.text() : undefined,
|
||||
}
|
||||
dailyLogger.info(logData, 'API Request')
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user