Files
gate-quote/server/index.js
Todd 74587ccceb Initial commit: gate operator quotation app
React + Vite + Tailwind frontend with Express backend.
3-step wizard (Operator, Ground Loops, Access Control)
with PDF quote download and CAD pricing data.
2026-05-21 09:13:59 -04:00

52 lines
1.4 KiB
JavaScript

const express = require('express');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
const os = require('os');
const app = express();
const PORT = process.env.PORT || 3001;
app.use(cors());
const pricingData = JSON.parse(
fs.readFileSync(path.join(__dirname, 'data', 'pricing.json'), 'utf-8')
);
app.get('/api/pricing', (req, res) => {
res.json(pricingData);
});
app.get('/api/pricing/:category', (req, res) => {
const { category } = req.params;
const validCategories = ['operators', 'groundLoopStyles', 'groundLoopTypes', 'accessControl'];
if (validCategories.includes(category)) {
return res.json(pricingData[category]);
}
res.status(400).json({ error: `Invalid category. Use: ${validCategories.join(', ')}` });
});
const clientDist = path.join(__dirname, '..', 'client', 'dist');
if (fs.existsSync(clientDist)) {
app.use(express.static(clientDist));
app.get('*', (req, res) => {
res.sendFile(path.join(clientDist, 'index.html'));
});
}
function getLocalIP() {
const ifaces = os.networkInterfaces();
for (const name of Object.keys(ifaces)) {
for (const iface of ifaces[name]) {
if (iface.family === 'IPv4' && !iface.internal) return iface.address;
}
}
return 'localhost';
}
app.listen(PORT, '0.0.0.0', () => {
const ip = getLocalIP();
console.log(`\n Local: http://localhost:${PORT}`);
console.log(` Network: http://${ip}:${PORT}\n`);
});