Files
mitsu-chaiyaporn/server.js
kunthawat a33932cbd0 Fix: pages/CSS 404 on URL-encoded links (double-decode broke UTF-8 filenames)
Express already decodes req.path; the extra decodeURIComponent re-decoded
UTF-8 filenames and returned 404 for URL-encoded links (as used by the
original site), so page CSS didn't load and buttons looked unstyled.

Now all 41 pages + all CSS/assets serve 200 via encoded URLs; 0 404.
2026-08-07 20:08:28 +07:00

91 lines
3.5 KiB
JavaScript

// ── มิตซูชัยพร — Astro + Express (static) ─────────────────────────
// Serves the Astro static build (dist/) on the configured port.
// Mirrors the moreminimore-astroreal deployment pattern that works on EasyPanel.
//
// Injects env vars into HTML at serve time (replaces ${PLACEHOLDER}s):
// GA_ID — Google Analytics / GTM measurement id (empty = analytics removed)
// FORM_EMAIL — FormSubmit recipient for RFQ/contact forms
// GMAP_EMBED — full Google Maps embed URL (no-API-key style)
// PORT — listen port (default 4321)
import express from 'express';
import { existsSync, readFileSync, statSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const PORT = parseInt(process.env.PORT || '4321', 10);
const DIST = join(__dirname, 'dist');
// Build the Google Map embed (type A — free, no API key)
const GMAP_LAT = process.env.GMAP_LAT || '13.557229094780995';
const GMAP_LNG = process.env.GMAP_LNG || '100.28916297408983';
const GMAP_EMBED =
process.env.GMAP_EMBED ||
`https://maps.google.com/maps?q=${GMAP_LAT},${GMAP_LNG}&z=${process.env.GMAP_ZOOM || 16}&output=embed`;
const FORM_EMAIL = process.env.FORM_EMAIL || 'mail@mitsuchaiyaporn.com';
const GA_ID = process.env.GA_ID || '';
const app = express();
app.disable('x-powered-by');
// Inject env placeholders into an HTML string; also remove GA snippet if disabled.
function injectEnv(html) {
let out = html
.replaceAll('${GMAP_EMBED}', GMAP_EMBED)
.replaceAll('${FORM_EMAIL}', FORM_EMAIL);
if (GA_ID) {
out = out
.replaceAll('${GA_ID}', GA_ID);
} else {
// GA disabled: strip the gtag snippet + config lines
out = out
.replace(/<script[^>]*googletagmanager\.com\/gtag\/js[^>]*><\/script>/g, '')
.replace(/<script[\s\S]*?window\.dataLayer[\s\S]*?<\/script>/g, '');
}
return out;
}
// Serve .html with env injection; everything else statically.
app.use((req, res, next) => {
// Express resolves req.path to a decoded path (e.g. %E0%B8%A1 -> ม).
// Do NOT decodeURIComponent again — decoding twice breaks UTF-8 filenames,
// which made URL-encoded links (from the original site) 404 and lose their CSS.
const pathname = req.path;
if (pathname.endsWith('.html') || pathname === '/' || !/\.[a-z0-9]+$/i.test(pathname)) {
let file = pathname === '/' ? 'index.html' : pathname;
let abs = join(DIST, file);
// if it's a directory, serve its index.html
if (existsSync(abs) && statSync(abs).isDirectory()) {
abs = join(abs, 'index.html');
}
// clean-URL support: /about-us/ => about-us/index.html
if (!existsSync(abs)) {
abs = join(DIST, file, 'index.html');
}
if (existsSync(abs) && statSync(abs).isFile()) {
const html = readFileSync(abs, 'utf-8');
res.setHeader('Content-Type', 'text/html; charset=utf-8');
return res.send(injectEnv(html));
}
}
next();
});
// Static Astro build (css, js, images) with long cache
app.use(
express.static(DIST, { maxAge: '1y', etag: true })
);
// 404 fallback
app.use((req, res) => {
res.status(404).sendFile(join(DIST, '404.html'), (err) => {
if (err) res.status(404).send('Not found');
});
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`[mitsu] Listening on http://0.0.0.0:${PORT}`);
console.log(`[mitsu] Static: ${existsSync(DIST) ? 'dist/ ✓' : 'dist/ NOT found'}`);
});