64 lines
3.4 KiB
JavaScript
64 lines
3.4 KiB
JavaScript
import http from 'node:http';
|
|
import { readFile } from 'node:fs/promises';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { resolve } from 'node:path';
|
|
import { telemetry, profiles } from './telemetry.mjs';
|
|
import { createServices } from './services.mjs';
|
|
|
|
const assets = new Map([
|
|
['/', ['index.html', 'text/html']], ['/index.html', ['index.html', 'text/html']],
|
|
['/app.js', ['app.js', 'text/javascript']], ['/styles.css', ['styles.css', 'text/css']],
|
|
...['journey.js', 'geo.js', 'theme.js', 'applications.js', 'cockpit.js', 'embeds.js', 'instruments.js', 'music.js'].map(name => [`/${name}`, [name, 'text/javascript']]),
|
|
['/cockpit.css', ['cockpit.css', 'text/css']],
|
|
['/applications.css', ['applications.css', 'text/css']],
|
|
['/journey.css', ['journey.css', 'text/css']],
|
|
['/vendor/leaflet/leaflet.js', ['vendor/leaflet/leaflet.js', 'text/javascript']],
|
|
['/vendor/leaflet/leaflet.css', ['vendor/leaflet/leaflet.css', 'text/css']]
|
|
]);
|
|
export const serviceRoutes = {
|
|
telemetry: ['/api/telemetry'],
|
|
weather: ['/api/weather'],
|
|
navigation: ['/api/map-config', '/api/search', '/api/route']
|
|
};
|
|
export function createServer(options = {}) {
|
|
const role = options.role || 'all';
|
|
if (role !== 'all' && !Object.hasOwn(serviceRoutes, role)) throw new Error(`Unknown service: ${role}`);
|
|
const services = createServices(options);
|
|
return http.createServer(async (req, res) => {
|
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
res.setHeader('Cache-Control', 'no-store');
|
|
const send = (status, value) => {
|
|
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
res.end(JSON.stringify(value));
|
|
};
|
|
if (req.method !== 'GET' && req.method !== 'HEAD') return send(405, { error: 'Lecture seule' });
|
|
const url = new URL(req.url, 'http://localhost');
|
|
if (url.pathname === '/api/health') return send(200, { status: 'ok', source: 'demo' });
|
|
if (role !== 'all' && !serviceRoutes[role].includes(url.pathname)) return send(404, { error: 'Introuvable' });
|
|
if (url.pathname === '/api/telemetry') {
|
|
const profile = url.searchParams.get('profile') || 'route';
|
|
return profiles.includes(profile) ? send(200, telemetry(profile)) : send(400, { error: 'Profil inconnu' });
|
|
}
|
|
if (url.pathname.startsWith('/api/')) {
|
|
try { return send(200, await services(url.pathname, url.searchParams)); }
|
|
catch (error) { return send(error.status || 502, { error: error.status ? error.message : 'Service indisponible' }); }
|
|
}
|
|
const asset = assets.get(url.pathname);
|
|
if (!asset) return send(404, { error: 'Introuvable' });
|
|
try {
|
|
const content = await readFile(new URL(`../ui/${asset[0]}`, import.meta.url));
|
|
res.writeHead(200, { 'Content-Type': `${asset[1]}; charset=utf-8` });
|
|
res.end(req.method === 'HEAD' ? undefined : content);
|
|
} catch { send(500, { error: 'Interface indisponible' }); }
|
|
});
|
|
}
|
|
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
const port = Number(process.env.PORT || 8080);
|
|
const server = createServer({ role: process.env.SERVICE_ROLE || 'all' });
|
|
server.listen(port, process.env.HOST || '0.0.0.0', () => console.log(`CARIA ${process.env.SERVICE_ROLE || 'development'} : http://localhost:${port}`));
|
|
for (const signal of ['SIGTERM', 'SIGINT']) process.once(signal, () => {
|
|
server.close(() => process.exit(0));
|
|
setTimeout(() => process.exit(1), 10000).unref();
|
|
});
|
|
}
|