- 사고별 이미지 분석 API 및 항공 미디어 조회 연동 - 사고 마커 팝업 디자인 개선, 필터링된 사고만 지도 표시 - 이미지 분석 시 사고명 파라미터 지원, 기본 예측시간 6시간으로 변경 - 유출량 정밀도 NUMERIC(14,10) 확대 (migration 031) - OpenDrift 유종 매핑 수정 (원유, 등유)
160 lines
5.7 KiB
TypeScript
160 lines
5.7 KiB
TypeScript
import { Router } from 'express';
|
|
import { requireAuth } from '../auth/authMiddleware.js';
|
|
import {
|
|
listIncidents,
|
|
getIncident,
|
|
listIncidentPredictions,
|
|
getIncidentWeather,
|
|
saveIncidentWeather,
|
|
getIncidentMedia,
|
|
getIncidentImageAnalysis,
|
|
} from './incidentsService.js';
|
|
|
|
const router = Router();
|
|
|
|
// ============================================================
|
|
// GET /api/incidents — 사고 목록
|
|
// ============================================================
|
|
router.get('/', requireAuth, async (req, res) => {
|
|
try {
|
|
const { status, region, search, startDate, endDate } = req.query as {
|
|
status?: string;
|
|
region?: string;
|
|
search?: string;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
};
|
|
const incidents = await listIncidents({ status, region, search, startDate, endDate });
|
|
res.json(incidents);
|
|
} catch (err) {
|
|
console.error('[incidents] 사고 목록 조회 오류:', err);
|
|
res.status(500).json({ error: '사고 목록 조회 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
|
|
// ============================================================
|
|
// GET /api/incidents/:sn — 사고 상세
|
|
// ============================================================
|
|
router.get('/:sn', requireAuth, async (req, res) => {
|
|
try {
|
|
const sn = parseInt(req.params.sn as string, 10);
|
|
if (isNaN(sn)) {
|
|
res.status(400).json({ error: '유효하지 않은 사고 번호입니다.' });
|
|
return;
|
|
}
|
|
const incident = await getIncident(sn);
|
|
if (!incident) {
|
|
res.status(404).json({ error: '사고를 찾을 수 없습니다.' });
|
|
return;
|
|
}
|
|
res.json(incident);
|
|
} catch (err) {
|
|
console.error('[incidents] 사고 상세 조회 오류:', err);
|
|
res.status(500).json({ error: '사고 상세 조회 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
|
|
// ============================================================
|
|
// GET /api/incidents/:sn/predictions — 예측 실행 목록
|
|
// ============================================================
|
|
router.get('/:sn/predictions', requireAuth, async (req, res) => {
|
|
try {
|
|
const sn = parseInt(req.params.sn as string, 10);
|
|
if (isNaN(sn)) {
|
|
res.status(400).json({ error: '유효하지 않은 사고 번호입니다.' });
|
|
return;
|
|
}
|
|
const predictions = await listIncidentPredictions(sn);
|
|
res.json(predictions);
|
|
} catch (err) {
|
|
console.error('[incidents] 예측 목록 조회 오류:', err);
|
|
res.status(500).json({ error: '예측 목록 조회 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
|
|
// ============================================================
|
|
// GET /api/incidents/:sn/weather — 기상정보
|
|
// ============================================================
|
|
router.get('/:sn/weather', requireAuth, async (req, res) => {
|
|
try {
|
|
const sn = parseInt(req.params.sn as string, 10);
|
|
if (isNaN(sn)) {
|
|
res.status(400).json({ error: '유효하지 않은 사고 번호입니다.' });
|
|
return;
|
|
}
|
|
const weather = await getIncidentWeather(sn);
|
|
if (!weather) {
|
|
res.json({ message: '기상정보가 없습니다.' });
|
|
return;
|
|
}
|
|
res.json(weather);
|
|
} catch (err) {
|
|
console.error('[incidents] 기상정보 조회 오류:', err);
|
|
res.status(500).json({ error: '기상정보 조회 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
|
|
// ============================================================
|
|
// POST /api/incidents/:sn/weather — 기상정보 저장
|
|
// ============================================================
|
|
router.post('/:sn/weather', requireAuth, async (req, res) => {
|
|
try {
|
|
const sn = parseInt(req.params.sn as string, 10);
|
|
if (isNaN(sn)) {
|
|
res.status(400).json({ error: '유효하지 않은 사고 번호입니다.' });
|
|
return;
|
|
}
|
|
const weatherSn = await saveIncidentWeather(sn, req.body as Record<string, unknown>);
|
|
res.json({ weatherSn });
|
|
} catch (err) {
|
|
console.error('[incidents] 기상정보 저장 오류:', err);
|
|
res.status(500).json({ error: '기상정보 저장 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
|
|
// ============================================================
|
|
// GET /api/incidents/:sn/media — 미디어 정보
|
|
// ============================================================
|
|
router.get('/:sn/media', requireAuth, async (req, res) => {
|
|
try {
|
|
const sn = parseInt(req.params.sn as string, 10);
|
|
if (isNaN(sn)) {
|
|
res.status(400).json({ error: '유효하지 않은 사고 번호입니다.' });
|
|
return;
|
|
}
|
|
const media = await getIncidentMedia(sn);
|
|
if (!media) {
|
|
res.json({ message: '미디어 정보가 없습니다.' });
|
|
return;
|
|
}
|
|
res.json(media);
|
|
} catch (err) {
|
|
console.error('[incidents] 미디어 정보 조회 오류:', err);
|
|
res.status(500).json({ error: '미디어 정보 조회 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
|
|
// ============================================================
|
|
// GET /api/incidents/:sn/image-analysis — 이미지 분석 데이터
|
|
// ============================================================
|
|
router.get('/:sn/image-analysis', requireAuth, async (req, res) => {
|
|
try {
|
|
const sn = parseInt(req.params.sn as string, 10);
|
|
if (isNaN(sn)) {
|
|
res.status(400).json({ error: '유효하지 않은 사고 번호입니다.' });
|
|
return;
|
|
}
|
|
const data = await getIncidentImageAnalysis(sn);
|
|
if (!data) {
|
|
res.status(404).json({ error: '이미지 분석 데이터가 없습니다.' });
|
|
return;
|
|
}
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('[incidents] 이미지 분석 데이터 조회 오류:', err);
|
|
res.status(500).json({ error: '이미지 분석 데이터 조회 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
|
|
export default router;
|