import { loadEnv } from 'vite'; import cookieSession from 'cookie-session'; import { readFileSync, existsSync } from 'fs'; import { join, extname } from 'path'; function parseBody(req) { return new Promise((resolve, reject) => { let body = ''; req.on('data', (chunk) => (body += chunk)); req.on('end', () => { try { resolve(JSON.parse(body)); } catch { reject(new Error('Invalid JSON')); } }); req.on('error', reject); }); } function buildInjectionScript(env) { const AUTH_ENABLED = (env.AUTH_ENABLED ?? 'false') !== 'false'; const APPWRITE_ENDPOINT = env.APPWRITE_ENDPOINT; const APPWRITE_PROJECT_ID = env.APPWRITE_PROJECT_ID; const POCKETBASE_URL = env.POCKETBASE_URL; const AUTH_GOOGLE_ENABLED = env.AUTH_GOOGLE_ENABLED; const AUTH_EMAIL_ENABLED = env.AUTH_EMAIL_ENABLED; const flags = []; if (AUTH_ENABLED) flags.push('window.__AUTH_GATE__=true'); const authProviderOverrides = {}; if (AUTH_GOOGLE_ENABLED !== undefined) { authProviderOverrides.google = AUTH_GOOGLE_ENABLED !== 'false'; } if (AUTH_EMAIL_ENABLED !== undefined) { authProviderOverrides.password = AUTH_EMAIL_ENABLED !== 'false'; } if (Object.keys(authProviderOverrides).length > 0) { flags.push(`window.__AUTH_PROVIDERS__=${JSON.stringify(authProviderOverrides)}`); } if (APPWRITE_ENDPOINT) flags.push(`window.__APPWRITE_ENDPOINT__=${JSON.stringify(APPWRITE_ENDPOINT)}`); if (APPWRITE_PROJECT_ID) flags.push(`window.__APPWRITE_PROJECT_ID__=${JSON.stringify(APPWRITE_PROJECT_ID)}`); if (POCKETBASE_URL) flags.push(`window.__POCKETBASE_URL__=${JSON.stringify(POCKETBASE_URL)}`); return flags.length > 0 ? `` : null; } export default function authGatePlugin() { let env = {}; return { name: 'auth-gate', config(_, { mode }) { env = loadEnv(mode, process.cwd(), ''); }, transformIndexHtml(html) { const scriptTag = buildInjectionScript(env); return scriptTag ? html.replace('', `${scriptTag}\n`) : html; }, configurePreviewServer(server) { const configScript = buildInjectionScript(env); // --- Pre-build injected HTML pages --- const distDir = join(process.cwd(), 'dist'); let indexHtml = null; const indexPath = join(distDir, 'index.html'); if (configScript && existsSync(indexPath)) { indexHtml = readFileSync(indexPath, 'utf-8'); indexHtml = indexHtml.replace('', `${configScript}\n`); } let loginHtml = null; const AUTH_ENABLED = (env.AUTH_ENABLED ?? 'false') !== 'false'; if (AUTH_ENABLED) { const loginPath = join(distDir, 'login.html'); if (existsSync(loginPath)) { loginHtml = readFileSync(loginPath, 'utf-8'); if (configScript) loginHtml = loginHtml.replace('', `${configScript}\n`); } } // --- /health (always available) --- server.middlewares.use((req, res, next) => { if (req.url.split('?')[0] === '/health') { res.end('OK'); return; } next(); }); // --- Auth gate (only when enabled) --- if (AUTH_ENABLED) { const AUTH_SECRET = env.AUTH_SECRET; const SESSION_MAX_AGE = Number(env.SESSION_MAX_AGE) || 7 * 24 * 60 * 60 * 1000; if (!AUTH_SECRET) { console.error('AUTH_SECRET is required when AUTH_ENABLED=true'); process.exit(1); } console.log(`Auth gate enabled (Project: ${env.APPWRITE_PROJECT_ID})`); server.middlewares.use( cookieSession({ name: 'mono_session', keys: [AUTH_SECRET], maxAge: SESSION_MAX_AGE, httpOnly: true, sameSite: 'lax', }) ); server.middlewares.use(async (req, res, next) => { const url = req.url.split('?')[0]; if (url === '/login' || url === '/login.html') { if (req.session && req.session.uid) { res.writeHead(302, { Location: '/' }); res.end(); return; } if (loginHtml) { res.setHeader('Content-Type', 'text/html'); res.setHeader('Cache-Control', 'no-store'); res.end(loginHtml); } else { res.statusCode = 404; res.end('Login page not found'); } return; } if (url === '/api/auth/login' && req.method === 'POST') { try { const body = await parseBody(req); if (!body.userId) { res.statusCode = 400; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ error: 'Missing userId' })); return; } req.session.uid = body.userId; req.session.email = body.email; req.session.iat = Date.now(); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ ok: true })); } catch (err) { console.error('Login session creation failed:', err.message); res.statusCode = 401; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ error: 'Login failed' })); } return; } if (url === '/api/auth/logout' && req.method === 'POST') { req.session = null; res.setHeader('Clear-Site-Data', '"cache", "storage"'); res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ ok: true })); return; } if (!req.session || !req.session.uid) { const ext = extname(url); if (ext && ext !== '.html') { res.statusCode = 401; res.end('Unauthorized'); } else { res.writeHead(302, { Location: '/login' }); res.end(); } return; } // Authenticated: serve injected index.html for HTML routes const ext = extname(url); if ((!ext || ext === '.html') && indexHtml) { res.setHeader('Content-Type', 'text/html'); res.setHeader('Cache-Control', 'no-store'); res.end(indexHtml); return; } next(); }); } else if (indexHtml) { // No auth gate, but env config needs injection into HTML server.middlewares.use((req, res, next) => { const url = req.url.split('?')[0]; const ext = extname(url); if (!ext || ext === '.html') { res.setHeader('Content-Type', 'text/html'); res.end(indexHtml); return; } next(); }); } }, }; }