import { createReadStream } from 'node:fs' import { readFile, stat } from 'node:fs/promises' import { createServer } from 'node:http' import path from 'node:path' import { fileURLToPath } from 'node:url' import { render } from './dist/server/entry-server.js' const root = fileURLToPath (new URL ('./dist/client/', import.meta.url)) const template = await readFile (path.join (root, 'index.html'), 'utf8') const origin = new URL (process.env.PUBLIC_ORIGIN ?? 'http://localhost:5173').origin const apiBaseURL = process.env.SSR_API_BASE_URL ?? 'http://127.0.0.1:3002' const types = { '.js': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.png': 'image/png', '.gif': 'image/gif', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.webp': 'image/webp', '.woff': 'font/woff', '.woff2': 'font/woff2', '.otf': 'font/otf', '.mp4': 'video/mp4', '.mp3': 'audio/mpeg', '.txt': 'text/plain' } if (process.env.NODE_ENV === 'production' && !process.env.PUBLIC_ORIGIN) throw new Error ('PUBLIC_ORIGIN is required in production') createServer (async (request, response) => { response.setHeader ('Cache-Control', 'no-store') if (!['GET', 'HEAD'].includes (request.method)) { response.writeHead (405, { Allow: 'GET, HEAD' }).end () return } try { const url = new URL (request.url, origin) const pathname = decodeURIComponent (url.pathname) const file = path.resolve (root, '.' + pathname) const allowed = file.startsWith (root) && !pathname.split ('/').some (segment => segment.startsWith ('.')) && pathname !== '/index.html' const info = allowed ? await stat (file).catch (() => null) : null if (info?.isFile ()) { response.writeHead (200, { 'Content-Type': types[path.extname (file)] ?? 'application/octet-stream', 'Content-Length': info.size, 'Cache-Control': /\/[^/]+-[\w-]{8}\.[^.]+$/.test (pathname) ? 'public, max-age=31536000, immutable' : 'public, max-age=3600' }) if (request.method === 'HEAD') response.end () else createReadStream (file).on ('error', () => response.destroy ()).pipe (response) return } if (pathname.startsWith ('/assets/')) { response.writeHead (404).end () return } const result = await render (url.pathname + url.search, origin, apiBaseURL) if (result?.location != null) { response.writeHead (result.status, { Location: result.location }).end () return } const html = result?.html == null ? template.replace ('', result?.status === 404 ? '' : '') : template .replace ('lang="ja"', `lang="${result.locale}"`) .replace (/