feat : add audit logs
CI / Verify (push) Successful in 1m28s
CI / Build immutable images and deploy (push) Successful in 2m40s

This commit is contained in:
2026-09-27 16:03:53 +07:00 Unverified
parent 094456ecba
commit 8ddb5896b6
18 changed files with 4027 additions and 115 deletions
+45 -20
View File
@@ -13,6 +13,7 @@ import {
glossaryAliasInputSchema,
normalizeGlossaryAlias,
} from "@/lib/glossary";
import { writeAuditLog, type AuditActor } from "@/lib/audit-log";
const catalogTables = {
character: catalogCharacters,
@@ -27,28 +28,52 @@ export async function listGlossaryAliases() {
);
}
export async function createGlossaryAlias(input: unknown) {
export async function createGlossaryAlias(input: unknown, actor: AuditActor) {
const data = glossaryAliasInputSchema.parse(input);
const table = catalogTables[data.kind];
const [item] = await getDb()
.select({ key: table.key })
.from(table)
.where(eq(table.key, data.catalogKey))
.limit(1);
if (!item) throw new Error("ไม่พบรายการใน Catalog");
const [created] = await getDb().insert(glossaryAliases).values({
...data,
alias: data.alias.trim(),
normalizedAlias: normalizeGlossaryAlias(data.alias),
}).returning();
return created;
return getDb().transaction(async (tx) => {
const [item] = await tx
.select({ key: table.key })
.from(table)
.where(eq(table.key, data.catalogKey))
.limit(1);
if (!item) throw new Error("ไม่พบรายการใน Catalog");
const [created] = await tx.insert(glossaryAliases).values({
...data,
alias: data.alias.trim(),
normalizedAlias: normalizeGlossaryAlias(data.alias),
}).returning();
await writeAuditLog(tx, actor, {
action: "glossary_alias.created",
targetType: "glossary_alias",
targetId: created.id,
metadata: {
alias: created.alias,
kind: created.kind,
catalogKey: created.catalogKey,
},
});
return created;
});
}
export async function deleteGlossaryAlias(id: string) {
const [deleted] = await getDb()
.delete(glossaryAliases)
.where(eq(glossaryAliases.id, id))
.returning();
if (!deleted) throw new Error("ไม่พบ Shortcut");
return deleted;
export async function deleteGlossaryAlias(id: string, actor: AuditActor) {
return getDb().transaction(async (tx) => {
const [deleted] = await tx
.delete(glossaryAliases)
.where(eq(glossaryAliases.id, id))
.returning();
if (!deleted) throw new Error("ไม่พบ Shortcut");
await writeAuditLog(tx, actor, {
action: "glossary_alias.deleted",
targetType: "glossary_alias",
targetId: deleted.id,
metadata: {
alias: deleted.alias,
kind: deleted.kind,
catalogKey: deleted.catalogKey,
},
});
return deleted;
});
}