serve-review.mjs 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. // Local-only review server. Serves model evidence and proxies the existing app's real engine/GIS assets.
  2. // Nothing from this tool is copied into the production web build.
  3. import http from 'node:http'
  4. import { createReadStream, existsSync, statSync } from 'node:fs'
  5. import { dirname, resolve, extname, sep } from 'node:path'
  6. import { fileURLToPath } from 'node:url'
  7. const root = dirname(fileURLToPath(import.meta.url))
  8. const upstream = new URL(process.env.COCKPIT_BASE || 'http://127.0.0.1:8001')
  9. const port = Number(process.env.MODEL_REVIEW_PORT || 8092)
  10. const mime = {
  11. '.html': 'text/html; charset=utf-8',
  12. '.json': 'application/json',
  13. '.glb': 'model/gltf-binary',
  14. '.png': 'image/png',
  15. }
  16. const server = http.createServer((req, res) => {
  17. const url = new URL(req.url, 'http://localhost')
  18. if (url.pathname === '/' || url.pathname === '/preview.html' || url.pathname.startsWith('/review-assets/')) {
  19. const relative = url.pathname.startsWith('/review-assets/')
  20. ? decodeURIComponent(url.pathname.slice(15))
  21. : 'preview.html'
  22. const file = resolve(root, relative)
  23. if (!file.startsWith(root + sep) || !existsSync(file) || !statSync(file).isFile()) {
  24. res.writeHead(404)
  25. res.end()
  26. return
  27. }
  28. res.writeHead(200, {
  29. 'Content-Type': mime[extname(file)] || 'application/octet-stream',
  30. 'Cache-Control': 'no-store',
  31. })
  32. createReadStream(file).pipe(res)
  33. return
  34. }
  35. const proxy = http.request(
  36. new URL(req.url, upstream),
  37. { method: req.method, headers: { ...req.headers, host: upstream.host } },
  38. (response) => {
  39. res.writeHead(response.statusCode, response.headers)
  40. response.pipe(res)
  41. }
  42. )
  43. proxy.on('error', () => {
  44. res.writeHead(502)
  45. res.end('Local cockpit preview server unavailable')
  46. })
  47. req.pipe(proxy)
  48. })
  49. server.listen(port, '127.0.0.1', () =>
  50. console.log(`Model review: http://127.0.0.1:${port}/preview.html (engine/GIS: ${upstream.origin})`)
  51. )