mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-18 14:27:20 +00:00
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
341 lines
9.8 KiB
TypeScript
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;
|
|
}
|