wing-ops/backend/src/hns/hnsRouter.ts
htlee ff085252b0 feat(phase4): Board/HNS/Prediction/Aerial/Rescue Mock → API 전환
- Board: 매뉴얼 CRUD + 첨부파일 API (012_board_ext.sql)
- HNS: 분석 CRUD 5개 API (013_hns_analysis.sql)
- Prediction: 분석/역추적/오일펜스 7개 API (014_prediction.sql)
- Aerial: 미디어/CCTV/위성 6개 API + PostGIS (015_aerial.sql)
- Rescue: 구난 작전/시나리오 3개 API + JSONB (016_rescue.sql)
- backtrackMockData.ts 삭제

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 01:17:10 +09:00

133 lines
4.7 KiB
TypeScript

import express from 'express'
import { searchSubstances, getSubstanceById, listAnalyses, getAnalysis, createAnalysis, deleteAnalysis } from './hnsService.js'
import { isValidNumber } from '../middleware/security.js'
import { requireAuth, requirePermission } from '../auth/authMiddleware.js'
const router = express.Router()
// ============================================================
// HNS 분석 라우트 (/:id 보다 먼저 등록)
// ============================================================
// GET /api/hns/analyses — 분석 목록
router.get('/analyses', requireAuth, requirePermission('hns', 'READ'), async (req, res) => {
try {
const { status, substance, search } = req.query
const items = await listAnalyses({
status: status as string | undefined,
substance: substance as string | undefined,
search: search as string | undefined,
})
res.json(items)
} catch (err) {
console.error('[hns] 분석 목록 오류:', err)
res.status(500).json({ error: 'HNS 분석 목록 조회 실패' })
}
})
// GET /api/hns/analyses/:sn — 분석 상세
router.get('/analyses/:sn', requireAuth, requirePermission('hns', 'READ'), async (req, res) => {
try {
const sn = parseInt(req.params.sn as string, 10)
if (!isValidNumber(sn, 1, 999999)) {
res.status(400).json({ error: '유효하지 않은 분석 번호' })
return
}
const item = await getAnalysis(sn)
if (!item) {
res.status(404).json({ error: '분석을 찾을 수 없습니다' })
return
}
res.json(item)
} catch (err) {
console.error('[hns] 분석 상세 오류:', err)
res.status(500).json({ error: 'HNS 분석 조회 실패' })
}
})
// POST /api/hns/analyses — 분석 생성
router.post('/analyses', requireAuth, requirePermission('hns', 'CREATE'), async (req, res) => {
try {
const { anlysNm, acdntDtm, locNm, lon, lat, sbstNm, spilQty, spilUnitCd, fcstHr, algoCd, critMdlCd, windSpd, windDir, temp, humid, atmStblCd, analystNm } = req.body
if (!anlysNm) {
res.status(400).json({ error: '분석명은 필수입니다.' })
return
}
const result = await createAnalysis({
anlysNm, acdntDtm, locNm, lon, lat, sbstNm, spilQty, spilUnitCd, fcstHr, algoCd, critMdlCd, windSpd, windDir, temp, humid, atmStblCd, analystNm,
})
res.status(201).json(result)
} catch (err) {
console.error('[hns] 분석 생성 오류:', err)
res.status(500).json({ error: 'HNS 분석 생성 실패' })
}
})
// DELETE /api/hns/analyses/:sn — 분석 삭제
router.delete('/analyses/:sn', requireAuth, requirePermission('hns', 'DELETE'), async (req, res) => {
try {
const sn = parseInt(req.params.sn as string, 10)
if (!isValidNumber(sn, 1, 999999)) {
res.status(400).json({ error: '유효하지 않은 분석 번호' })
return
}
await deleteAnalysis(sn)
res.json({ success: true })
} catch (err) {
console.error('[hns] 분석 삭제 오류:', err)
res.status(500).json({ error: 'HNS 분석 삭제 실패' })
}
})
// ============================================================
// HNS 물질 라우트
// ============================================================
// HNS 물질 검색
router.get('/', async (req, res) => {
try {
const q = req.query.q as string | undefined
const type = req.query.type as string | undefined
const sebc = req.query.sebc as string | undefined
const page = parseInt(req.query.page as string, 10) || 1
const limit = parseInt(req.query.limit as string, 10) || 50
if (!isValidNumber(page, 1, 10000) || !isValidNumber(limit, 1, 100)) {
return res.status(400).json({
error: '유효하지 않은 페이지네이션',
message: 'page는 1~10000, limit은 1~100 범위여야 합니다.',
})
}
const validTypes = ['abbreviation', 'nameKr', 'nameEn', 'casNumber', 'unNumber', 'cargoCode']
const searchType = type && validTypes.includes(type)
? type as 'abbreviation' | 'nameKr' | 'nameEn' | 'casNumber' | 'unNumber' | 'cargoCode'
: undefined
const result = await searchSubstances({ q, type: searchType, sebc, page, limit })
res.json(result)
} catch {
res.status(500).json({ error: 'HNS 물질 검색 실패' })
}
})
// HNS 물질 상세 조회
router.get('/:id', async (req, res) => {
try {
const id = parseInt(req.params.id, 10)
if (!isValidNumber(id, 1, 999999)) {
return res.status(400).json({ error: '유효하지 않은 물질 ID' })
}
const substance = await getSubstanceById(id)
if (!substance) {
return res.status(404).json({ error: '물질을 찾을 수 없습니다' })
}
res.json(substance)
} catch {
res.status(500).json({ error: 'HNS 물질 조회 실패' })
}
})
export default router