Files
arbeitszeit/lib/db.ts
T
admin 4a3dfa5821 Passkey-Anmeldung (WebAuthn) zusätzlich zum Passwort
- Tabelle passkeys + lib/passkeys.ts (CRUD, Counter-Replay-Schutz)
- lib/webauthn.ts: RP-Config via RP_ID/RP_ORIGIN/RP_NAME, Challenge im
  httpOnly-Cookie, Wrapper um @simplewebauthn/server
- API: app/api/passkey/{register,authenticate,route}; authenticate ist
  öffentlich (proxy.ts ausgenommen) und setzt bei Erfolg die Session
- Login-Button "Mit Passkey anmelden", Verwaltung im Einstellungen-Tab
- Passwort bleibt als Fallback; Version 0.1.1

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 13:30:12 +02:00

60 lines
1.6 KiB
TypeScript

import Database from 'better-sqlite3';
import { mkdirSync } from 'fs';
import { dirname } from 'path';
let db: Database.Database | null = null;
function dbPath(): string {
return process.env.DB_PATH || './data/arbeitszeit.db';
}
/** Singleton-Zugriff auf die SQLite-Datenbank. Legt Datei + Schema beim ersten Aufruf an. */
export function getDb(): Database.Database {
if (db) return db;
const path = dbPath();
mkdirSync(dirname(path), { recursive: true });
db = new Database(path);
db.pragma('journal_mode = WAL');
db.exec(`
CREATE TABLE IF NOT EXISTS arbeitszeit (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datum TEXT NOT NULL,
ort TEXT NOT NULL,
beginn TEXT NOT NULL,
ende TEXT NOT NULL,
reisezeit REAL NOT NULL DEFAULT 0,
vertriebsunterstuetzung REAL NOT NULL DEFAULT 2,
kommentar TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_arbeitszeit_datum ON arbeitszeit (datum);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS passkeys (
credential_id TEXT PRIMARY KEY,
public_key BLOB NOT NULL,
counter INTEGER NOT NULL DEFAULT 0,
transports TEXT NOT NULL DEFAULT '[]',
label TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL DEFAULT (datetime('now')),
last_used_at TEXT
);
`);
// Standard-Pause beim ersten Start setzen (0,5 h)
db.prepare(
`INSERT INTO settings (key, value) VALUES ('pause', '0.5')
ON CONFLICT(key) DO NOTHING`
).run();
return db;
}