- PATCH /api/v1/me: self-service profile update (name, phone, timezone) - User settings page with profile editor + notification preferences - Audit log API with filtering (entity, action, user, date range) - Audit log page with search, entity type, and action filters - Berth create/delete: POST /api/v1/berths + DELETE /api/v1/berths/[id] - Client duplicates endpoint: GET /api/v1/clients/duplicates?name= - Replace settings and audit stub pages with real implementations Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
58 lines
1.6 KiB
TypeScript
58 lines
1.6 KiB
TypeScript
import { and, eq, desc, sql, gte, lte } from 'drizzle-orm';
|
|
|
|
import { db } from '@/lib/db';
|
|
import { auditLogs } from '@/lib/db/schema';
|
|
|
|
interface AuditListQuery {
|
|
page: number;
|
|
limit: number;
|
|
entityType?: string;
|
|
action?: string;
|
|
userId?: string;
|
|
entityId?: string;
|
|
dateFrom?: string;
|
|
dateTo?: string;
|
|
search?: string;
|
|
}
|
|
|
|
export async function listAuditLogs(portId: string, query: AuditListQuery) {
|
|
const conditions = [eq(auditLogs.portId, portId)];
|
|
|
|
if (query.entityType) conditions.push(eq(auditLogs.entityType, query.entityType));
|
|
if (query.action) conditions.push(eq(auditLogs.action, query.action));
|
|
if (query.userId) conditions.push(eq(auditLogs.userId, query.userId));
|
|
if (query.entityId) conditions.push(eq(auditLogs.entityId, query.entityId));
|
|
if (query.dateFrom) conditions.push(gte(auditLogs.createdAt, new Date(query.dateFrom)));
|
|
if (query.dateTo) conditions.push(lte(auditLogs.createdAt, new Date(query.dateTo)));
|
|
if (query.search) {
|
|
conditions.push(
|
|
sql`(${auditLogs.entityType} ILIKE ${'%' + query.search + '%'} OR ${auditLogs.action} ILIKE ${'%' + query.search + '%'})`,
|
|
);
|
|
}
|
|
|
|
const offset = (query.page - 1) * query.limit;
|
|
|
|
const [data, countResult] = await Promise.all([
|
|
db
|
|
.select()
|
|
.from(auditLogs)
|
|
.where(and(...conditions))
|
|
.orderBy(desc(auditLogs.createdAt))
|
|
.limit(query.limit)
|
|
.offset(offset),
|
|
db
|
|
.select({ count: sql<number>`count(*)` })
|
|
.from(auditLogs)
|
|
.where(and(...conditions)),
|
|
]);
|
|
|
|
return {
|
|
data,
|
|
pagination: {
|
|
page: query.page,
|
|
limit: query.limit,
|
|
total: Number(countResult[0]?.count ?? 0),
|
|
},
|
|
};
|
|
}
|