import express, { type Express } from "express"; import cors from "cors"; import path from "path"; import { fileURLToPath } from "url"; import router from "./routes/index.js"; import bootstrapRouter from "./routes/bootstrap.js"; // New: Bootstrap routes import adminRelayPanelRouter from "./routes/admin-relay-panel.js"; import relayPolicyRouter from "./routes/relay-policy.js"; import { requestIdMiddleware } from "./middlewares/request-id.js"; import { responseTimeMiddleware } from "./middlewares/response-time.js"; const app: Express = express(); app.set("trust proxy", 1); // ── CORS (#5) ──────────────────────────────────────────────────────────────── // CORS_ORIGINS = comma-separated list of allowed origins. // Default in production: alexanderwhitestone.com (and www. variant). // Default in development: all origins permitted. const isProd = process.env["NODE_ENV"] === "production"; const rawOrigins = process.env["CORS_ORIGINS"]; const allowedOrigins: string[] = rawOrigins ? rawOrigins.split(",").map((o) => o.trim()).filter(Boolean) : isProd ? [ "https://alexanderwhitestone.com", "https://www.alexanderwhitestone.com", "https://alexanderwhitestone.ai", "https://www.alexanderwhitestone.ai", "https://hermes.tailb74b2d.ts.net", ] : []; app.use( cors({ origin: allowedOrigins.length === 0 ? true : (origin, callback) => { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error(`CORS: origin '${origin}' not allowed`)); } }, credentials: true, methods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization", "X-Session-Token", "X-Nostr-Token"], exposedHeaders: ["X-Session-Token", "X-Nostr-Token"], }), ); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(requestIdMiddleware); app.use(responseTimeMiddleware); app.use("/api", router); app.use("/api", bootstrapRouter); // New: Mount bootstrap routes app.use("/api", relayPolicyRouter); // ── Relay admin panel at /admin/relay ──────────────────────────────────────── // Served outside /api so the URL is clean: /admin/relay (not /api/admin/relay). app.use(adminRelayPanelRouter); // ── Tower (Matrix 3D frontend) ─────────────────────────────────────────────── // Serve the pre-built Three.js world at /tower. WS client auto-connects to // /api/ws on the same host. // // Path resolution strategy: // ESM dev (tsx ./src/index.ts): import.meta.url is a file:// URL; resolve 3 // levels up from src/ to reach the workspace root. // CJS prod bundle (node dist/index.cjs): import.meta is {} (empty), so fall // back to process.cwd(). The run command is issued from the workspace root, // so process.cwd() == workspace root. const towerDist = (() => { try { const metaUrl: string | undefined = (import.meta as { url?: string }).url; if (metaUrl && metaUrl.startsWith("file:")) { return path.resolve(path.dirname(fileURLToPath(metaUrl)), "../../..", "the-matrix", "dist"); } } catch {} // CJS bundle: run command is `node artifacts/api-server/dist/index.cjs` from workspace root return path.join(process.cwd(), "the-matrix", "dist"); })(); app.use("/tower", express.static(towerDist)); app.get("/tower/*splat", (req, res, next) => { // Never serve the SPA shell for requests that should hit the API or WS endpoint. // The *splat wildcard would otherwise swallow paths like /tower/api/ws and return // index.html, preventing the WebSocket upgrade from reaching the ws server. const splatArr = (req.params as Record)["splat"] ?? []; const sub = splatArr.join("/"); if (sub === "api" || sub.startsWith("api/")) return next(); res.sendFile(path.join(towerDist, "index.html")); }); // Vite builds asset references as absolute /assets/... paths. // Mirror them at the root so the browser can load them from /tower. app.use("/assets", express.static(path.join(towerDist, "assets"))); app.use("/sw.js", express.static(path.join(towerDist, "sw.js"))); app.use("/manifest.json", express.static(path.join(towerDist, "manifest.json"))); app.get("/", (_req, res) => { res.setHeader("Content-Type", "text/html"); res.send(` Alexander Whitestone

Alexander Whitestone

AI infrastructure & Lightning-native agents.

enter
`); }); app.get("/api", (_req, res) => res.redirect("/api/ui")); export default app;