Add user settings, audit log, berth CRUD, and missing endpoints
- 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>
This commit is contained in:
57
src/lib/services/audit.service.ts
Normal file
57
src/lib/services/audit.service.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
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),
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user