Repository navigation
Expand file tree
/
Copy pathserver.mjs
More file actions
22 lines (22 loc) · 1.59 KB
/
Copy pathserver.mjs
File metadata and controls
22 lines (22 loc) · 1.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import http from 'node:http';
import {createReadStream,statSync,existsSync} from 'node:fs';
import {resolve,extname,sep} from 'node:path';
const root=resolve(process.argv[2]||'dist');
const port=Number(process.env.PORT||4173);
const types={'.html':'text/html; charset=utf-8','.js':'text/javascript','.mjs':'text/javascript','.css':'text/css','.wasm':'application/wasm','.json':'application/json','.svg':'image/svg+xml','.pdf':'application/pdf','.data':'application/octet-stream','.otf':'font/otf','.ttf':'font/ttf','.woff2':'font/woff2'};
http.createServer((req,res)=>{
res.setHeader('Cross-Origin-Opener-Policy','same-origin');
res.setHeader('Cross-Origin-Embedder-Policy','require-corp');
res.setHeader('Cross-Origin-Resource-Policy','same-origin');
res.setHeader('X-Content-Type-Options','nosniff');
let pathname;try{pathname=decodeURIComponent(new URL(req.url,'http://localhost').pathname)}catch{res.writeHead(400).end();return}
if(pathname==='/')pathname='/ui/index.html';
const path=resolve(root,'.'+pathname);
if(path!==root&&!path.startsWith(root+sep)){res.writeHead(403).end();return}
const stat=existsSync(path)?statSync(path):null;
if(!stat?.isFile()){res.writeHead(404).end('Not found');return}
res.setHeader('Content-Type',types[extname(path)]||'application/octet-stream');
res.setHeader('Content-Length',stat.size);
res.setHeader('Cache-Control',/\.(wasm|data)$|\.part\d+$/.test(path)?'public,max-age=86400':'no-cache');
if(req.method==='HEAD'){res.end();return}createReadStream(path).pipe(res);
}).listen(port,'127.0.0.1',()=>console.log(`Realtime TeX Web: http://127.0.0.1:${port}/`));