import type { Express, Request, Response, NextFunction } from "express"; import { createServer, type Server } from "http"; import { storage } from "./storage"; import { loginSchema } from "@shared/schema"; import bcrypt from "bcrypt"; import crypto from "crypto"; import { getChatResponse } from "./openai"; // Extend session type declare module 'express-session' { interface SessionData { userId?: string; isAdmin?: boolean; token?: string; } } // Auth middleware - requires any authenticated user function requireAuth(req: Request, res: Response, next: NextFunction) { if (!req.session.userId) { return res.status(401).json({ error: "Unauthorized" }); } next(); } // Admin middleware - requires authenticated admin user function requireAdmin(req: Request, res: Response, next: NextFunction) { if (!req.session.userId) { return res.status(401).json({ error: "Unauthorized" }); } if (!req.session.isAdmin) { return res.status(403).json({ error: "Admin access required" }); } next(); } // Generate JWT-like token function generateToken(userId: string, username: string): string { const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString('base64url'); const payload = Buffer.from(JSON.stringify({ userId, username, iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 3600 // 1 hour })).toString('base64url'); const signature = crypto.createHmac('sha256', process.env.SESSION_SECRET || 'dev-secret') .update(`${header}.${payload}`) .digest('base64url'); return `${header}.${payload}.${signature}`; } export async function registerRoutes( httpServer: Server, app: Express ): Promise { // ========== AUTH ROUTES ========== // Login - creates JWT token and stores in sessions table app.post("/api/auth/login", async (req, res) => { try { const result = loginSchema.safeParse(req.body); if (!result.success) { return res.status(400).json({ error: "Invalid credentials" }); } const { username, password } = result.data; const user = await storage.getUserByUsername(username); if (!user) { return res.status(401).json({ error: "Invalid credentials" }); } const isValid = await bcrypt.compare(password, user.password); if (!isValid) { return res.status(401).json({ error: "Invalid credentials" }); } // Generate token like your other apps const token = generateToken(user.id, user.username); const expiresAt = new Date(Date.now() + 3600 * 1000); // 1 hour // Store session in sessions table (like your other apps) await storage.createSession({ user_id: user.id, username: user.username, token, expires_at: expiresAt.toISOString() }); // Also set express session for this app req.session.regenerate((err) => { if (err) { return res.status(500).json({ error: "Session error" }); } req.session.userId = user.id; req.session.isAdmin = user.is_admin ?? false; req.session.token = token; res.json({ success: true, token, user: { id: user.id, username: user.username, isAdmin: user.is_admin } }); }); } catch (err: any) { console.error('Login error:', err); res.status(500).json({ error: err.message }); } }); // Logout app.post("/api/auth/logout", (req, res) => { req.session.destroy((err) => { if (err) { return res.status(500).json({ error: "Logout failed" }); } res.clearCookie('connect.sid'); res.json({ success: true }); }); }); // Get current session app.get("/api/auth/session", async (req, res) => { if (!req.session.userId) { return res.json({ authenticated: false }); } const user = await storage.getUser(req.session.userId); if (!user) { return res.json({ authenticated: false }); } res.json({ authenticated: true, user: { id: user.id, username: user.username, isAdmin: user.is_admin } }); }); // ========== PUBLIC API ROUTES ========== // Get ecosystem metrics (public - for dashboard) app.get("/api/metrics", async (req, res) => { try { const metrics = await storage.getMetrics(); res.json(metrics); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // ========== ADMIN-PROTECTED API ROUTES ========== // Get all profiles (admin only) app.get("/api/profiles", requireAdmin, async (req, res) => { try { const profiles = await storage.getProfiles(); res.json(profiles); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Get single profile (admin only) app.get("/api/profiles/:id", requireAdmin, async (req, res) => { try { const profile = await storage.getProfile(req.params.id); if (!profile) { return res.status(404).json({ error: "Profile not found" }); } res.json(profile); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Update profile (admin only) app.patch("/api/profiles/:id", requireAdmin, async (req, res) => { try { const profile = await storage.updateProfile(req.params.id, req.body); if (!profile) { return res.status(404).json({ error: "Profile not found" }); } res.json(profile); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Get all projects (admin only) app.get("/api/projects", requireAdmin, async (req, res) => { try { const projects = await storage.getProjects(); res.json(projects); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Get single project (admin only) app.get("/api/projects/:id", requireAdmin, async (req, res) => { try { const project = await storage.getProject(req.params.id); if (!project) { return res.status(404).json({ error: "Project not found" }); } res.json(project); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // ========== NEW ADMIN ROUTES ========== // Get all aethex sites (admin only) app.get("/api/sites", requireAdmin, async (req, res) => { try { const sites = await storage.getSites(); res.json(sites); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Get auth logs (admin only) app.get("/api/auth-logs", requireAdmin, async (req, res) => { try { const logs = await storage.getAuthLogs(); res.json(logs); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Get achievements (admin only) app.get("/api/achievements", requireAdmin, async (req, res) => { try { const achievements = await storage.getAchievements(); res.json(achievements); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Get applications (admin only) app.get("/api/applications", requireAdmin, async (req, res) => { try { const applications = await storage.getApplications(); res.json(applications); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Get alerts for Aegis (admin only) app.get("/api/alerts", requireAdmin, async (req, res) => { try { const alerts = await storage.getAlerts(); res.json(alerts); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Resolve alert (admin only) app.patch("/api/alerts/:id", requireAdmin, async (req, res) => { try { const alert = await storage.updateAlert(req.params.id, req.body); if (!alert) { return res.status(404).json({ error: "Alert not found" }); } res.json(alert); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Update application status (admin only) app.patch("/api/applications/:id", requireAdmin, async (req, res) => { try { const application = await storage.updateApplication(req.params.id, req.body); if (!application) { return res.status(404).json({ error: "Application not found" }); } res.json(application); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // ========== CHATBOT API (Auth + Rate limited) ========== const chatRateLimits = new Map(); app.post("/api/chat", requireAuth, async (req, res) => { try { const userId = req.session?.userId; if (!userId) { return res.status(401).json({ error: "Authentication required" }); } const now = Date.now(); const rateLimit = chatRateLimits.get(userId); if (rateLimit) { if (now < rateLimit.resetTime) { if (rateLimit.count >= 30) { return res.status(429).json({ error: "Rate limit exceeded. Please wait before sending more messages." }); } rateLimit.count++; } else { chatRateLimits.set(userId, { count: 1, resetTime: now + 60000 }); } } else { chatRateLimits.set(userId, { count: 1, resetTime: now + 60000 }); } const { message, history } = req.body; if (!message || typeof message !== "string") { return res.status(400).json({ error: "Message is required" }); } const response = await getChatResponse(message, history); res.json({ response }); } catch (err: any) { console.error("Chat error:", err); res.status(500).json({ error: "Failed to get response" }); } }); return httpServer; }