import { db, securityEvents } from "@spinza/database"; import type { FastifyRequest } from "fastify"; export type SecurityEventType = | "login.success" | "login.failed" | "register" | "logout" | "password.changed" | "recovery.used" | "recovery.failed" | "rate_limit" | "spin.suspicious" | "admin.login.success" | "admin.login.failed" | "admin.wallet.adjust" | "admin.user.status" | "admin.game.lifecycle" | "admin.game.flags" | "admin.flag" | "admin.setting" | "admin.maintenance" | "csrf.rejected"; export function clientIp(req: FastifyRequest): string { const xff = req.headers["x-forwarded-for"]; if (typeof xff === "string" && xff.length) return xff.split(",").pop()!.trim(); return req.ip; } export async function logSecurity( req: FastifyRequest | null, type: SecurityEventType, opts: { userId?: string | null; adminId?: string | null; severity?: "info" | "warn" | "high"; meta?: Record } = {}, ): Promise { try { await db.insert(securityEvents).values({ userId: opts.userId ?? null, adminId: opts.adminId ?? null, type, severity: opts.severity ?? (type.includes("failed") || type === "rate_limit" || type === "csrf.rejected" ? "warn" : "info"), ip: req ? clientIp(req) : null, userAgent: req ? (req.headers["user-agent"] ?? "").slice(0, 300) : null, meta: opts.meta ?? null, }); } catch (e) { req?.log.error({ err: e }, "security event write failed"); } }