91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
if (process.env.NODE_ENV !== "test")
|
|
require("server-only");
|
|
import getPool from "@/lib/server/db";
|
|
import type { GroupRole } from "@/lib/server/group-access";
|
|
import { requireGroupAdmin } from "@/lib/server/group-access";
|
|
|
|
export type GroupAuditEvent = {
|
|
groupId: number;
|
|
actorUserId: number | null;
|
|
actorRole: GroupRole | null;
|
|
eventType: string;
|
|
requestId: string;
|
|
ip?: string | null;
|
|
userAgent?: string | null;
|
|
success?: boolean;
|
|
errorCode?: string | null;
|
|
metadata?: Record<string, unknown>;
|
|
};
|
|
|
|
export async function recordGroupAudit(event: GroupAuditEvent) {
|
|
const pool = getPool();
|
|
await pool.query(
|
|
`insert into group_audit_log(
|
|
group_id,
|
|
actor_user_id,
|
|
actor_role,
|
|
event_type,
|
|
request_id,
|
|
ip,
|
|
user_agent,
|
|
success,
|
|
error_code,
|
|
metadata
|
|
) values($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`,
|
|
[
|
|
event.groupId,
|
|
event.actorUserId,
|
|
event.actorRole,
|
|
event.eventType,
|
|
event.requestId,
|
|
event.ip ?? null,
|
|
event.userAgent ?? null,
|
|
event.success ?? true,
|
|
event.errorCode ?? null,
|
|
event.metadata ?? {}
|
|
]
|
|
);
|
|
}
|
|
|
|
type GroupAuditRow = {
|
|
id: number;
|
|
group_id: number;
|
|
actor_user_id: number | null;
|
|
actor_role: GroupRole | null;
|
|
event_type: string;
|
|
request_id: string;
|
|
ip: string | null;
|
|
user_agent: string | null;
|
|
success: boolean;
|
|
error_code: string | null;
|
|
metadata: Record<string, unknown>;
|
|
created_at: string;
|
|
};
|
|
|
|
export async function listGroupAudit(input: { userId: number; groupId: number }) {
|
|
await requireGroupAdmin(input.userId, input.groupId);
|
|
const pool = getPool();
|
|
const { rows } = await pool.query(
|
|
`select id, group_id, actor_user_id, actor_role, event_type, request_id, ip, user_agent, success, error_code, metadata, created_at
|
|
from group_audit_log
|
|
where group_id=$1
|
|
order by created_at desc
|
|
limit 100`,
|
|
[input.groupId]
|
|
);
|
|
return rows.map((row: GroupAuditRow) => ({
|
|
id: Number(row.id),
|
|
groupId: Number(row.group_id),
|
|
actorUserId: row.actor_user_id === null ? null : Number(row.actor_user_id),
|
|
actorRole: row.actor_role,
|
|
eventType: row.event_type,
|
|
requestId: row.request_id,
|
|
ip: row.ip,
|
|
userAgent: row.user_agent,
|
|
success: Boolean(row.success),
|
|
errorCode: row.error_code,
|
|
metadata: row.metadata || {},
|
|
createdAt: row.created_at
|
|
}));
|
|
}
|