← back to Textiletuesday
server.js
51 lines
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
const PORT = process.env.PORT || 9899;
app.use(express.json());
// Vendor-redact: strip DW vendor names from /api/* responses (titles + vendor field).
// Standing rule — DW vendor names NEVER appear in customer-facing UI. (Matches the fleet.)
app.use(require('../_shared/api-vendor-redact'));
// 404-guard: never serve .bak / .pre-* / .orig snapshot files from static root
app.use((req, res, next) => {
if (/\.(bak|orig)(\?|$)/i.test(req.path) || /\.pre-/i.test(req.path)) {
return res.status(404).send('Not found');
}
next();
});
require('../_shared/_universal-promo-banner')(app, { slug: 'textiletuesday' });
app.use(express.static(path.join(__dirname, 'public')));
const products = JSON.parse(fs.readFileSync(path.join(__dirname, 'data/products.json'), 'utf8'));
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
app.get('/health', (req, res) => {
res.json({ status: 'ok', site: 'textiletuesday.com', port: PORT });
});
app.get('/api/products', (req, res) => {
res.json(products);
});
app.post('/api/subscribe', (req, res) => {
const { email } = req.body;
if (!email || !email.includes('@')) {
return res.status(400).json({ error: 'Valid email required' });
}
const entry = JSON.stringify({ email, site: 'textiletuesday.com', ts: new Date().toISOString() }) + '\n';
fs.appendFileSync(path.join(__dirname, 'data/subscribers.jsonl'), entry);
res.json({ ok: true, message: 'You\'re in. Every Tuesday, one textile worth knowing.' });
});
app.listen(PORT, () => {
console.log(`textiletuesday.com running on port ${PORT}`);
});