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

139 lines
3.8 KiB
TypeScript

import { authPool } from '../db/authDb.js'
interface InsertAuditLogInput {
userId: string
actionCd: string
actionDtl?: string
httpMethod?: string
crudType?: string
reqUrl?: string
resStatus?: number
resSize?: number
ipAddr?: string
userAgent?: string
extra?: Record<string, unknown>
}
interface AuditLogItem {
logSn: number
userId: string
userName: string | null
userAccount: string | null
actionCd: string
actionDtl: string | null
httpMethod: string | null
crudType: string | null
reqUrl: string | null
reqDtm: string
resDtm: string | null
resStatus: number | null
resSize: number | null
ipAddr: string | null
userAgent: string | null
extra: Record<string, unknown> | null
}
interface AuditLogListResult {
items: AuditLogItem[]
total: number
page: number
size: number
}
export async function insertAuditLog(input: InsertAuditLogInput): Promise<void> {
await authPool.query(
`INSERT INTO AUTH_AUDIT_LOG
(USER_ID, ACTION_CD, ACTION_DTL, HTTP_METHOD, CRUD_TYPE, REQ_URL,
RES_STATUS, RES_SIZE, IP_ADDR, USER_AGENT, EXTRA)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
[
input.userId,
input.actionCd,
input.actionDtl || null,
input.httpMethod || null,
input.crudType || null,
input.reqUrl || null,
input.resStatus || null,
input.resSize || null,
input.ipAddr || null,
input.userAgent || null,
input.extra ? JSON.stringify(input.extra) : null,
]
)
}
export async function listAuditLogs(filters?: {
page?: number
size?: number
userId?: string
actionCd?: string
from?: string
to?: string
}): Promise<AuditLogListResult> {
const page = filters?.page || 1
const size = Math.min(filters?.size || 50, 200)
const offset = (page - 1) * size
let whereClause = 'WHERE 1=1'
const params: (string | number)[] = []
let paramIdx = 1
if (filters?.userId) {
whereClause += ` AND a.USER_ID = $${paramIdx++}`
params.push(filters.userId)
}
if (filters?.actionCd) {
whereClause += ` AND a.ACTION_CD = $${paramIdx++}`
params.push(filters.actionCd)
}
if (filters?.from) {
whereClause += ` AND a.REQ_DTM >= $${paramIdx++}`
params.push(filters.from)
}
if (filters?.to) {
whereClause += ` AND a.REQ_DTM <= $${paramIdx++}`
params.push(filters.to)
}
const countResult = await authPool.query(
`SELECT COUNT(*) as cnt FROM AUTH_AUDIT_LOG a ${whereClause}`,
params
)
const total = parseInt(countResult.rows[0].cnt, 10)
const dataParams = [...params, size, offset]
const result = await authPool.query(
`SELECT a.LOG_SN, a.USER_ID, u.USER_NM, u.USER_ACNT,
a.ACTION_CD, a.ACTION_DTL, a.HTTP_METHOD, a.CRUD_TYPE,
a.REQ_URL, a.REQ_DTM, a.RES_DTM, a.RES_STATUS, a.RES_SIZE,
a.IP_ADDR, a.USER_AGENT, a.EXTRA
FROM AUTH_AUDIT_LOG a
LEFT JOIN AUTH_USER u ON a.USER_ID = u.USER_ID
${whereClause}
ORDER BY a.REQ_DTM DESC
LIMIT $${paramIdx++} OFFSET $${paramIdx}`,
dataParams
)
const items: AuditLogItem[] = result.rows.map((row: Record<string, unknown>) => ({
logSn: row.log_sn as number,
userId: row.user_id as string,
userName: row.user_nm as string | null,
userAccount: row.user_acnt as string | null,
actionCd: row.action_cd as string,
actionDtl: row.action_dtl as string | null,
httpMethod: row.http_method as string | null,
crudType: row.crud_type as string | null,
reqUrl: row.req_url as string | null,
reqDtm: row.req_dtm as string,
resDtm: row.res_dtm as string | null,
resStatus: row.res_status as number | null,
resSize: row.res_size as number | null,
ipAddr: row.ip_addr as string | null,
userAgent: row.user_agent as string | null,
extra: row.extra as Record<string, unknown> | null,
}))
return { items, total, page, size }
}