Add auth system with invite-only registration and admin roles

JWT-based auth (hono/jwt + bcrypt), anonymous-first flow preserved.
Registration requires invite code when REQUIRE_INVITE=true. Admin
user seeded on startup (admin/admin, forced password reset). Login
accepts email or username. Admin invitations management page in
sidebar. Regular users get invite-a-friend button when USER_INVITATIONS > 0.
Frontend gate screen blocks game access for unregistered users with
invite code entry, registration, login, and password reset flows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 19:25:16 -04:00
parent df01ac8e35
commit 4881907c28
20 changed files with 1161 additions and 48 deletions
+15
View File
@@ -5,6 +5,13 @@ import { serve } from '@hono/node-server';
import { auth } from './routes/auth';
import { savesRouter } from './routes/saves';
import { leaderboardRouter } from './routes/leaderboard';
import { invitesRouter } from './routes/invites';
import { seedAdmin } from './db/seed';
if (!process.env.JWT_SECRET) {
console.error('FATAL: JWT_SECRET environment variable is required');
process.exit(1);
}
const app = new Hono();
@@ -19,12 +26,20 @@ app.use('*', cors({
app.get('/health', (c) => c.json({ status: 'ok', version: '0.1.0' }));
app.get('/api/config', (c) => c.json({
requireInvite: process.env.REQUIRE_INVITE !== 'false',
userInvitations: parseInt(process.env.USER_INVITATIONS || '0', 10),
}));
app.route('/api/auth', auth);
app.route('/api/saves', savesRouter);
app.route('/api/leaderboard', leaderboardRouter);
app.route('/api/invites', invitesRouter);
const port = Number(process.env.PORT) || 3001;
console.log(`AI Tycoon API server starting on port ${port}...`);
await seedAdmin();
serve({ fetch: app.fetch, port });