feat: platform backbone and field capture, first part (checkpoint)
Platform backbone (plan 2026-10-13-plattform-rueckgrat), reviewed tasks: - run roles api/worker/job/all with shared startup checks (Task 1) - schema and migration for DomainEvent, deliveries, JobRun, ThrottlerHit (Task 2) - audit, notifications and domain events written in the same transaction (Task 3) - dispatcher with SKIP LOCKED claims, retries and stale release (Task 4) - worker building blocks: tick, LISTEN/NOTIFY wake-up, health (Task 5) - worker process and entry points (Task 6) - throttler counters in Postgres (Task 19) - separate keys per purpose with key ring (Task 20) - public origin page without exact location (Task 22) - shop keys split into read and write (Task 23) - Kustomize base, components, overlay encry, CI validation (Task 26) Field capture T3 (plan 2026-10-13-t3-erfassung-am-stand), reviewed tasks: - brood frames, read endpoints, apiary overview and offline pack - network basics, offline queue v3, navigation sheets, form building blocks - new inspection with last-time hints and weather, varroa per day, treatment prefill, apiary page with round - inspection photo (Task 10) is implemented, review pending Not rolled out. Remaining tasks follow in later commits. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
10cbe9bf2d
commit
c01a38835a
340 files changed
+25695
-1808
No files matched your search
+12
-45
@@ -1,55 +1,22 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { AppModule } from './app.module';
|
||||
import { configureApp } from './app.config';
|
||||
|
||||
const REQUIRED_ENV_VARS = ['DATABASE_URL', 'JWT_ACCESS_SECRET'] as const;
|
||||
// JWT_ACCESS_SECRET is the root of trust for both authentication (every
|
||||
// access/MFA-challenge/SSO-completion token is signed with it) and MFA
|
||||
// secret confidentiality (mfa-crypto.util.ts derives the AES key that
|
||||
// encrypts stored TOTP secrets from it) - a short or guessable value here
|
||||
// would let an attacker forge tokens for any user AND decrypt everyone's
|
||||
// stored TOTP secret, not just guess a password. 32 chars matches the
|
||||
// output of "openssl rand -hex 32" / "-base64 32", the standard way to
|
||||
// generate one.
|
||||
const MIN_JWT_SECRET_LENGTH = 32;
|
||||
|
||||
function assertProductionSettings(): void {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return;
|
||||
}
|
||||
// Without it CORS and SSO redirects silently fall back to localhost.
|
||||
if (!process.env.FRONTEND_ORIGIN?.startsWith('https://')) {
|
||||
throw new Error(
|
||||
'FRONTEND_ORIGIN must be set to an https:// origin in production',
|
||||
);
|
||||
}
|
||||
if (process.env.COOKIE_SECURE === 'false') {
|
||||
throw new Error('COOKIE_SECURE=false is for local HTTP development only');
|
||||
}
|
||||
}
|
||||
|
||||
function assertRequiredEnvVars(): void {
|
||||
const missing = REQUIRED_ENV_VARS.filter((name) => !process.env[name]);
|
||||
if (missing.length > 0) {
|
||||
throw new Error(
|
||||
`Missing required environment variable(s): ${missing.join(', ')}`,
|
||||
);
|
||||
}
|
||||
const jwtSecret = process.env.JWT_ACCESS_SECRET as string;
|
||||
if (jwtSecret.length < MIN_JWT_SECRET_LENGTH) {
|
||||
throw new Error(
|
||||
`JWT_ACCESS_SECRET must be at least ${MIN_JWT_SECRET_LENGTH} characters (got ${jwtSecret.length}) - generate one with e.g. "openssl rand -hex 32"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
import { resolveRole } from './runtime/role';
|
||||
import { isHttpRole, rootModuleFor } from './runtime/root-module';
|
||||
import { assertStartupEnvironment } from './runtime/startup-checks';
|
||||
|
||||
async function bootstrap() {
|
||||
assertRequiredEnvVars();
|
||||
assertProductionSettings();
|
||||
const role = resolveRole('main');
|
||||
// resolveRole('main') admits no other role; this only narrows the type.
|
||||
if (!isHttpRole(role)) {
|
||||
throw new Error(`APICORE_ROLE=${role} cannot start dist/main.js`);
|
||||
}
|
||||
assertStartupEnvironment(role);
|
||||
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
||||
const app = await NestFactory.create<NestExpressApplication>(
|
||||
rootModuleFor(role),
|
||||
);
|
||||
app.enableShutdownHooks();
|
||||
|
||||
configureApp(app);
|
||||
|
||||
Reference in new issue
Block a user