Files
config-panel2/mock-server.js

111 lines
4.4 KiB
JavaScript

// 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}`);
});