feat: blocklist table with registration check endpoint

- is_blocked BOOLEAN (default true), INET type for IP validation
- Indexes on email/username/phone/ip for fast registration checks
- POST /api/blocklist/check — checks all fields in one request, returns 403 if blocked
- Auto-timestamps on block/unblock, email stored lowercase

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 10:29:53 +02:00
parent 9eac7b47fc
commit 5f79e76b67
3 changed files with 141 additions and 0 deletions

View File

@@ -307,6 +307,34 @@ async function migrate() {
)
`);
// blocklist
await query(`
CREATE TABLE IF NOT EXISTS blocklist (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
is_blocked BOOLEAN NOT NULL DEFAULT true,
username TEXT,
email TEXT,
phone TEXT,
ip INET,
blocked_at TIMESTAMPTZ,
unblocked_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
await query(`
DROP TRIGGER IF EXISTS blocklist_updated_at ON blocklist;
CREATE TRIGGER blocklist_updated_at
BEFORE UPDATE ON blocklist
FOR EACH ROW EXECUTE FUNCTION update_updated_at()
`);
await query(`CREATE INDEX IF NOT EXISTS blocklist_email_idx ON blocklist (lower(email)) WHERE email IS NOT NULL`);
await query(`CREATE INDEX IF NOT EXISTS blocklist_username_idx ON blocklist (lower(username)) WHERE username IS NOT NULL`);
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`);
console.log('Migration complete');
}