2026-07-10 05:42:46 +07:00
|
|
|
import { existsSync, readFileSync } from 'node:fs';
|
|
|
|
|
import { resolve, sep } from 'node:path';
|
2026-07-09 22:08:26 +07:00
|
|
|
|
2026-07-10 05:42:46 +07:00
|
|
|
const distDir = resolve('./dist');
|
|
|
|
|
const indexPath = resolve(distDir, 'index.html');
|
2026-07-09 22:08:26 +07:00
|
|
|
|
|
|
|
|
const contentTypes = {
|
2026-07-10 05:42:46 +07:00
|
|
|
html: 'text/html; charset=utf-8',
|
|
|
|
|
css: 'text/css; charset=utf-8',
|
|
|
|
|
js: 'application/javascript; charset=utf-8',
|
|
|
|
|
json: 'application/json; charset=utf-8',
|
|
|
|
|
svg: 'image/svg+xml',
|
|
|
|
|
png: 'image/png',
|
|
|
|
|
jpg: 'image/jpeg',
|
|
|
|
|
jpeg: 'image/jpeg',
|
|
|
|
|
gif: 'image/gif',
|
|
|
|
|
webp: 'image/webp',
|
|
|
|
|
ico: 'image/x-icon',
|
|
|
|
|
woff: 'font/woff',
|
|
|
|
|
woff2: 'font/woff2',
|
|
|
|
|
ttf: 'font/ttf',
|
|
|
|
|
};
|
2026-07-09 22:08:26 +07:00
|
|
|
|
|
|
|
|
function responseFromFile(filePath, headers) {
|
|
|
|
|
try {
|
2026-07-10 05:42:46 +07:00
|
|
|
return new Response(readFileSync(filePath), { headers });
|
2026-07-09 22:08:26 +07:00
|
|
|
} catch (error) {
|
2026-07-10 05:42:46 +07:00
|
|
|
if (error?.code === 'ENOENT') {
|
|
|
|
|
return new Response('Not Found', { status: 404 });
|
2026-07-09 22:08:26 +07:00
|
|
|
}
|
|
|
|
|
|
2026-07-10 05:42:46 +07:00
|
|
|
console.error('File read error:', error);
|
|
|
|
|
return new Response('Internal Server Error', { status: 500 });
|
2026-07-09 22:08:26 +07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function serveIndex() {
|
|
|
|
|
return responseFromFile(indexPath, {
|
2026-07-10 05:42:46 +07:00
|
|
|
'Content-Type': contentTypes.html,
|
|
|
|
|
'Cache-Control': 'no-cache',
|
|
|
|
|
});
|
2026-07-09 22:08:26 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function serveFile(filePath, pathname) {
|
2026-07-10 05:42:46 +07:00
|
|
|
const ext = filePath.split('.').pop() || '';
|
2026-07-09 22:08:26 +07:00
|
|
|
return responseFromFile(filePath, {
|
2026-07-10 05:42:46 +07:00
|
|
|
'Content-Type': contentTypes[ext] || 'application/octet-stream',
|
|
|
|
|
'Cache-Control': pathname.startsWith('/assets/')
|
|
|
|
|
? 'public, max-age=31536000, immutable'
|
|
|
|
|
: 'no-cache',
|
|
|
|
|
});
|
2026-07-09 22:08:26 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bun.serve({
|
|
|
|
|
port: 80,
|
2026-07-10 05:42:46 +07:00
|
|
|
hostname: '0.0.0.0',
|
2026-07-09 22:08:26 +07:00
|
|
|
fetch(req) {
|
2026-07-10 05:42:46 +07:00
|
|
|
const url = new URL(req.url);
|
|
|
|
|
const pathname = url.pathname;
|
|
|
|
|
const filePath = resolve(distDir, pathname.slice(1));
|
|
|
|
|
const insideDist = filePath === distDir || filePath.startsWith(`${distDir}${sep}`);
|
2026-07-09 22:08:26 +07:00
|
|
|
|
|
|
|
|
if (!insideDist) {
|
2026-07-10 05:42:46 +07:00
|
|
|
return new Response('Forbidden', { status: 403 });
|
2026-07-09 22:08:26 +07:00
|
|
|
}
|
|
|
|
|
|
2026-07-10 05:42:46 +07:00
|
|
|
if (pathname !== '/' && existsSync(filePath)) {
|
|
|
|
|
return serveFile(filePath, pathname);
|
2026-07-09 22:08:26 +07:00
|
|
|
}
|
|
|
|
|
|
2026-07-10 05:42:46 +07:00
|
|
|
if (!pathname.includes('.') && existsSync(indexPath)) {
|
|
|
|
|
return serveIndex();
|
2026-07-09 22:08:26 +07:00
|
|
|
}
|
|
|
|
|
|
2026-07-10 05:42:46 +07:00
|
|
|
return new Response('Not Found', { status: 404 });
|
2026-07-09 22:08:26 +07:00
|
|
|
},
|
2026-07-10 05:42:46 +07:00
|
|
|
});
|