- Add server.js: Express serves dist/ + injects env (GA_ID, FORM_EMAIL, GMAP) at runtime - Container port now 4321 (identical to moreminimore-astroreal that deploys on EasyPanel) - Remove nginx.conf + entrypoint.sh (no longer needed); Dockerfile uses node server.js - Verified: pnpm build + node server.js -> all 40 pages serve 200, env injected, 0 leftovers
93 lines
3.3 KiB
JavaScript
93 lines
3.3 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) => {
|
|
let pathname;
|
|
try {
|
|
pathname = decodeURIComponent(req.path);
|
|
} catch {
|
|
return next();
|
|
}
|
|
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'}`);
|
|
});
|