Spaces:
Running
Running
File size: 5,008 Bytes
90f0300 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | import fs from 'node:fs/promises';
import path from 'node:path';
import { gzipSync } from 'node:zlib';
import { GENERATED_ROOT } from './image-generator.js';
import { CLIENT_DIST, HTTPS_ROOT_CA_PATH, compressibleExtensions, mimeTypes } from './app-config.js';
export function acceptsGzip(req) {
return String(req.headers['accept-encoding'] || '')
.split(',')
.some((value) => value.trim().toLowerCase().startsWith('gzip'));
}
export function staticCacheControl(ext, filePath = '') {
if (ext === '.html') {
return 'no-store';
}
const normalized = filePath.split(path.sep).join('/');
if (normalized.includes('/assets/')) {
return 'public, max-age=31536000, immutable';
}
return 'public, max-age=3600';
}
export function sendStaticContent(req, res, status, content, headers, ext) {
let body = content;
const nextHeaders = { ...headers };
if (content.length >= 1024 && compressibleExtensions.has(ext) && acceptsGzip(req)) {
body = gzipSync(content);
nextHeaders['content-encoding'] = 'gzip';
nextHeaders.vary = nextHeaders.vary ? `${nextHeaders.vary}, Accept-Encoding` : 'Accept-Encoding';
}
nextHeaders['content-length'] = body.length;
res.writeHead(status, nextHeaders);
res.end(body);
}
export async function serveFileFromRoot(req, res, rootDir, requestedPath, cacheControl) {
const relativePath = requestedPath.replace(/^\/+/, '');
const resolvedRoot = path.resolve(rootDir);
const candidate = path.normalize(path.join(resolvedRoot, relativePath));
const rootWithSep = resolvedRoot.endsWith(path.sep) ? resolvedRoot : `${resolvedRoot}${path.sep}`;
if (candidate !== resolvedRoot && !candidate.startsWith(rootWithSep)) {
res.writeHead(403);
res.end('Forbidden');
return true;
}
try {
const [realRoot, realCandidate] = await Promise.all([
fs.realpath(resolvedRoot),
fs.realpath(candidate)
]);
const realRootWithSep = realRoot.endsWith(path.sep) ? realRoot : `${realRoot}${path.sep}`;
if (realCandidate !== realRoot && !realCandidate.startsWith(realRootWithSep)) {
res.writeHead(403);
res.end('Forbidden');
return true;
}
const stat = await fs.stat(realCandidate);
if (!stat.isFile()) {
res.writeHead(404);
res.end('Not found');
return true;
}
const ext = path.extname(realCandidate);
const content = await fs.readFile(realCandidate);
sendStaticContent(req, res, 200, content, {
'content-type': mimeTypes.get(ext) || 'application/octet-stream',
'cache-control': cacheControl,
'x-content-type-options': 'nosniff'
}, ext);
return true;
} catch {
res.writeHead(404);
res.end('Not found');
return true;
}
}
export async function serveStatic(req, res, url) {
let requestedPath = '';
try {
requestedPath = decodeURIComponent(url.pathname);
} catch {
res.writeHead(400);
res.end('Bad request');
return true;
}
if (requestedPath === '/codexmobile-root-ca.cer') {
try {
const stat = await fs.stat(HTTPS_ROOT_CA_PATH);
const content = await fs.readFile(HTTPS_ROOT_CA_PATH);
res.writeHead(200, {
'content-type': 'application/x-x509-ca-cert',
'content-length': stat.size,
'cache-control': 'no-store',
'content-disposition': 'attachment; filename="codexmobile-root-ca.cer"',
'x-content-type-options': 'nosniff'
});
res.end(content);
} catch {
res.writeHead(404);
res.end('Certificate not found');
}
return;
}
if (requestedPath.startsWith('/generated/')) {
await serveFileFromRoot(
req,
res,
GENERATED_ROOT,
requestedPath.slice('/generated/'.length),
'private, max-age=86400'
);
return;
}
if (requestedPath === '/') {
requestedPath = '/index.html';
}
const candidate = path.normalize(path.join(CLIENT_DIST, requestedPath));
if (!candidate.startsWith(CLIENT_DIST)) {
res.writeHead(403);
res.end('Forbidden');
return;
}
try {
const stat = await fs.stat(candidate);
const filePath = stat.isDirectory() ? path.join(candidate, 'index.html') : candidate;
const ext = path.extname(filePath);
const content = await fs.readFile(filePath);
sendStaticContent(req, res, 200, content, {
'content-type': mimeTypes.get(ext) || 'application/octet-stream',
'cache-control': staticCacheControl(ext, filePath),
'x-content-type-options': 'nosniff'
}, ext);
} catch {
const indexPath = path.join(CLIENT_DIST, 'index.html');
try {
const content = await fs.readFile(indexPath);
sendStaticContent(req, res, 200, content, {
'content-type': 'text/html; charset=utf-8',
'cache-control': 'no-store',
'x-content-type-options': 'nosniff'
}, '.html');
} catch {
res.writeHead(200, { 'content-type': 'text/plain; charset=utf-8' });
res.end('CodexMobile server is running. Build the PWA with: npm run build');
}
}
}
|