Docker 파일

This commit is contained in:
2025-12-28 14:31:12 +09:00
parent ba7a208da9
commit 6196c03e46
23 changed files with 302 additions and 355 deletions

View File

@@ -1,36 +1,29 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const { year, month, week } = query as { year?: string, month?: string, week?: string } const { year, month, week } = queryParams as { year?: string, month?: string, week?: string }
if (!year || !month || !week) { if (!year || !month || !week) {
return { error: 'year, month, week are required' } return { error: 'year, month, week are required' }
} }
// 해당 월의 첫날과 마지막날
const y = parseInt(year) const y = parseInt(year)
const m = parseInt(month) const m = parseInt(month)
const w = parseInt(week) const w = parseInt(week)
// 해당 월의 첫날
const firstDayOfMonth = new Date(y, m - 1, 1) const firstDayOfMonth = new Date(y, m - 1, 1)
const firstDayWeekday = firstDayOfMonth.getDay() // 0=일, 1=월, ... const firstDayWeekday = firstDayOfMonth.getDay()
// 주차의 시작일 계산 (월요일 기준)
// 1주차: 1일이 포함된 주
const mondayOffset = firstDayWeekday === 0 ? -6 : 1 - firstDayWeekday const mondayOffset = firstDayWeekday === 0 ? -6 : 1 - firstDayWeekday
const firstMondayOfMonth = new Date(y, m - 1, 1 + mondayOffset) const firstMondayOfMonth = new Date(y, m - 1, 1 + mondayOffset)
// 선택한 주차의 월요일
const weekStart = new Date(firstMondayOfMonth) const weekStart = new Date(firstMondayOfMonth)
weekStart.setDate(weekStart.getDate() + (w - 1) * 7) weekStart.setDate(weekStart.getDate() + (w - 1) * 7)
// 주차의 일요일
const weekEnd = new Date(weekStart) const weekEnd = new Date(weekStart)
weekEnd.setDate(weekEnd.getDate() + 6) weekEnd.setDate(weekEnd.getDate() + 6)
// 해당 주의 날짜 목록 생성
const weekDates: string[] = [] const weekDates: string[] = []
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
const d = new Date(weekStart) const d = new Date(weekStart)
@@ -39,31 +32,23 @@ export default defineEventHandler(async (event) => {
weekDates.push(dateStr) weekDates.push(dateStr)
} }
const db = getDb()
// 시작/종료 날짜
const startDate = weekDates[0] const startDate = weekDates[0]
const endDate = weekDates[6] const endDate = weekDates[6]
// 1시간 단위 성공률 조회 const heatmapData = await query(`
const heatmapData = db.prepare(`
SELECT SELECT
date(checked_at) as date, TO_CHAR(checked_at::timestamp, 'YYYY-MM-DD') as date,
strftime('%H', checked_at) || ':00' as time_slot, TO_CHAR(checked_at::timestamp, 'HH24:00') as time_slot,
COUNT(*) as total_count, COUNT(*) as total_count,
SUM(CASE WHEN is_success = 1 THEN 1 ELSE 0 END) as success_count, SUM(CASE WHEN is_success = 1 THEN 1 ELSE 0 END) as success_count,
ROUND(SUM(CASE WHEN is_success = 1 THEN 1.0 ELSE 0.0 END) / COUNT(*) * 100, 1) as success_rate ROUND(SUM(CASE WHEN is_success = 1 THEN 1.0 ELSE 0.0 END) / COUNT(*) * 100, 1) as success_rate
FROM privnet_logs FROM privnet_logs
WHERE date(checked_at) >= ? AND date(checked_at) <= ? WHERE checked_at::date >= $1 AND checked_at::date <= $2
GROUP BY date, time_slot GROUP BY date, time_slot
ORDER BY date, time_slot ORDER BY date, time_slot
`).all(startDate, endDate) `, [startDate, endDate])
// 해당 월의 주차 수 계산
const lastDayOfMonth = new Date(y, m, 0) const lastDayOfMonth = new Date(y, m, 0)
const lastDate = lastDayOfMonth.getDate()
// 마지막 날이 몇 주차인지 계산
const lastDayFromFirstMonday = Math.floor((lastDayOfMonth.getTime() - firstMondayOfMonth.getTime()) / (1000 * 60 * 60 * 24)) const lastDayFromFirstMonday = Math.floor((lastDayOfMonth.getTime() - firstMondayOfMonth.getTime()) / (1000 * 60 * 60 * 24))
const totalWeeks = Math.ceil((lastDayFromFirstMonday + 1) / 7) const totalWeeks = Math.ceil((lastDayFromFirstMonday + 1) / 7)

View File

@@ -1,8 +1,8 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const { year, month, day, hour } = query as { const { year, month, day, hour } = queryParams as {
year?: string, month?: string, day?: string, hour?: string year?: string, month?: string, day?: string, hour?: string
} }
@@ -10,13 +10,10 @@ export default defineEventHandler(async (event) => {
return { error: 'year, month, day, hour are required' } return { error: 'year, month, day, hour are required' }
} }
const db = getDb()
// 해당 시간대 로그 조회
const startTime = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:00:00` const startTime = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:00:00`
const endTime = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:59:59` const endTime = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:59:59`
const logs = db.prepare(` const logs = await query(`
SELECT SELECT
l.id, l.id,
l.checked_at, l.checked_at,
@@ -25,9 +22,9 @@ export default defineEventHandler(async (event) => {
t.url as target_url t.url as target_url
FROM privnet_logs l FROM privnet_logs l
JOIN privnet_targets t ON l.target_id = t.id JOIN privnet_targets t ON l.target_id = t.id
WHERE l.checked_at >= ? AND l.checked_at <= ? WHERE l.checked_at >= $1 AND l.checked_at <= $2
ORDER BY l.checked_at DESC ORDER BY l.checked_at DESC
`).all(startTime, endTime) `, [startTime, endTime])
return { logs } return { logs }
}) })

View File

@@ -1,11 +1,8 @@
import { getDb } from '../../../utils/db' import { query, queryOne } from '../../../utils/db'
import { privnetScheduler } from '../../../utils/privnet-scheduler' import { privnetScheduler } from '../../../utils/privnet-scheduler'
export default defineEventHandler(() => { export default defineEventHandler(async () => {
const db = getDb() const status = await queryOne(`
// 현재 상태 조회
const status = db.prepare(`
SELECT SELECT
ps.*, ps.*,
pt.name as last_target_name, pt.name as last_target_name,
@@ -13,10 +10,9 @@ export default defineEventHandler(() => {
FROM privnet_status ps FROM privnet_status ps
LEFT JOIN privnet_targets pt ON ps.last_target_id = pt.id LEFT JOIN privnet_targets pt ON ps.last_target_id = pt.id
WHERE ps.id = 1 WHERE ps.id = 1
`).get() `)
// 최근 10개 로그 const recentLogs = await query(`
const recentLogs = db.prepare(`
SELECT SELECT
pl.*, pl.*,
pt.name as target_name, pt.name as target_name,
@@ -25,17 +21,16 @@ export default defineEventHandler(() => {
JOIN privnet_targets pt ON pl.target_id = pt.id JOIN privnet_targets pt ON pl.target_id = pt.id
ORDER BY pl.checked_at DESC ORDER BY pl.checked_at DESC
LIMIT 10 LIMIT 10
`).all() `)
// 활성 타겟 수 const targetCount = await queryOne<{ cnt: number }>(`
const targetCount = db.prepare(`
SELECT COUNT(*) as cnt FROM privnet_targets WHERE is_active = 1 SELECT COUNT(*) as cnt FROM privnet_targets WHERE is_active = 1
`).get() as { cnt: number } `)
return { return {
status, status,
recentLogs, recentLogs,
targetCount: targetCount.cnt, targetCount: targetCount?.cnt || 0,
schedulerRunning: privnetScheduler.getIsRunning() schedulerRunning: privnetScheduler.getIsRunning()
} }
}) })

View File

@@ -1,10 +1,9 @@
import { getDb } from '../../../../utils/db' import { execute } from '../../../../utils/db'
export default defineEventHandler((event) => { export default defineEventHandler(async (event) => {
const db = getDb()
const id = getRouterParam(event, 'id') const id = getRouterParam(event, 'id')
db.prepare(`DELETE FROM privnet_targets WHERE id = ?`).run(id) await execute(`DELETE FROM privnet_targets WHERE id = $1`, [id])
return { success: true } return { success: true }
}) })

View File

@@ -1,7 +1,6 @@
import { getDb } from '../../../../utils/db' import { execute } from '../../../../utils/db'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const db = getDb()
const id = getRouterParam(event, 'id') const id = getRouterParam(event, 'id')
const body = await readBody(event) const body = await readBody(event)
@@ -14,11 +13,11 @@ export default defineEventHandler(async (event) => {
}) })
} }
db.prepare(` await execute(`
UPDATE privnet_targets UPDATE privnet_targets
SET name = ?, url = ?, is_active = ?, updated_at = datetime('now', 'localtime') SET name = $1, url = $2, is_active = $3, updated_at = TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS')
WHERE id = ? WHERE id = $4
`).run(name, url, is_active ? 1 : 0, id) `, [name, url, is_active ? 1 : 0, id])
return { return {
id: Number(id), id: Number(id),

View File

@@ -1,12 +1,10 @@
import { getDb } from '../../../../utils/db' import { query } from '../../../../utils/db'
export default defineEventHandler(() => { export default defineEventHandler(async () => {
const db = getDb() const targets = await query(`
const targets = db.prepare(`
SELECT * FROM privnet_targets SELECT * FROM privnet_targets
ORDER BY id ASC ORDER BY id ASC
`).all() `)
return targets return targets
}) })

View File

@@ -1,7 +1,6 @@
import { getDb } from '../../../../utils/db' import { queryOne } from '../../../../utils/db'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const db = getDb()
const body = await readBody(event) const body = await readBody(event)
const { name, url, is_active } = body const { name, url, is_active } = body
@@ -13,13 +12,14 @@ export default defineEventHandler(async (event) => {
}) })
} }
const result = db.prepare(` const result = await queryOne<{ id: number }>(`
INSERT INTO privnet_targets (name, url, is_active) INSERT INTO privnet_targets (name, url, is_active)
VALUES (?, ?, ?) VALUES ($1, $2, $3)
`).run(name, url, is_active ? 1 : 0) RETURNING id
`, [name, url, is_active ? 1 : 0])
return { return {
id: result.lastInsertRowid, id: result?.id,
name, name,
url, url,
is_active: is_active ? 1 : 0 is_active: is_active ? 1 : 0

View File

@@ -1,36 +1,29 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const { year, month, week } = query as { year?: string, month?: string, week?: string } const { year, month, week } = queryParams as { year?: string, month?: string, week?: string }
if (!year || !month || !week) { if (!year || !month || !week) {
return { error: 'year, month, week are required' } return { error: 'year, month, week are required' }
} }
// 해당 월의 첫날과 마지막날
const y = parseInt(year) const y = parseInt(year)
const m = parseInt(month) const m = parseInt(month)
const w = parseInt(week) const w = parseInt(week)
// 해당 월의 첫날
const firstDayOfMonth = new Date(y, m - 1, 1) const firstDayOfMonth = new Date(y, m - 1, 1)
const firstDayWeekday = firstDayOfMonth.getDay() // 0=일, 1=월, ... const firstDayWeekday = firstDayOfMonth.getDay()
// 주차의 시작일 계산 (월요일 기준)
// 1주차: 1일이 포함된 주
const mondayOffset = firstDayWeekday === 0 ? -6 : 1 - firstDayWeekday const mondayOffset = firstDayWeekday === 0 ? -6 : 1 - firstDayWeekday
const firstMondayOfMonth = new Date(y, m - 1, 1 + mondayOffset) const firstMondayOfMonth = new Date(y, m - 1, 1 + mondayOffset)
// 선택한 주차의 월요일
const weekStart = new Date(firstMondayOfMonth) const weekStart = new Date(firstMondayOfMonth)
weekStart.setDate(weekStart.getDate() + (w - 1) * 7) weekStart.setDate(weekStart.getDate() + (w - 1) * 7)
// 주차의 일요일
const weekEnd = new Date(weekStart) const weekEnd = new Date(weekStart)
weekEnd.setDate(weekEnd.getDate() + 6) weekEnd.setDate(weekEnd.getDate() + 6)
// 해당 주의 날짜 목록 생성
const weekDates: string[] = [] const weekDates: string[] = []
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
const d = new Date(weekStart) const d = new Date(weekStart)
@@ -39,31 +32,23 @@ export default defineEventHandler(async (event) => {
weekDates.push(dateStr) weekDates.push(dateStr)
} }
const db = getDb()
// 시작/종료 날짜
const startDate = weekDates[0] const startDate = weekDates[0]
const endDate = weekDates[6] const endDate = weekDates[6]
// 1시간 단위 성공률 조회 const heatmapData = await query(`
const heatmapData = db.prepare(`
SELECT SELECT
date(checked_at) as date, TO_CHAR(checked_at::timestamp, 'YYYY-MM-DD') as date,
strftime('%H', checked_at) || ':00' as time_slot, TO_CHAR(checked_at::timestamp, 'HH24:00') as time_slot,
COUNT(*) as total_count, COUNT(*) as total_count,
SUM(CASE WHEN is_success = 1 THEN 1 ELSE 0 END) as success_count, SUM(CASE WHEN is_success = 1 THEN 1 ELSE 0 END) as success_count,
ROUND(SUM(CASE WHEN is_success = 1 THEN 1.0 ELSE 0.0 END) / COUNT(*) * 100, 1) as success_rate ROUND(SUM(CASE WHEN is_success = 1 THEN 1.0 ELSE 0.0 END) / COUNT(*) * 100, 1) as success_rate
FROM pubnet_logs FROM pubnet_logs
WHERE date(checked_at) >= ? AND date(checked_at) <= ? WHERE checked_at::date >= $1 AND checked_at::date <= $2
GROUP BY date, time_slot GROUP BY date, time_slot
ORDER BY date, time_slot ORDER BY date, time_slot
`).all(startDate, endDate) `, [startDate, endDate])
// 해당 월의 주차 수 계산
const lastDayOfMonth = new Date(y, m, 0) const lastDayOfMonth = new Date(y, m, 0)
const lastDate = lastDayOfMonth.getDate()
// 마지막 날이 몇 주차인지 계산
const lastDayFromFirstMonday = Math.floor((lastDayOfMonth.getTime() - firstMondayOfMonth.getTime()) / (1000 * 60 * 60 * 24)) const lastDayFromFirstMonday = Math.floor((lastDayOfMonth.getTime() - firstMondayOfMonth.getTime()) / (1000 * 60 * 60 * 24))
const totalWeeks = Math.ceil((lastDayFromFirstMonday + 1) / 7) const totalWeeks = Math.ceil((lastDayFromFirstMonday + 1) / 7)

View File

@@ -1,8 +1,8 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const { year, month, day, hour } = query as { const { year, month, day, hour } = queryParams as {
year?: string, month?: string, day?: string, hour?: string year?: string, month?: string, day?: string, hour?: string
} }
@@ -10,13 +10,10 @@ export default defineEventHandler(async (event) => {
return { error: 'year, month, day, hour are required' } return { error: 'year, month, day, hour are required' }
} }
const db = getDb()
// 해당 시간대 로그 조회
const startTime = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:00:00` const startTime = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:00:00`
const endTime = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:59:59` const endTime = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:59:59`
const logs = db.prepare(` const logs = await query(`
SELECT SELECT
l.id, l.id,
l.checked_at, l.checked_at,
@@ -25,9 +22,9 @@ export default defineEventHandler(async (event) => {
t.url as target_url t.url as target_url
FROM pubnet_logs l FROM pubnet_logs l
JOIN pubnet_targets t ON l.target_id = t.id JOIN pubnet_targets t ON l.target_id = t.id
WHERE l.checked_at >= ? AND l.checked_at <= ? WHERE l.checked_at >= $1 AND l.checked_at <= $2
ORDER BY l.checked_at DESC ORDER BY l.checked_at DESC
`).all(startTime, endTime) `, [startTime, endTime])
return { logs } return { logs }
}) })

View File

@@ -1,11 +1,8 @@
import { getDb } from '../../../utils/db' import { query, queryOne } from '../../../utils/db'
import { pubnetScheduler } from '../../../utils/pubnet-scheduler' import { pubnetScheduler } from '../../../utils/pubnet-scheduler'
export default defineEventHandler(() => { export default defineEventHandler(async () => {
const db = getDb() const status = await queryOne(`
// 현재 상태 조회
const status = db.prepare(`
SELECT SELECT
ps.*, ps.*,
pt.name as last_target_name, pt.name as last_target_name,
@@ -13,10 +10,9 @@ export default defineEventHandler(() => {
FROM pubnet_status ps FROM pubnet_status ps
LEFT JOIN pubnet_targets pt ON ps.last_target_id = pt.id LEFT JOIN pubnet_targets pt ON ps.last_target_id = pt.id
WHERE ps.id = 1 WHERE ps.id = 1
`).get() `)
// 최근 10개 로그 const recentLogs = await query(`
const recentLogs = db.prepare(`
SELECT SELECT
pl.*, pl.*,
pt.name as target_name, pt.name as target_name,
@@ -25,17 +21,16 @@ export default defineEventHandler(() => {
JOIN pubnet_targets pt ON pl.target_id = pt.id JOIN pubnet_targets pt ON pl.target_id = pt.id
ORDER BY pl.checked_at DESC ORDER BY pl.checked_at DESC
LIMIT 10 LIMIT 10
`).all() `)
// 활성 타겟 수 const targetCount = await queryOne<{ cnt: number }>(`
const targetCount = db.prepare(`
SELECT COUNT(*) as cnt FROM pubnet_targets WHERE is_active = 1 SELECT COUNT(*) as cnt FROM pubnet_targets WHERE is_active = 1
`).get() as { cnt: number } `)
return { return {
status, status,
recentLogs, recentLogs,
targetCount: targetCount.cnt, targetCount: targetCount?.cnt || 0,
schedulerRunning: pubnetScheduler.getIsRunning() schedulerRunning: pubnetScheduler.getIsRunning()
} }
}) })

View File

@@ -1,10 +1,9 @@
import { getDb } from '../../../../utils/db' import { execute } from '../../../../utils/db'
export default defineEventHandler((event) => { export default defineEventHandler(async (event) => {
const db = getDb()
const id = getRouterParam(event, 'id') const id = getRouterParam(event, 'id')
db.prepare(`DELETE FROM pubnet_targets WHERE id = ?`).run(id) await execute(`DELETE FROM pubnet_targets WHERE id = $1`, [id])
return { success: true } return { success: true }
}) })

View File

@@ -1,7 +1,6 @@
import { getDb } from '../../../../utils/db' import { execute } from '../../../../utils/db'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const db = getDb()
const id = getRouterParam(event, 'id') const id = getRouterParam(event, 'id')
const body = await readBody(event) const body = await readBody(event)
@@ -14,11 +13,11 @@ export default defineEventHandler(async (event) => {
}) })
} }
db.prepare(` await execute(`
UPDATE pubnet_targets UPDATE pubnet_targets
SET name = ?, url = ?, is_active = ?, updated_at = datetime('now', 'localtime') SET name = $1, url = $2, is_active = $3, updated_at = TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS')
WHERE id = ? WHERE id = $4
`).run(name, url, is_active ? 1 : 0, id) `, [name, url, is_active ? 1 : 0, id])
return { return {
id: Number(id), id: Number(id),

View File

@@ -1,12 +1,10 @@
import { getDb } from '../../../../utils/db' import { query } from '../../../../utils/db'
export default defineEventHandler(() => { export default defineEventHandler(async () => {
const db = getDb() const targets = await query(`
const targets = db.prepare(`
SELECT * FROM pubnet_targets SELECT * FROM pubnet_targets
ORDER BY id ASC ORDER BY id ASC
`).all() `)
return targets return targets
}) })

View File

@@ -1,7 +1,6 @@
import { getDb } from '../../../../utils/db' import { queryOne } from '../../../../utils/db'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const db = getDb()
const body = await readBody(event) const body = await readBody(event)
const { name, url, is_active } = body const { name, url, is_active } = body
@@ -13,13 +12,14 @@ export default defineEventHandler(async (event) => {
}) })
} }
const result = db.prepare(` const result = await queryOne<{ id: number }>(`
INSERT INTO pubnet_targets (name, url, is_active) INSERT INTO pubnet_targets (name, url, is_active)
VALUES (?, ?, ?) VALUES ($1, $2, $3)
`).run(name, url, is_active ? 1 : 0) RETURNING id
`, [name, url, is_active ? 1 : 0])
return { return {
id: result.lastInsertRowid, id: result?.id,
name, name,
url, url,
is_active: is_active ? 1 : 0 is_active: is_active ? 1 : 0

View File

@@ -1,8 +1,8 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler((event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const targetId = query.target_id as string const targetId = queryParams.target_id as string
if (!targetId) { if (!targetId) {
throw createError({ throw createError({
@@ -11,20 +11,22 @@ export default defineEventHandler((event) => {
}) })
} }
const db = getDb() let containers: any[] = []
try {
// 최신 수집 시간 기준 컨테이너 목록 containers = await query(`
const containers = db.prepare(` SELECT DISTINCT container_name
SELECT DISTINCT container_name FROM server_containers
FROM server_containers WHERE target_id = $1
WHERE target_id = ? AND collected_at = (
AND collected_at = ( SELECT MAX(collected_at)
SELECT MAX(collected_at) FROM server_containers
FROM server_containers WHERE target_id = $1
WHERE target_id = ? )
) ORDER BY container_name ASC
ORDER BY container_name ASC `, [targetId])
`).all(targetId, targetId) } catch (e) {
containers = []
}
return containers.map((c: any) => c.container_name) return containers.map((c: any) => c.container_name)
}) })

View File

@@ -1,9 +1,9 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler((event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const targetId = query.target_id as string const targetId = queryParams.target_id as string
const period = (query.period as string) || '1h' const period = (queryParams.period as string) || '1h'
if (!targetId) { if (!targetId) {
throw createError({ throw createError({
@@ -13,41 +13,44 @@ export default defineEventHandler((event) => {
} }
const periodMap: Record<string, string> = { const periodMap: Record<string, string> = {
'1h': '-1 hour', '1h': '1 hour',
'2h': '-2 hours', '2h': '2 hours',
'3h': '-3 hours', '3h': '3 hours',
'4h': '-4 hours', '4h': '4 hours',
'5h': '-5 hours', '5h': '5 hours',
'6h': '-6 hours', '6h': '6 hours',
'12h': '-12 hours', '12h': '12 hours',
'18h': '-18 hours', '18h': '18 hours',
'24h': '-24 hours', '24h': '24 hours',
'7d': '-7 days', '7d': '7 days',
'30d': '-30 days' '30d': '30 days'
} }
const timeOffset = periodMap[period] || '-1 hour' const interval = periodMap[period] || '1 hour'
const db = getDb() let containers: any[] = []
try {
const containers = db.prepare(` containers = await query(`
SELECT SELECT
container_id, container_id,
container_name, container_name,
container_status, container_status,
cpu_percent, cpu_percent,
memory_usage, memory_usage,
memory_limit, memory_limit,
memory_percent, memory_percent,
uptime, uptime,
network_rx, network_rx,
network_tx, network_tx,
collected_at collected_at
FROM server_containers FROM server_containers
WHERE target_id = ? WHERE target_id = $1
AND collected_at >= datetime('now', 'localtime', ?) AND collected_at >= NOW() - INTERVAL '${interval}'
ORDER BY collected_at ASC, container_name ASC ORDER BY collected_at ASC, container_name ASC
`).all(targetId, timeOffset) `, [targetId])
} catch (e) {
containers = []
}
return { return {
target_id: targetId, target_id: targetId,

View File

@@ -1,8 +1,8 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler((event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const targetId = query.target_id as string const targetId = queryParams.target_id as string
if (!targetId) { if (!targetId) {
throw createError({ throw createError({
@@ -11,19 +11,21 @@ export default defineEventHandler((event) => {
}) })
} }
const db = getDb() let disks: any[] = []
try {
// 최신 수집 시간 기준 디스크 목록 (물리 디스크만) disks = await query(`
const disks = db.prepare(` SELECT DISTINCT device_name, mount_point, fs_type, disk_total, disk_used, disk_percent
SELECT DISTINCT device_name, mount_point, fs_type, disk_total, disk_used, disk_percent FROM server_disks
FROM server_disks WHERE target_id = $1
WHERE target_id = ? AND collected_at = (SELECT MAX(collected_at) FROM server_disks WHERE target_id = $1)
AND collected_at = (SELECT MAX(collected_at) FROM server_disks WHERE target_id = ?) AND device_name NOT LIKE '%loop%'
AND device_name NOT LIKE '%loop%' AND mount_point NOT LIKE '%/snap%'
AND mount_point NOT LIKE '%/snap%' AND fs_type NOT IN ('tmpfs', 'squashfs', 'overlay')
AND fs_type NOT IN ('tmpfs', 'squashfs', 'overlay') ORDER BY mount_point ASC
ORDER BY mount_point ASC `, [targetId])
`).all(targetId, targetId) } catch (e) {
disks = []
}
return disks return disks
}) })

View File

@@ -1,9 +1,9 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler((event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const targetId = query.target_id as string const targetId = queryParams.target_id as string
const period = (query.period as string) || '1h' const period = (queryParams.period as string) || '1h'
if (!targetId) { if (!targetId) {
throw createError({ throw createError({
@@ -13,38 +13,41 @@ export default defineEventHandler((event) => {
} }
const periodMap: Record<string, string> = { const periodMap: Record<string, string> = {
'1h': '-1 hour', '1h': '1 hour',
'2h': '-2 hours', '2h': '2 hours',
'3h': '-3 hours', '3h': '3 hours',
'4h': '-4 hours', '4h': '4 hours',
'5h': '-5 hours', '5h': '5 hours',
'6h': '-6 hours', '6h': '6 hours',
'12h': '-12 hours', '12h': '12 hours',
'18h': '-18 hours', '18h': '18 hours',
'24h': '-24 hours', '24h': '24 hours',
'7d': '-7 days', '7d': '7 days',
'30d': '-30 days' '30d': '30 days'
} }
const timeOffset = periodMap[period] || '-1 hour' const interval = periodMap[period] || '1 hour'
const db = getDb() let disks: any[] = []
try {
const disks = db.prepare(` disks = await query(`
SELECT SELECT
disk_id, disk_id,
device_name, device_name,
mount_point, mount_point,
fs_type, fs_type,
disk_total, disk_total,
disk_used, disk_used,
disk_percent, disk_percent,
collected_at collected_at
FROM server_disks FROM server_disks
WHERE target_id = ? WHERE target_id = $1
AND collected_at >= datetime('now', 'localtime', ?) AND collected_at >= NOW() - INTERVAL '${interval}'
ORDER BY collected_at ASC, mount_point ASC ORDER BY collected_at ASC, mount_point ASC
`).all(targetId, timeOffset) `, [targetId])
} catch (e) {
disks = []
}
return { return {
target_id: targetId, target_id: targetId,

View File

@@ -1,8 +1,8 @@
import { getDb } from '../../../utils/db' import { queryOne } from '../../../utils/db'
export default defineEventHandler((event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const targetId = query.target_id as string const targetId = queryParams.target_id as string
if (!targetId) { if (!targetId) {
throw createError({ throw createError({
@@ -11,22 +11,17 @@ export default defineEventHandler((event) => {
}) })
} }
const db = getDb() const snapshot = await queryOne(`
// 최신 스냅샷
const snapshot = db.prepare(`
SELECT SELECT
s.*, l.*,
t.server_name, t.name as server_name,
t.server_ip, t.host as server_ip
t.glances_url, FROM server_logs l
t.collect_interval JOIN server_targets t ON l.target_id = t.target_id
FROM server_snapshots s WHERE l.target_id = $1
JOIN server_targets t ON s.target_id = t.target_id ORDER BY l.checked_at DESC
WHERE s.target_id = ?
ORDER BY s.collected_at DESC
LIMIT 1 LIMIT 1
`).get(targetId) `, [targetId])
return snapshot || null return snapshot || null
}) })

View File

@@ -1,9 +1,9 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler((event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const targetId = query.target_id as string const targetId = queryParams.target_id as string
const period = (query.period as string) || '1h' const period = (queryParams.period as string) || '1h'
if (!targetId) { if (!targetId) {
throw createError({ throw createError({
@@ -13,38 +13,41 @@ export default defineEventHandler((event) => {
} }
const periodMap: Record<string, string> = { const periodMap: Record<string, string> = {
'1h': '-1 hour', '1h': '1 hour',
'2h': '-2 hours', '2h': '2 hours',
'3h': '-3 hours', '3h': '3 hours',
'4h': '-4 hours', '4h': '4 hours',
'5h': '-5 hours', '5h': '5 hours',
'6h': '-6 hours', '6h': '6 hours',
'12h': '-12 hours', '12h': '12 hours',
'18h': '-18 hours', '18h': '18 hours',
'24h': '-24 hours', '24h': '24 hours',
'7d': '-7 days', '7d': '7 days',
'30d': '-30 days' '30d': '30 days'
} }
const timeOffset = periodMap[period] || '-1 hour' const interval = periodMap[period] || '1 hour'
const db = getDb() let networks: any[] = []
try {
const networks = db.prepare(` networks = await query(`
SELECT SELECT
network_id, network_id,
interface_name, interface_name,
bytes_recv, bytes_recv,
bytes_sent, bytes_sent,
speed_recv, speed_recv,
speed_sent, speed_sent,
is_up, is_up,
collected_at collected_at
FROM server_networks FROM server_networks
WHERE target_id = ? WHERE target_id = $1
AND collected_at >= datetime('now', 'localtime', ?) AND collected_at >= NOW() - INTERVAL '${interval}'
ORDER BY collected_at ASC, interface_name ASC ORDER BY collected_at ASC, interface_name ASC
`).all(targetId, timeOffset) `, [targetId])
} catch (e) {
networks = []
}
return { return {
target_id: targetId, target_id: targetId,

View File

@@ -1,9 +1,9 @@
import { getDb } from '../../../utils/db' import { query } from '../../../utils/db'
export default defineEventHandler((event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event) const queryParams = getQuery(event)
const targetId = query.target_id as string const targetId = queryParams.target_id as string
const period = (query.period as string) || '1h' const period = (queryParams.period as string) || '1h'
if (!targetId) { if (!targetId) {
throw createError({ throw createError({
@@ -12,44 +12,35 @@ export default defineEventHandler((event) => {
}) })
} }
// 기간별 시간 계산
const periodMap: Record<string, string> = { const periodMap: Record<string, string> = {
'1h': '-1 hour', '1h': '1 hour',
'2h': '-2 hours', '2h': '2 hours',
'3h': '-3 hours', '3h': '3 hours',
'4h': '-4 hours', '4h': '4 hours',
'5h': '-5 hours', '5h': '5 hours',
'6h': '-6 hours', '6h': '6 hours',
'12h': '-12 hours', '12h': '12 hours',
'18h': '-18 hours', '18h': '18 hours',
'24h': '-24 hours', '24h': '24 hours',
'7d': '-7 days', '7d': '7 days',
'30d': '-30 days' '30d': '30 days'
} }
const timeOffset = periodMap[period] || '-1 hour' const interval = periodMap[period] || '1 hour'
const db = getDb() const snapshots = await query(`
const snapshots = db.prepare(`
SELECT SELECT
snapshot_id, log_id,
cpu_percent, cpu_usage as cpu_percent,
cpu_temp, memory_usage as memory_percent,
load_percent, disk_usage as disk_percent,
memory_percent, is_success as is_online,
memory_used, checked_at as collected_at
memory_total, FROM server_logs
swap_percent, WHERE target_id = $1
swap_used, AND checked_at >= NOW() - INTERVAL '${interval}'
swap_total, ORDER BY checked_at ASC
is_online, `, [targetId])
collected_at
FROM server_snapshots
WHERE target_id = ?
AND collected_at >= datetime('now', 'localtime', ?)
ORDER BY collected_at ASC
`).all(targetId, timeOffset)
return { return {
target_id: targetId, target_id: targetId,

View File

@@ -1,15 +1,17 @@
import { getDb } from '../../utils/db' import { query } from '../../utils/db'
export default defineEventHandler(() => { export default defineEventHandler(async () => {
const db = getDb() let rows: any[] = []
try {
rows = await query(`
SELECT category, metric, warning, critical, danger, updated_at
FROM thresholds
ORDER BY category, metric
`)
} catch (e) {
rows = []
}
const rows = db.prepare(`
SELECT category, metric, warning, critical, danger, updated_at
FROM thresholds
ORDER BY category, metric
`).all() as any[]
// 카테고리별로 그룹화
const result: Record<string, Record<string, { warning: number; critical: number; danger: number }>> = {} const result: Record<string, Record<string, { warning: number; critical: number; danger: number }>> = {}
for (const row of rows) { for (const row of rows) {

View File

@@ -1,4 +1,4 @@
import { getDb } from '../../utils/db' import { execute } from '../../utils/db'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const body = await readBody(event) const body = await readBody(event)
@@ -10,15 +10,8 @@ export default defineEventHandler(async (event) => {
}) })
} }
const db = getDb()
const now = new Date().toLocaleString('sv-SE', { timeZone: 'Asia/Seoul' }).replace('T', ' ') const now = new Date().toLocaleString('sv-SE', { timeZone: 'Asia/Seoul' }).replace('T', ' ')
const stmt = db.prepare(`
UPDATE thresholds
SET warning = ?, critical = ?, danger = ?, updated_at = ?
WHERE category = ? AND metric = ?
`)
let updated = 0 let updated = 0
for (const [category, metrics] of Object.entries(body)) { for (const [category, metrics] of Object.entries(body)) {
@@ -29,7 +22,6 @@ export default defineEventHandler(async (event) => {
const { warning, critical, danger } = values const { warning, critical, danger } = values
// 유효성 검사
if (typeof warning !== 'number' || typeof critical !== 'number' || typeof danger !== 'number') { if (typeof warning !== 'number' || typeof critical !== 'number' || typeof danger !== 'number') {
continue continue
} }
@@ -45,8 +37,16 @@ export default defineEventHandler(async (event) => {
}) })
} }
const result = stmt.run(warning, critical, danger, now, category, metric) try {
if (result.changes > 0) updated++ const result = await execute(`
UPDATE thresholds
SET warning = $1, critical = $2, danger = $3, updated_at = $4
WHERE category = $5 AND metric = $6
`, [warning, critical, danger, now, category, metric])
if (result > 0) updated++
} catch (e) {
// 테이블이 없는 경우 무시
}
} }
} }