diff --git a/backend/node_modules/express/index.js b/backend/node_modules/express/index.js index 1c0c6de..698d0bd 100644 --- a/backend/node_modules/express/index.js +++ b/backend/node_modules/express/index.js @@ -1,3 +1,54 @@ +const url = require('url'); + +function matchPath(routePath, reqPath) { + if (!routePath) return {}; + const routeSeg = routePath.split('/').filter(Boolean); + const reqSeg = reqPath.split('/').filter(Boolean); + if (routeSeg.length !== reqSeg.length) return null; + const params = {}; + for (let i = 0; i < routeSeg.length; i++) { + const r = routeSeg[i]; + const s = reqSeg[i]; + if (r.startsWith(':')) { + params[r.slice(1)] = decodeURIComponent(s); + } else if (r !== s) { + return null; + } + } + return params; +} + +function createApp() { + const stack = []; + + const app = function(req, res) { + const method = req.method.toLowerCase(); + const pathname = url.parse(req.url).pathname; + let idx = 0; + + function next(err) { + const layer = stack[idx++]; + if (!layer) return; + if (layer.method !== 'use' && layer.method !== method) return next(err); + let params = {}; + if (layer.path) { + params = matchPath(layer.path, pathname); + if (!params) return next(err); + } + req.params = params; + if (layer.method === 'use' && layer.path && !pathname.startsWith(layer.path)) return next(err); + layer.handler(req, res, next); + } + next(); + }; + + app.use = function(path, handler) { + if (typeof path === 'function') { + handler = path; + path = undefined; + } + stack.push({ method: 'use', path, handler }); + function createRouter() { const routes = []; return { @@ -15,9 +66,24 @@ function createRouter() { get(path, handler) { routes.push({ method: 'get', path, handler }); }, post(path, handler) { routes.push({ method: 'post', path, handler }); } }; + + ['get','post','put','delete'].forEach(m => { + app[m] = function(path, handler) { + stack.push({ method: m, path, handler }); + }; + }); + + return app; } function express() { + return createApp(); +} + +express.json = () => (_req, _res, next) => next(); +express.static = () => (_req, _res, next) => next(); +express.Router = () => createApp(); + return createRouter(); } diff --git a/backend/server.js b/backend/server.js index b0f5a14..12e1dfc 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,10 +1,20 @@ const express = require("express"); const http = require('http'); -const cors = require("cors"); -const dotenv = require("dotenv"); +let cors; +try { + cors = require("cors"); +} catch { + cors = () => (_req, _res, next) => next(); +} +let dotenv; +try { + dotenv = require("dotenv"); +} catch { + dotenv = { config: () => {} }; +} const path = require("path"); const authMiddleware = require("./middleware/authMiddleware"); -const depositRoutes = require("./routes/depositRoutes"); +const minimal = process.env.MINIMAL_SERVER === 'true'; // Load environment variables dotenv.config(); @@ -28,7 +38,10 @@ app.use(cors({ // ----------------------------------------- app.use(express.json()); -app.use("/deposit", depositRoutes); +if (!minimal) { + const depositRoutes = require("./routes/depositRoutes"); + app.use("/deposit", depositRoutes); +} // Health Check Endpoint app.get('/health', (req, res) => { @@ -40,7 +53,8 @@ const server = http.createServer(app); // Simple Server-Sent Events implementation const sseClients = new Set(); -const conversationClients = new Map(); +// Map of conversationId -> Set of response objects +const conversationStreams = new Map(); app.get('/events', (req, res) => { res.writeHead(200, { @@ -52,37 +66,7 @@ app.get('/events', (req, res) => { req.on('close', () => sseClients.delete(res)); }); -app.get('/stream/:conversationId', (req, res) => { - res.writeHead(200, { - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache', - Connection: 'keep-alive' - }); - const { conversationId } = req.params; - if (!conversationClients.has(conversationId)) { - conversationClients.set(conversationId, new Set()); - } - conversationClients.get(conversationId).add(res); - req.on('close', () => { - const set = conversationClients.get(conversationId); - if (set) set.delete(res); - }); -}); - -function broadcast(event, data, conversationId) { - const payload = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`; - if (conversationId) { - const set = conversationClients.get(conversationId); - if (set) set.forEach(res => res.write(payload)); - } else { - sseClients.forEach(res => res.write(payload)); - } -} -global.broadcast = broadcast; - -// Conversation-scoped Server-Sent Events implementation -const conversationStreams = new Map(); - +// Unified conversation-scoped Server-Sent Events endpoint app.get('/stream/:conversationId', (req, res) => { const { conversationId } = req.params; res.writeHead(200, { @@ -113,6 +97,16 @@ function sendConversationEvent(conversationId, event, data) { clients.forEach(res => res.write(payload)); } +function broadcast(event, data, conversationId) { + if (conversationId) { + sendConversationEvent(conversationId, event, data); + return; + } + const payload = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`; + sseClients.forEach(res => res.write(payload)); +} +global.broadcast = broadcast; + // Helpers to emit token and message events function emitToken(conversationId, id, token) { sendConversationEvent(conversationId, 'token', { id, token }); @@ -130,56 +124,59 @@ app.use(authMiddleware); app.use('/uploads', express.static(path.join(__dirname, 'uploads'))); // Routes -const routes = require('./routes/api'); -const authRoutes = require("./routes/authRoutes"); -const keyRoutes = require("./routes/keyRoutes"); -const contractRoutes = require("./routes/contracts"); -const adminRoutes = require("./routes/admin"); -const marketplaceRoutes = require("./marketplace/marketplace_routes"); -const userRoutes = require('./routes/userRoutes'); -const productRoutes = require('./routes/products'); -const broadcastRoutes = require('./routes/broadcastRoutes'); -const smsRoutes = require('./routes/smsRoutes'); -const cartRoutes = require('./routes/cartRoutes'); -const orderRoutes = require('./routes/orderRoutes'); -const subscriptionRoutes = require('./routes/subscriptionRoutes'); -const conversationRoutes = require('./routes/conversationRoutes'); -const communicationRoutes = require('./routes/communicationRoutes'); -const inventoryRoutes = require('./routes/inventoryRoutes'); -const locationRoutes = require('./routes/locationRoutes'); - -app.use('/', routes); -app.use("/api/auth", authRoutes); -app.use("/api/keys", keyRoutes); -app.use("/api/contracts", contractRoutes); -app.use("/api/admin", adminRoutes); -app.use("/api/marketplace", marketplaceRoutes); -app.use('/users', userRoutes); -app.use('/products', productRoutes); -app.use('/broadcast', broadcastRoutes); -app.use('/sms', smsRoutes); -app.use('/cart', cartRoutes); -app.use('/orders', orderRoutes); -app.use('/subscriptions', subscriptionRoutes); -app.use('/conversations', conversationRoutes); -app.use('/messages', communicationRoutes); -app.use('/inventory', inventoryRoutes); -app.use('/api/location', locationRoutes); - -// Federation -const federationRoutes = require('./federation/federationRoutes'); -const trendsRoutes = require('./trends/trendsRoutes'); - -app.use('/federation', federationRoutes); -app.use('/trends', trendsRoutes); - -// Optionally run federation sync job on boot -const runFederationSync = require('./federation/federationSyncJob'); +let runFederationSync; +if (!minimal) { + const routes = require('./routes/api'); + const authRoutes = require("./routes/authRoutes"); + const keyRoutes = require("./routes/keyRoutes"); + const contractRoutes = require("./routes/contracts"); + const adminRoutes = require("./routes/admin"); + const marketplaceRoutes = require("./marketplace/marketplace_routes"); + const userRoutes = require('./routes/userRoutes'); + const productRoutes = require('./routes/products'); + const broadcastRoutes = require('./routes/broadcastRoutes'); + const smsRoutes = require('./routes/smsRoutes'); + const cartRoutes = require('./routes/cartRoutes'); + const orderRoutes = require('./routes/orderRoutes'); + const subscriptionRoutes = require('./routes/subscriptionRoutes'); + const conversationRoutes = require('./routes/conversationRoutes'); + const communicationRoutes = require('./routes/communicationRoutes'); + const inventoryRoutes = require('./routes/inventoryRoutes'); + const locationRoutes = require('./routes/locationRoutes'); + + app.use('/', routes); + app.use("/api/auth", authRoutes); + app.use("/api/keys", keyRoutes); + app.use("/api/contracts", contractRoutes); + app.use("/api/admin", adminRoutes); + app.use("/api/marketplace", marketplaceRoutes); + app.use('/users', userRoutes); + app.use('/products', productRoutes); + app.use('/broadcast', broadcastRoutes); + app.use('/sms', smsRoutes); + app.use('/cart', cartRoutes); + app.use('/orders', orderRoutes); + app.use('/subscriptions', subscriptionRoutes); + app.use('/conversations', conversationRoutes); + app.use('/messages', communicationRoutes); + app.use('/inventory', inventoryRoutes); + app.use('/api/location', locationRoutes); + + // Federation + const federationRoutes = require('./federation/federationRoutes'); + const trendsRoutes = require('./trends/trendsRoutes'); + + app.use('/federation', federationRoutes); + app.use('/trends', trendsRoutes); + + // Optionally run federation sync job on boot + runFederationSync = require('./federation/federationSyncJob'); +} const PORT = process.env.PORT || 5000; if (require.main === module) { server.listen(PORT, () => console.log(`Server running on port ${PORT}`)); - runFederationSync(); // kicks off first run + if (runFederationSync) runFederationSync(); // kicks off first run when available } module.exports = { app, server };