import { createReadStream, statSync } from 'node:fs' import { createServer } from 'node:http' import { extname, resolve, sep } from 'node:path' const root = resolve(process.env.REMORA_WEB_ROOT ?? '/app/dist') const port = numberEnvironment('REMORA_WEB_PORT', 8080) const runtimeConfig = { apiUrl: environment('REMORA_WEB_API_URL', '/v1/'), eventsUrl: environment('REMORA_WEB_EVENTS_URL', ''), oidcAuthority: environment('REMORA_WEB_OIDC_AUTHORITY', '/oidc'), oidcClientId: environment('REMORA_WEB_OIDC_CLIENT_ID', 'remora-web'), oidcRedirectUri: environment('REMORA_WEB_OIDC_REDIRECT_URI', '/'), oidcPostLogoutRedirectUri: environment( 'REMORA_WEB_OIDC_POST_LOGOUT_REDIRECT_URI', '/', ), oidcScope: environment('REMORA_WEB_OIDC_SCOPE', 'openid profile'), } const startedAt = Date.now() let requests = 0 createServer((request, response) => { requests += 1 const traceId = traceIdentifier(request.headers.traceparent) response.setHeader('X-Content-Type-Options', 'nosniff') response.setHeader('Referrer-Policy', 'no-referrer') response.setHeader('X-Frame-Options', 'DENY') response.setHeader('Cross-Origin-Opener-Policy', 'same-origin') response.setHeader('Content-Security-Policy', contentSecurityPolicy()) response.setHeader('Traceparent', `00-${traceId}-0000000000000001-01`) response.once('finish', () => { process.stdout.write( `${JSON.stringify({ timestamp: new Date().toISOString(), level: 'info', component: 'remora-web', trace_id: traceId, method: request.method, path: requestPath(request.url), status: response.statusCode, })}\n`, ) }) route(request, response) }).listen(port, '0.0.0.0', () => { process.stdout.write( `${JSON.stringify({ timestamp: new Date().toISOString(), level: 'info', component: 'remora-web', message: 'web application ready', port, })}\n`, ) }) function route(request, response) { if (request.method !== 'GET' && request.method !== 'HEAD') { response.writeHead(405, { Allow: 'GET, HEAD' }) response.end() return } const url = new URL(request.url ?? '/', 'http://localhost') if (url.pathname === '/health/live' || url.pathname === '/health/ready') { json(response, 200, { status: 'ready' }) return } if (url.pathname === '/metrics') { const body = '# TYPE remora_web_build_info gauge\n' + 'remora_web_build_info 1\n' + '# TYPE remora_web_requests_total counter\n' + `remora_web_requests_total ${requests}\n` + '# TYPE remora_web_uptime_seconds gauge\n' + `remora_web_uptime_seconds ${Math.floor((Date.now() - startedAt) / 1000)}\n` response.writeHead(200, { 'Content-Type': 'text/plain; version=0.0.4', 'Content-Length': Buffer.byteLength(body), }) response.end(request.method === 'HEAD' ? undefined : body) return } if (url.pathname === '/runtime-config.js') { const body = `window.remoraRuntimeConfig=${JSON.stringify( normalizedRuntimeConfig(), )};\n` response.writeHead(200, { 'Cache-Control': 'no-store', 'Content-Type': 'text/javascript; charset=utf-8', 'Content-Length': Buffer.byteLength(body), }) response.end(request.method === 'HEAD' ? undefined : body) return } const candidate = safeFile(url.pathname) const file = candidate ?? resolve(root, 'index.html') const body = regularFile(file) ? file : resolve(root, 'index.html') if (!regularFile(body)) { response.writeHead(404) response.end() return } const size = statSync(body).size response.writeHead(200, { 'Cache-Control': body.endsWith('index.html') ? 'no-cache' : 'public, max-age=31536000, immutable', 'Content-Type': contentType(body), 'Content-Length': size, }) if (request.method === 'HEAD') { response.end() } else { createReadStream(body).pipe(response) } } function requestPath(value) { try { return new URL(value ?? '/', 'http://localhost').pathname } catch { return '/' } } function normalizedRuntimeConfig() { const origin = requiredEnvironment('REMORA_WEB_PUBLIC_ORIGIN') return { ...runtimeConfig, eventsUrl: runtimeConfig.eventsUrl || absolute(runtimeConfig.apiUrl.replace(/\/?$/, '/events'), origin, true), oidcAuthority: absolute(runtimeConfig.oidcAuthority, origin, false), oidcRedirectUri: absolute(runtimeConfig.oidcRedirectUri, origin, false), oidcPostLogoutRedirectUri: absolute( runtimeConfig.oidcPostLogoutRedirectUri, origin, false, ), } } function absolute(value, origin, websocket) { if (/^[a-z][a-z0-9+.-]*:/i.test(value)) return value if (!origin) return value const url = new URL(value, origin) if (websocket) url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:' return url.toString() } function safeFile(pathname) { let decoded try { decoded = decodeURIComponent(pathname) } catch { return null } const candidate = resolve(root, `.${decoded}`) return candidate === root || candidate.startsWith(`${root}${sep}`) ? candidate : null } function regularFile(path) { try { return statSync(path).isFile() } catch { return false } } function json(response, status, value) { const body = JSON.stringify(value) response.writeHead(status, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), }) response.end(body) } function contentType(path) { return ( { '.css': 'text/css; charset=utf-8', '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.json': 'application/json', '.svg': 'image/svg+xml', '.woff2': 'font/woff2', }[extname(path)] ?? 'application/octet-stream' ) } function contentSecurityPolicy() { const connect = environment('REMORA_WEB_CSP_CONNECT_SRC', "'self'") return `default-src 'self'; base-uri 'none'; frame-ancestors 'none'; object-src 'none'; connect-src ${connect}` } function traceIdentifier(traceparent) { const match = typeof traceparent === 'string' ? /^00-([0-9a-f]{32})-[0-9a-f]{16}-[0-9a-f]{2}$/.exec(traceparent) : null return match?.[1] ?? crypto.randomUUID().replaceAll('-', '') } function environment(name, fallback) { const value = process.env[name] return value === undefined || value === '' ? fallback : value } function requiredEnvironment(name) { const value = process.env[name] if (value === undefined || value === '') { throw new Error(`${name} is required`) } return value } function numberEnvironment(name, fallback) { const value = Number(environment(name, String(fallback))) if (!Number.isSafeInteger(value) || value < 1 || value > 65535) { throw new Error(`${name} must be a valid port`) } return value }