import { query } from '../../utils/db' export default defineEventHandler(async (event) => { const queryParams = getQuery(event) const type = queryParams.type as string || 'short-term' const period = queryParams.period as string || '24h' // 기간/간격 설정 const config = getPeriodConfig(period) // 시간 슬롯 생성 (연속된 X축) const timeSlots = generateTimeSlots(config) // DB에서 로그 조회 let rows: any[] = [] try { rows = await query(` SELECT to_char(detected_at::timestamp, '${config.dbFormat}') as time_slot, SUM(CASE WHEN level = 'warning' THEN 1 ELSE 0 END) as warning, SUM(CASE WHEN level = 'danger' THEN 1 ELSE 0 END) as danger FROM anomaly_logs WHERE detect_type = $1 AND detected_at::timestamp >= NOW() - INTERVAL '${config.interval}' GROUP BY time_slot ORDER BY time_slot ASC `, [type]) } catch (e) { rows = [] } // 로그 데이터를 Map으로 변환 const logMap = new Map() for (const r of rows) { logMap.set(r.time_slot, { warning: Number(r.warning) || 0, danger: Number(r.danger) || 0 }) } // 전체 시간 슬롯에 데이터 매핑 (없으면 0) const data = timeSlots.map(slot => ({ time: slot.label, warning: logMap.get(slot.key)?.warning || 0, danger: logMap.get(slot.key)?.danger || 0 })) return { data, type, period, timestamp: new Date().toISOString() } }) interface PeriodConfig { interval: string stepMinutes: number dbFormat: string labelFormat: (date: Date) => string } function getPeriodConfig(period: string): PeriodConfig { switch (period) { case '1h': return { interval: '1 hour', stepMinutes: 5, dbFormat: 'YYYY-MM-DD HH24:MI', labelFormat: (d) => `${pad(d.getHours())}:${pad(d.getMinutes())}` } case '6h': return { interval: '6 hours', stepMinutes: 30, dbFormat: 'YYYY-MM-DD HH24:MI', labelFormat: (d) => `${pad(d.getHours())}:${pad(d.getMinutes())}` } case '12h': return { interval: '12 hours', stepMinutes: 60, dbFormat: 'YYYY-MM-DD HH24:00', labelFormat: (d) => `${pad(d.getHours())}:00` } case '24h': return { interval: '24 hours', stepMinutes: 60, dbFormat: 'YYYY-MM-DD HH24:00', labelFormat: (d) => `${pad(d.getHours())}:00` } case '7d': return { interval: '7 days', stepMinutes: 360, // 6시간 dbFormat: 'YYYY-MM-DD HH24:00', labelFormat: (d) => `${pad(d.getMonth()+1)}/${pad(d.getDate())} ${pad(d.getHours())}시` } case '30d': return { interval: '30 days', stepMinutes: 1440, // 1일 dbFormat: 'YYYY-MM-DD', labelFormat: (d) => `${pad(d.getMonth()+1)}/${pad(d.getDate())}` } default: return { interval: '24 hours', stepMinutes: 60, dbFormat: 'YYYY-MM-DD HH24:00', labelFormat: (d) => `${pad(d.getHours())}:00` } } } function generateTimeSlots(config: PeriodConfig): { key: string; label: string }[] { const slots: { key: string; label: string }[] = [] const now = new Date() // 현재 시간을 간격에 맞게 내림 const roundedNow = new Date(now) if (config.stepMinutes >= 1440) { // 일 단위: 오늘 00:00 roundedNow.setHours(0, 0, 0, 0) } else if (config.stepMinutes >= 60) { // 시간 단위: 현재 시간 00분 roundedNow.setMinutes(0, 0, 0) } else { // 분 단위: 가장 가까운 간격 const mins = roundedNow.getMinutes() const rounded = Math.floor(mins / config.stepMinutes) * config.stepMinutes roundedNow.setMinutes(rounded, 0, 0) } // interval을 밀리초로 변환 const intervalMs = parseInterval(config.interval) const startTime = new Date(roundedNow.getTime() - intervalMs) // 시작부터 현재까지 슬롯 생성 const stepMs = config.stepMinutes * 60 * 1000 let current = new Date(startTime) while (current <= roundedNow) { const key = formatDbKey(current, config.dbFormat) const label = config.labelFormat(current) slots.push({ key, label }) current = new Date(current.getTime() + stepMs) } return slots } function parseInterval(interval: string): number { const match = interval.match(/(\d+)\s*(hour|hours|day|days)/) if (!match) return 24 * 60 * 60 * 1000 const num = parseInt(match[1]) const unit = match[2] if (unit.startsWith('day')) { return num * 24 * 60 * 60 * 1000 } else { return num * 60 * 60 * 1000 } } function formatDbKey(date: Date, format: string): string { const y = date.getFullYear() const m = pad(date.getMonth() + 1) const d = pad(date.getDate()) const h = pad(date.getHours()) const mi = pad(date.getMinutes()) if (format === 'YYYY-MM-DD') { return `${y}-${m}-${d}` } else if (format === 'YYYY-MM-DD HH24:00') { return `${y}-${m}-${d} ${h}:00` } else { return `${y}-${m}-${d} ${h}:${mi}` } } function pad(n: number): string { return n.toString().padStart(2, '0') }