wing-ops/backend/src/audit/auditRouter.ts
2026-02-28 12:24:20 +09:00

75 lines
2.1 KiB
TypeScript

import { Router } from 'express'
import { requireAuth, requireRole } from '../auth/authMiddleware.js'
import { insertAuditLog, listAuditLogs } from './auditService.js'
const router = Router()
// POST /api/audit/log — sendBeacon 수신 (탭 이동 등 클라이언트 감사 로그)
router.post('/log', requireAuth, async (req, res) => {
try {
// sendBeacon은 Content-Type: text/plain으로 전송하므로 body를 직접 파싱
let body = req.body
if (typeof body === 'string') {
try {
body = JSON.parse(body)
} catch {
res.status(400).json({ error: '잘못된 요청 형식입니다.' })
return
}
}
const { action, detail } = body as { action?: string; detail?: string }
if (!action) {
res.status(400).json({ error: 'action 필드는 필수입니다.' })
return
}
const ipAddr = (req.headers['x-forwarded-for'] as string)?.split(',')[0]?.trim() || req.ip || ''
const userAgent = req.headers['user-agent'] || ''
await insertAuditLog({
userId: req.user!.sub,
actionCd: action,
actionDtl: detail,
ipAddr,
userAgent,
})
res.json({ success: true })
} catch (err) {
console.error('[audit] 감사 로그 기록 오류:', err)
res.status(500).json({ error: '감사 로그 기록 중 오류가 발생했습니다.' })
}
})
// GET /api/audit/logs — 관리자용 감사 로그 조회
router.get('/logs', requireAuth, requireRole('ADMIN'), async (req, res) => {
try {
const { page, size, userId, actionCd, from, to } = req.query as {
page?: string
size?: string
userId?: string
actionCd?: string
from?: string
to?: string
}
const result = await listAuditLogs({
page: page ? parseInt(page, 10) : undefined,
size: size ? parseInt(size, 10) : undefined,
userId,
actionCd,
from,
to,
})
res.json(result)
} catch (err) {
console.error('[audit] 감사 로그 조회 오류:', err)
res.status(500).json({ error: '감사 로그 조회 중 오류가 발생했습니다.' })
}
})
export default router