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:
1 parent
15b8a20751
commit
13733ac262
20 files changed
+1937
-46
No files matched your search
+20
-2
@@ -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));
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user