wing-ops/backend/src/reports/reportsRouter.ts

189 lines
6.9 KiB
TypeScript

import { Router } from 'express';
import { requireAuth, requirePermission } from '../auth/authMiddleware.js';
import { AuthError } from '../auth/authService.js';
import {
getTemplates,
getCategories,
listReports,
getReport,
createReport,
updateReport,
deleteReport,
updateReportSection,
} from './reportsService.js';
const router = Router();
// ============================================================
// GET /api/reports/templates — 템플릿 목록 + 섹션 정의
// ============================================================
router.get('/templates', requireAuth, async (_req, res) => {
try {
const templates = await getTemplates();
res.json(templates);
} catch (err) {
console.error('[reports] 템플릿 조회 오류:', err);
res.status(500).json({ error: '템플릿 조회 중 오류가 발생했습니다.' });
}
});
// ============================================================
// GET /api/reports/categories — 분석 카테고리 목록 + 섹션 정의
// ============================================================
router.get('/categories', requireAuth, async (_req, res) => {
try {
const categories = await getCategories();
res.json(categories);
} catch (err) {
console.error('[reports] 카테고리 조회 오류:', err);
res.status(500).json({ error: '카테고리 조회 중 오류가 발생했습니다.' });
}
});
// ============================================================
// GET /api/reports — 보고서 목록
// ============================================================
router.get('/', requireAuth, requirePermission('reports', 'READ'), async (req, res) => {
try {
const { jrsdCd, tmplCd, sttsCd, search, page, size } = req.query;
const result = await listReports({
jrsdCd: jrsdCd as string | undefined,
tmplCd: tmplCd as string | undefined,
sttsCd: sttsCd as string | undefined,
search: search as string | undefined,
page: page ? parseInt(page as string, 10) : undefined,
size: size ? parseInt(size as string, 10) : undefined,
});
res.json(result);
} catch (err) {
console.error('[reports] 목록 조회 오류:', err);
if (err instanceof AuthError) {
res.status(err.status).json({ error: err.message });
} else {
res.status(500).json({ error: '보고서 목록 조회 중 오류가 발생했습니다.' });
}
}
});
// ============================================================
// GET /api/reports/:sn — 보고서 상세
// ============================================================
router.get('/:sn', requireAuth, requirePermission('reports', 'READ'), async (req, res) => {
try {
const sn = parseInt(req.params.sn as string, 10);
if (isNaN(sn)) {
res.status(400).json({ error: '유효하지 않은 보고서 번호입니다.' });
return;
}
const report = await getReport(sn);
res.json(report);
} catch (err) {
if (err instanceof AuthError) {
res.status(err.status).json({ error: err.message });
} else {
console.error('[reports] 상세 조회 오류:', err);
res.status(500).json({ error: '보고서 조회 중 오류가 발생했습니다.' });
}
}
});
// ============================================================
// POST /api/reports — 보고서 생성
// ============================================================
router.post('/', requireAuth, requirePermission('reports', 'CREATE'), async (req, res) => {
try {
const { tmplSn, ctgrSn, acdntSn, title, jrsdCd, sttsCd, sections, mapCaptureImg, step3MapImg, step6MapImg } = req.body;
const result = await createReport({
tmplSn,
ctgrSn,
acdntSn,
title,
jrsdCd,
sttsCd,
authorId: req.user!.sub,
mapCaptureImg,
step3MapImg,
step6MapImg,
sections,
});
res.status(201).json(result);
} catch (err) {
if (err instanceof AuthError) {
res.status(err.status).json({ error: err.message });
} else {
console.error('[reports] 생성 오류:', err);
res.status(500).json({ error: '보고서 생성 중 오류가 발생했습니다.' });
}
}
});
// ============================================================
// POST /api/reports/:sn/update — 보고서 수정
// ============================================================
router.post('/:sn/update', requireAuth, requirePermission('reports', 'UPDATE'), async (req, res) => {
try {
const sn = parseInt(req.params.sn as string, 10);
if (isNaN(sn)) {
res.status(400).json({ error: '유효하지 않은 보고서 번호입니다.' });
return;
}
const { title, jrsdCd, sttsCd, acdntSn, sections, mapCaptureImg, step3MapImg, step6MapImg } = req.body;
await updateReport(sn, { title, jrsdCd, sttsCd, acdntSn, sections, mapCaptureImg, step3MapImg, step6MapImg }, req.user!.sub);
res.json({ success: true });
} catch (err) {
if (err instanceof AuthError) {
res.status(err.status).json({ error: err.message });
} else {
console.error('[reports] 수정 오류:', err);
res.status(500).json({ error: '보고서 수정 중 오류가 발생했습니다.' });
}
}
});
// ============================================================
// POST /api/reports/:sn/delete — 보고서 삭제 (논리 삭제)
// ============================================================
router.post('/:sn/delete', requireAuth, requirePermission('reports', 'DELETE'), async (req, res) => {
try {
const sn = parseInt(req.params.sn as string, 10);
if (isNaN(sn)) {
res.status(400).json({ error: '유효하지 않은 보고서 번호입니다.' });
return;
}
await deleteReport(sn, req.user!.sub);
res.json({ success: true });
} catch (err) {
if (err instanceof AuthError) {
res.status(err.status).json({ error: err.message });
} else {
console.error('[reports] 삭제 오류:', err);
res.status(500).json({ error: '보고서 삭제 중 오류가 발생했습니다.' });
}
}
});
// ============================================================
// POST /api/reports/:sn/sections/:sectCd — 개별 섹션 수정
// ============================================================
router.post('/:sn/sections/:sectCd', requireAuth, requirePermission('reports', 'UPDATE'), async (req, res) => {
try {
const sn = parseInt(req.params.sn as string, 10);
if (isNaN(sn)) {
res.status(400).json({ error: '유효하지 않은 보고서 번호입니다.' });
return;
}
const { sectData, includeYn } = req.body;
await updateReportSection(sn, req.params.sectCd as string, sectData, includeYn, req.user!.sub);
res.json({ success: true });
} catch (err) {
if (err instanceof AuthError) {
res.status(err.status).json({ error: err.message });
} else {
console.error('[reports] 섹션 수정 오류:', err);
res.status(500).json({ error: '섹션 수정 중 오류가 발생했습니다.' });
}
}
});
export default router;