Initialize backendized SurClaw report system
- Add React/Vite frontend for login, dashboard, reports, templates, users, settings, AI, speech, and media workflows. - Add NestJS/Prisma/PostgreSQL backend with auth, dashboard stats, reports, templates, users, departments, settings, files, AI, speech, audit logs, and HTML sanitization. - Add Prisma schema, migrations, seed data, persistent app sessions, Docker/Nginx deployment files, and upload volume configuration. - Add Vitest, Playwright, backend integration tests, and project documentation for requirements, design, permissions, API contracts, testing, deployment, security, and progress. - Configure production local fallback switch and remove unused Gemini direct dependency/env wiring.
This commit is contained in:
11
server/src/audit/audit.module.ts
Normal file
11
server/src/audit/audit.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { PrismaModule } from '../prisma/prisma.module.js';
|
||||
import { AuditService } from './audit.service.js';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [AuditService],
|
||||
exports: [AuditService],
|
||||
})
|
||||
export class AuditModule {}
|
||||
46
server/src/audit/audit.service.ts
Normal file
46
server/src/audit/audit.service.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import type { Prisma } from '@prisma/client';
|
||||
import type { SafeUser } from '../auth/auth.types.js';
|
||||
import { PrismaService } from '../prisma/prisma.service.js';
|
||||
|
||||
export interface AuditInput {
|
||||
actor?: SafeUser | null;
|
||||
action: string;
|
||||
targetType: string;
|
||||
targetId?: string | null;
|
||||
departmentId?: string | null;
|
||||
metadata?: Record<string, unknown>;
|
||||
ip?: string | null;
|
||||
userAgent?: string | null;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AuditService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async record(input: AuditInput) {
|
||||
const actor = input.actor || null;
|
||||
const tenantId = actor?.tenantId;
|
||||
if (!tenantId) return;
|
||||
|
||||
await this.prisma.auditLog.create({
|
||||
data: {
|
||||
tenantId,
|
||||
actorUserId: actor.id,
|
||||
actorRole: actor.role,
|
||||
action: input.action,
|
||||
targetType: input.targetType,
|
||||
targetId: input.targetId || null,
|
||||
departmentId: input.departmentId || actor.departmentId || null,
|
||||
ip: input.ip || null,
|
||||
userAgent: input.userAgent || null,
|
||||
metadata: input.metadata ? (cleanJson(input.metadata) as Prisma.InputJsonValue) : undefined,
|
||||
},
|
||||
}).catch(() => {
|
||||
// Auditing must not break the clinical workflow. Operational alerts can watch failed inserts.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const cleanJson = (value: Record<string, unknown>) =>
|
||||
JSON.parse(JSON.stringify(value)) as Record<string, unknown>;
|
||||
Reference in New Issue
Block a user