From 9291b063d0875594f9aab85cd205db03d29fc616 Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Thu, 26 Mar 2026 15:19:53 -0400 Subject: [PATCH] refactor: replace express server setup with platform server integration and streamline proxy endpoints --- server/server.js | 118 +++++++++++++---------------------------------- 1 file changed, 31 insertions(+), 87 deletions(-) diff --git a/server/server.js b/server/server.js index 4d08f2c..5f3907b 100644 --- a/server/server.js +++ b/server/server.js @@ -1,41 +1,27 @@ -import express from 'express'; import { WebSocketServer } from 'ws'; -import cors from 'cors'; -import { createServer } from 'http'; +import { createRequire } from 'module'; import * as db from './database.js'; import { Turtle } from './Turtle.js'; import { TaskDispatcher } from './TaskDispatcher.js'; import { WorldBlockCache } from './WorldBlockCache.js'; -const app = express(); -const PORT = 3001; +const require = createRequire(import.meta.url); +const { + createPlatformServer, + setupGracefulShutdown, + createProxyEndpoint, +} = require('@cc-platform/server'); -app.use(cors()); -app.use(express.json({ limit: '5mb' })); - -// ========== API Key Authentication ========== -const API_KEY = process.env.API_KEY || ''; - -function extractApiKey(req) { - const auth = req.headers.authorization || ''; - if (auth.startsWith('Bearer ')) return auth.slice(7); - return req.headers['x-api-key'] || req.query.key || ''; -} - -function requireAuth(req, res, next) { - if (!API_KEY) return next(); // Auth disabled when no key configured - if (extractApiKey(req) === API_KEY) return next(); - return res.status(401).json({ error: 'Unauthorized — invalid or missing API key' }); -} - -// Protect mutating endpoints when an API key is set -app.use((req, res, next) => { - if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') { - return next(); - } - return requireAuth(req, res, next); +// ========== Platform Server Setup ========== +const { app, server, auth, start, port } = createPlatformServer({ + serviceName: 'turtle-control', + port: 3001, + cors: true, }); +const { requireAuth } = auth; +const API_KEY = process.env.API_KEY || ''; + // Rewrite requests that arrive without /api prefix (from reverse proxy stripping it) app.use((req, res, next) => { if (!req.path.startsWith('/api') && req.path !== '/' && req.path !== '/health') { @@ -215,19 +201,12 @@ function getBlockPosition(turtlePos, facing, direction) { return pos; } -// Create HTTP server -const server = createServer(app); - // WebSocket server for web clients AND bridge connections const wss = new WebSocketServer({ server }); // Track bridge connections separately const bridgeClients = new Set(); -console.log(`šŸš€ Turtle Control Server starting...`); -console.log(`šŸ“” HTTP Server: http://localhost:${PORT}`); -console.log(`šŸ”Œ WebSocket Server: ws://localhost:${PORT}/ws`); - /** * Push a command to the webbridge for a specific turtle. * If a bridge is connected via WebSocket, send instantly. @@ -1880,8 +1859,6 @@ app.post('/api/turtle/:id/refresh-inventory', async (req, res) => { // ========== Cross-Project Integration API ========== // These endpoints allow the Inventory Manager system to query turtle state -const INVENTORY_SERVER_URL = process.env.INVENTORY_SERVER_URL || ''; // e.g. http://inventory-server:3001 - // Get all turtle summaries (for inventory dashboard sidebar widget) app.get('/api/integration/turtle-summary', (req, res) => { const summaries = []; @@ -1902,62 +1879,29 @@ app.get('/api/integration/turtle-summary', (req, res) => { }); // Proxy to inventory server (locate items for turtle pickup) -app.get('/api/integration/inventory-locate', async (req, res) => { - if (!INVENTORY_SERVER_URL) { - return res.json({ configured: false, message: 'INVENTORY_SERVER_URL not configured' }); - } - try { - const params = new URLSearchParams(req.query); - const resp = await fetch(`${INVENTORY_SERVER_URL}/api/integration/locate-item?${params}`); - const data = await resp.json(); - res.json({ configured: true, ...data }); - } catch (err) { - res.status(502).json({ configured: true, error: `Cannot reach inventory server: ${err.message}` }); - } -}); +createProxyEndpoint(app, '/api/integration/inventory-locate', 'INVENTORY_SERVER_URL', '/api/integration/locate-item'); // Proxy to inventory server (low stock alerts — turtles could auto-mine missing resources) -app.get('/api/integration/inventory-alerts', async (req, res) => { - if (!INVENTORY_SERVER_URL) { - return res.json({ configured: false, message: 'INVENTORY_SERVER_URL not configured' }); - } - try { - const resp = await fetch(`${INVENTORY_SERVER_URL}/api/integration/low-stock`); - const data = await resp.json(); - res.json({ configured: true, ...data }); - } catch (err) { - res.status(502).json({ configured: true, error: `Cannot reach inventory server: ${err.message}` }); - } -}); +createProxyEndpoint(app, '/api/integration/inventory-alerts', 'INVENTORY_SERVER_URL', '/api/integration/low-stock'); // Proxy to inventory server (storage space check — should turtles keep mining?) -app.get('/api/integration/storage-status', async (req, res) => { - if (!INVENTORY_SERVER_URL) { - return res.json({ configured: false, message: 'INVENTORY_SERVER_URL not configured' }); - } - try { - const resp = await fetch(`${INVENTORY_SERVER_URL}/api/integration/storage-status`); - const data = await resp.json(); - res.json({ configured: true, ...data }); - } catch (err) { - res.status(502).json({ configured: true, error: `Cannot reach inventory server: ${err.message}` }); - } +createProxyEndpoint(app, '/api/integration/storage-status', 'INVENTORY_SERVER_URL', '/api/integration/storage-status'); + +// ========== Graceful Shutdown & Start ========== + +setupGracefulShutdown({ + serviceName: 'turtle-control', + cleanup: [ + () => taskDispatcher.stop(), + () => db.closeDatabase(), + ], }); -// Graceful shutdown -process.on('SIGINT', () => { - console.log('\nšŸ›‘ Shutting down server...'); - taskDispatcher.stop(); - db.closeDatabase(); - process.exit(0); -}); - -server.listen(PORT, () => { - console.log(`āœ… Server ready!`); +start(() => { console.log(`\nConfigured turtles to send updates to:`); - console.log(` http://localhost:${PORT}/api/turtle/update`); - if (INVENTORY_SERVER_URL) { - console.log(`šŸ“¦ Inventory server integration: ${INVENTORY_SERVER_URL}`); + console.log(` http://localhost:${port}/api/turtle/update`); + if (process.env.INVENTORY_SERVER_URL) { + console.log(`šŸ“¦ Inventory server integration: ${process.env.INVENTORY_SERVER_URL}`); } // Start task dispatcher after server is ready taskDispatcher.start();