AeThex-OS/server/routes.ts
sirpiglr 8ee5f71ef4 Add login, admin panel, and user management features
Introduces authentication via JWT, session management with CSRF protection, and new admin routes for managing users, projects, and monitoring security. Enhances dashboard and home pages with dynamic metrics fetched from the backend.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 279f1558-c0e3-40e4-8217-be7e9f4c6eca
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: dcd55177-c240-4288-8fc0-652032c758f2
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/b984cb14-1d19-4944-922b-bc79e821ed35/279f1558-c0e3-40e4-8217-be7e9f4c6eca/2riq6Ir
Replit-Helium-Checkpoint-Created: true
2025-12-15 22:15:36 +00:00

190 lines
5.1 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";
// Extend session type
declare module 'express-session' {
interface SessionData {
userId?: string;
isAdmin?: boolean;
}
}
// 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();
}
export async function registerRoutes(
httpServer: Server,
app: Express
): Promise<Server> {
// ========== AUTH ROUTES ==========
// Login
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" });
}
// Regenerate session on login to prevent session fixation
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;
res.json({
success: true,
user: {
id: user.id,
username: user.username,
isAdmin: user.is_admin
}
});
});
} catch (err: any) {
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 });
}
});
return httpServer;
}