AeThex-OS/server/index.ts
sirpiglr 7f03ac1bb9 Add a virtual desktop environment with app management
Adds the AeThexOS virtual desktop page, including window management, app launching, and basic system utilities, along with updates to the protected route and authentication context.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 279f1558-c0e3-40e4-8217-be7e9f4c6eca
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 9aeffd21-c394-4a5b-a2cb-b0ba603639c1
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/b984cb14-1d19-4944-922b-bc79e821ed35/279f1558-c0e3-40e4-8217-be7e9f4c6eca/ogW6F7k
Replit-Helium-Checkpoint-Created: true
2025-12-16 06:30:51 +00:00

120 lines
3.2 KiB
TypeScript

import express, { type Request, Response, NextFunction } from "express";
import session from "express-session";
import { registerRoutes } from "./routes";
import { serveStatic } from "./static";
import { createServer } from "http";
const app = express();
const httpServer = createServer(app);
declare module "http" {
interface IncomingMessage {
rawBody: unknown;
}
}
// Require session secret in production
const sessionSecret = process.env.SESSION_SECRET;
if (process.env.NODE_ENV === "production" && !sessionSecret) {
throw new Error("SESSION_SECRET environment variable is required in production");
}
// Session configuration with security best practices
app.use(
session({
secret: sessionSecret || "dev-only-secret-not-for-prod",
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === "production",
httpOnly: true,
sameSite: "lax", // Allow navigation from external links
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
},
})
);
app.use(
express.json({
verify: (req, _res, buf) => {
req.rawBody = buf;
},
}),
);
app.use(express.urlencoded({ extended: false }));
export function log(message: string, source = "express") {
const formattedTime = new Date().toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: true,
});
console.log(`${formattedTime} [${source}] ${message}`);
}
app.use((req, res, next) => {
const start = Date.now();
const path = req.path;
let capturedJsonResponse: Record<string, any> | undefined = undefined;
const originalResJson = res.json;
res.json = function (bodyJson, ...args) {
capturedJsonResponse = bodyJson;
return originalResJson.apply(res, [bodyJson, ...args]);
};
res.on("finish", () => {
const duration = Date.now() - start;
if (path.startsWith("/api")) {
let logLine = `${req.method} ${path} ${res.statusCode} in ${duration}ms`;
if (capturedJsonResponse) {
logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
}
log(logLine);
}
});
next();
});
(async () => {
await registerRoutes(httpServer, app);
app.use((err: any, _req: Request, res: Response, _next: NextFunction) => {
const status = err.status || err.statusCode || 500;
const message = err.message || "Internal Server Error";
res.status(status).json({ message });
throw err;
});
// importantly only setup vite in development and after
// setting up all the other routes so the catch-all route
// doesn't interfere with the other routes
if (process.env.NODE_ENV === "production") {
serveStatic(app);
} else {
const { setupVite } = await import("./vite");
await setupVite(httpServer, app);
}
// ALWAYS serve the app on the port specified in the environment variable PORT
// Other ports are firewalled. Default to 5000 if not specified.
// this serves both the API and the client.
// It is the only port that is not firewalled.
const port = parseInt(process.env.PORT || "5000", 10);
httpServer.listen(
{
port,
host: "0.0.0.0",
reusePort: true,
},
() => {
log(`serving on port ${port}`);
},
);
})();