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:
2026-05-02 01:37:20 +08:00
commit 014aca8619
162 changed files with 27116 additions and 0 deletions

49
server/src/main.ts Normal file
View File

@@ -0,0 +1,49 @@
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import { AppModule } from './app.module.js';
import { AuthService } from './auth/auth.service.js';
import { ApiExceptionFilter } from './common/api-exception.filter.js';
import { PrismaService } from './prisma/prisma.service.js';
import { PrismaSessionStore } from './session/prisma-session.store.js';
import { attachSpeechProxy } from './speech/speech.gateway.js';
import { SpeechService } from './speech/speech.service.js';
const bootstrap = async () => {
const app = await NestFactory.create(AppModule);
const port = Number(process.env.API_PORT ?? 3000);
app.setGlobalPrefix('api');
app.useGlobalFilters(new ApiExceptionFilter());
app.enableCors({
origin: process.env.CORS_ORIGIN?.split(',') ?? ['http://localhost:3001'],
credentials: true,
});
app.use(cookieParser());
const sessionMiddleware = session({
name: 'surclaw.sid',
secret: process.env.SESSION_SECRET ?? 'dev-only-session-secret-change-me',
store: new PrismaSessionStore(app.get(PrismaService)),
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
sameSite: 'lax',
secure: process.env.SESSION_COOKIE_SECURE === 'true',
maxAge: 1000 * 60 * 60 * 8,
},
});
app.use(sessionMiddleware);
await app.listen(port, '0.0.0.0');
attachSpeechProxy(
app.getHttpServer(),
sessionMiddleware,
app.get(AuthService),
app.get(SpeechService),
);
};
void bootstrap();