feat: registration and login with JWT auth

- users table: email, password_hash (bcrypt), role, is_active
- POST /auth/register — checks blocklist, hashes password, returns JWT
- POST /auth/login — verifies password, returns JWT
- Auth middleware: accepts env tokens (dev) OR valid JWTs
- end-user role → 403 Insufficient permissions on all /api/* routes
- JWT_SECRET + JWT_EXPIRES_IN env vars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 13:04:17 +02:00
parent 5f79e76b67
commit 217aab7dcd
6 changed files with 246 additions and 6 deletions

View File

@@ -335,6 +335,29 @@ async function migrate() {
await query(`CREATE INDEX IF NOT EXISTS blocklist_phone_idx ON blocklist (phone) WHERE phone IS NOT NULL`);
await query(`CREATE INDEX IF NOT EXISTS blocklist_ip_idx ON blocklist (ip) WHERE ip IS NOT NULL`);
// users
await query(`
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role VARCHAR(20) NOT NULL DEFAULT 'end-user'
CHECK (role IN ('end-user', 'admin')),
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
await query(`CREATE UNIQUE INDEX IF NOT EXISTS users_email_idx ON users (lower(email))`);
await query(`
DROP TRIGGER IF EXISTS users_updated_at ON users;
CREATE TRIGGER users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at()
`);
console.log('Migration complete');
}