AeThex-OS/server/routes.ts
sirpiglr 9901ea3e2d Add AI chatbot and theme toggle functionality for improved user experience
Integrates a new AI chatbot using OpenAI, adds a theme toggle for light/dark modes, and includes a live activity feed for administrators.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 279f1558-c0e3-40e4-8217-be7e9f4c6eca
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: c19480f5-d8f1-4a0b-98ea-7bfe9144b25d
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/b984cb14-1d19-4944-922b-bc79e821ed35/279f1558-c0e3-40e4-8217-be7e9f4c6eca/yVjwaR4
Replit-Helium-Checkpoint-Created: true
2025-12-16 00:00:42 +00:00

341 lines
9.8 KiB
TypeScript

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<Server> {
// ========== 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<number, { count: number; resetTime: number }>();
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;
}