auth: sessions, login/logout/me/change-password, CSRF, guards, audit

argon2id passwords with a min length and a common-password list (SecLists,
MIT). Login failures are limited per IP and per email; must_change_password
blocks everything outside /api/auth/*. The first admin is created from env
on startup. pnpm dev derives DATABASE_URL from .env.

Task: M1-T3
This commit is contained in:
iamdoubz committed 2026-10-17 15:08:44 -05:00
1 parent 15b8a20751
commit 13733ac262
20 files changed
+1937 -46

No files matched your search

+20 -2
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// Startup order (04 §1).
import { buildApp } from './app.ts';
import { ConfigError, loadConfig } from './config.ts';
import { setSecretKey } from './crypto/secrets.ts';
import { createDb, runMigrations } from './db/client.ts';
import { ensureInitialAdmin } from './services/bootstrap.ts';
let config;
try {
@@ -10,10 +14,24 @@ try {
console.error(err.message);
process.exit(1);
}
setSecretKey(config.APP_SECRET);
const app = buildApp(config);
const { db, pool } = createDb(config.DATABASE_URL);
const app = buildApp(config, db); // creates the pino logger
await runMigrations(db);
app.log.info('database migrated');
if (
(await ensureInitialAdmin(db, config.INITIAL_ADMIN_EMAIL, config.INITIAL_ADMIN_PASSWORD)) ===
'created'
)
app.log.info({ email: config.INITIAL_ADMIN_EMAIL }, 'initial admin created');
await app.listen({ host: '127.0.0.1', port: config.API_PORT });
for (const sig of ['SIGTERM', 'SIGINT'] as const) {
process.once(sig, () => void app.close().then(() => process.exit(0)));
process.once(sig, () => {
void app
.close()
.then(() => pool.end())
.then(() => process.exit(0));
});
}