task-b complate

This commit is contained in:
phaichayon
2026-06-11 16:55:45 +07:00
parent 7a390cf0df
commit 67545d9295
135 changed files with 3295 additions and 6385 deletions

View File

@@ -0,0 +1,98 @@
import { headers } from 'next/headers';
import { trAuditLogs } from '@/db/schema';
import { db } from '@/lib/db';
import { getCurrentUserContext } from '@/features/foundation/auth-context/service';
import type { AuditLogInput, AuditLogRecord } from './types';
async function resolveAuditContext(input: AuditLogInput) {
const context = await getCurrentUserContext();
const organizationId = input.organizationId ?? context?.activeOrganization?.id ?? null;
const userId = input.userId ?? context?.user.id ?? null;
if (!organizationId) {
throw new Error('organizationId is required for audit logging');
}
if (!userId) {
throw new Error('userId is required for audit logging');
}
return {
organizationId,
userId
};
}
async function resolveRequestId(inputRequestId?: string | null) {
if (inputRequestId) {
return inputRequestId;
}
try {
const requestHeaders = await headers();
return requestHeaders.get('x-request-id');
} catch {
return null;
}
}
function mapAuditRecord(row: typeof trAuditLogs.$inferSelect): AuditLogRecord {
return {
id: row.id,
organizationId: row.organizationId,
branchId: row.branchId,
userId: row.userId,
entityType: row.entityType,
entityId: row.entityId,
action: row.action,
beforeData: row.beforeData,
afterData: row.afterData,
requestId: row.requestId,
createdAt: row.createdAt.toISOString()
};
}
export async function auditAction(input: AuditLogInput) {
const { organizationId, userId } = await resolveAuditContext(input);
const requestId = await resolveRequestId(input.requestId);
const [created] = await db
.insert(trAuditLogs)
.values({
id: crypto.randomUUID(),
organizationId,
branchId: input.branchId ?? null,
userId,
entityType: input.entityType,
entityId: input.entityId,
action: input.action,
beforeData:
input.beforeData === undefined ? null : JSON.parse(JSON.stringify(input.beforeData)),
afterData: input.afterData === undefined ? null : JSON.parse(JSON.stringify(input.afterData)),
requestId
})
.returning();
return mapAuditRecord(created);
}
export async function auditCreate(input: Omit<AuditLogInput, 'action'>) {
return auditAction({
...input,
action: 'create'
});
}
export async function auditUpdate(input: Omit<AuditLogInput, 'action'>) {
return auditAction({
...input,
action: 'update'
});
}
export async function auditDelete(input: Omit<AuditLogInput, 'action'>) {
return auditAction({
...input,
action: 'delete'
});
}

View File

@@ -0,0 +1,25 @@
export interface AuditLogInput {
organizationId?: string;
branchId?: string | null;
userId?: string;
entityType: string;
entityId: string;
action: string;
beforeData?: unknown;
afterData?: unknown;
requestId?: string | null;
}
export interface AuditLogRecord {
id: string;
organizationId: string;
branchId: string | null;
userId: string;
entityType: string;
entityId: string;
action: string;
beforeData: unknown;
afterData: unknown;
requestId: string | null;
createdAt: string;
}