// Mock backend server (port 3001) - matches /config API from api.rest import { createServer } from 'node:http'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); // In-memory mock data store const store = { lan: { dhcp: false, ip: '192.168.6.51', mask: '255.255.255.0', gateway: '192.168.6.1', dns: '8.8.8.8' }, main_relay: { lockDelayMs: 5000, unlockDelayMs: 2000 }, elevator: { enabled: true, timeoutMs: 8000, portHintList: [], portCount: 0 }, device: { type: 0, sn: 1, name: 'test', unit: 1, room: 101 }, }; const server = createServer((req, res) => { const url = new URL(req.url, `http://${req.headers.host}`); const pathname = url.pathname; // e.g. /config/lan const apiKey = req.headers['x-api-key'] || ''; // CORS res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-API-Key'); if (req.method === 'OPTIONS') { res.writeHead(204); return res.end(); } // Auth check const auth = req.headers['authorization'] || ''; if (auth && !auth.startsWith('Basic ') && !auth.startsWith('Bearer ')) { res.writeHead(401, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ err: 'Authorization header must start with Basic or Bearer' })); } if (!pathname.startsWith('/config/')) { res.writeHead(404, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ err: 'Not found' })); } const resource = pathname.replace(/^\/config\//, ''); const routeKey = ['lan', 'main_relay', 'elevator', 'device']; if (!routeKey.includes(resource)) { res.writeHead(404, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ err: `Unknown resource: ${resource}` })); } const timeout = setTimeout(() => { if (!res.headersSent) { res.writeHead(504, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ err: 'Gateway timeout' })); } }, 3000); if (req.method === 'GET') { // Simulate latency: 100-400ms const delay = Math.random() * 300 + 100; setTimeout(() => { clearTimeout(timeout); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(store[resource])); }, delay); } else if (req.method === 'PUT') { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { const delay = Math.random() * 300 + 100; setTimeout(() => { try { const data = JSON.parse(body); // Validate based on resource type if (resource === 'lan') { if (!data.ip || !data.mask || !data.gateway) { throw new Error('request error: missing required fields'); } store[resource] = { ...store[resource], ...data }; } else if (resource === 'main_relay') { if (typeof data.lockDelayMs !== 'number' || typeof data.unlockDelayMs !== 'number') { throw new Error('request error: invalid delay values'); } store[resource] = { ...store[resource], ...data }; } else if (resource === 'elevator') { if (!data.enabled) { throw new Error('request error: disabled not allowed'); } store[resource] = { ...store[resource], ...data, portHintList: [], portCount: Object.keys(data).filter(k => k.startsWith('port')).length }; } else if (resource === 'device') { if (!data.name) { throw new Error('request error: name required'); } store[resource] = { ...store[resource], ...data, sn: Date.now() % 10000 }; } res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(store[resource])); } catch (e) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ err: e.message })); } }, delay); }); } else { clearTimeout(timeout); res.writeHead(405, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ err: `Method ${req.method} not allowed` })); } }); const PORT = 3001; server.listen(PORT, () => { console.log(`Mock server running at http://localhost:${PORT}/config/{lan,main_relay,elevator,device}`); });