All checks were successful
CD / Build and push images (push) Successful in 1m52s
CI / Lint, typecheck, test (push) Successful in 1m34s
CI / Auth e2e pack (push) Successful in 1m44s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m7s
CD / Promote to Int (push) Successful in 10s
Prisma models `pages`/`page_updates`/`page_content_cache` per data-model.md. Endpoints: POST /ponds/:id/pages (title -> empty Yjs doc state, seeded via y-prosemirror), GET /pages/:id (meta + base64 state), PUT /pages/:id/state (client-encoded Yjs state, rejected above the 5 MiB operations.md limit or if it doesn't decode into a valid document for the schema), PATCH /pages/:id (title/slug — explicit slug changes validate uniqueness per pond, title-only renames keep the slug), DELETE (soft). Access follows InterimAccessService via the page's pond, same 404-not-403 interim rule as ponds. State saves decode the Yjs update with yjs + y-prosemirror and run it through the #24 shared derivation functions (docToPlainText/ docToMarkdown/docToHtml/extractOutline) to refresh page_content_cache. The Yjs XmlFragment name ("default") and the derivation call are factored so the collab server's persistence hooks (#35) can reuse both. Raised the API's JSON body limit to 8 MiB (main.ts and the e2e test app) to fit base64-encoded page state. Closes #23
49 lines
1.9 KiB
TypeScript
49 lines
1.9 KiB
TypeScript
import { execFileSync } from 'node:child_process';
|
|
|
|
import cookieParser from 'cookie-parser';
|
|
import { NestFactory } from '@nestjs/core';
|
|
import type { NestExpressApplication } from '@nestjs/platform-express';
|
|
import { apiEnvSchema, parseEnv } from '@dorfteich/shared';
|
|
import { Logger } from 'nestjs-pino';
|
|
|
|
import { AppModule } from './app.module';
|
|
import { AppConfig } from './config/app-config.service';
|
|
|
|
/**
|
|
* Apply pending migrations before the application accepts traffic, so
|
|
* self-hosters update by simply pulling a new image (ADR 0002). Prisma
|
|
* serializes concurrent deploys with a database advisory lock.
|
|
*/
|
|
function runMigrations(): void {
|
|
const prismaCli = require.resolve('prisma/build/index.js');
|
|
execFileSync(process.execPath, [prismaCli, 'migrate', 'deploy'], { stdio: 'inherit' });
|
|
}
|
|
|
|
async function bootstrap(): Promise<void> {
|
|
// Validate the environment before doing anything with it; this throws a
|
|
// readable list of problems and prevents a half-started process.
|
|
const env = parseEnv(apiEnvSchema, process.env);
|
|
if (env.MIGRATE_ON_START) {
|
|
runMigrations();
|
|
}
|
|
|
|
// bufferLogs holds early log lines until the pino logger is attached,
|
|
// so even bootstrap errors come out as structured JSON.
|
|
const app = await NestFactory.create<NestExpressApplication>(AppModule, { bufferLogs: true });
|
|
app.useLogger(app.get(Logger));
|
|
// One reverse-proxy hop (Caddy) in front of us: req.ip must reflect the
|
|
// real client for rate limiting and audit logs.
|
|
app.set('trust proxy', 1);
|
|
app.use(cookieParser());
|
|
// Base64-encoded Yjs page state (max 5 MiB, operations.md) inflates by
|
|
// ~4/3; 8 MiB leaves headroom for the JSON envelope around it.
|
|
app.useBodyParser('json', { limit: '8mb' });
|
|
app.setGlobalPrefix('api/v1');
|
|
app.enableShutdownHooks();
|
|
|
|
const config = app.get(AppConfig);
|
|
await app.listen(config.env.PORT);
|
|
}
|
|
|
|
void bootstrap();
|