2026-02-17 23:03:48 -05:00

145 lines
4.5 KiB
JavaScript

require('dotenv').config();
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
const PORT = process.env.PORT || 8899;
const MAIN_DOMAIN = process.env.MAIN_DOMAIN || 'solvedby.us';
// Ensure data directory exists
const dataDir = path.join(__dirname, 'data');
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
}
// Subdomain-to-clientId index (loaded from disk)
const subdomainIndexPath = path.join(dataDir, 'subdomain-index.json');
let subdomainIndex = {};
if (fs.existsSync(subdomainIndexPath)) {
subdomainIndex = JSON.parse(fs.readFileSync(subdomainIndexPath, 'utf8'));
}
function saveSubdomainIndex() {
fs.writeFileSync(subdomainIndexPath, JSON.stringify(subdomainIndex, null, 2));
}
function getSubdomain(host) {
// Strip port
const hostname = (host || '').split(':')[0];
// Check if it's a subdomain of MAIN_DOMAIN
if (hostname.endsWith('.' + MAIN_DOMAIN)) {
return hostname.replace('.' + MAIN_DOMAIN, '').toLowerCase();
}
return null;
}
// View engine
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'templates'));
// Middleware
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// ─── Subdomain routing: {clientname}.solvedby.us serves that client's site ───
app.use((req, res, next) => {
const subdomain = getSubdomain(req.headers.host);
if (!subdomain) {
// No subdomain — serve the main wizard app
return next();
}
// Look up clientId from subdomain
const clientId = subdomainIndex[subdomain];
if (!clientId) {
return res.status(404).send(`<h1>Site not found</h1><p>No business site exists at <strong>${subdomain}.${MAIN_DOMAIN}</strong></p><p><a href="https://${MAIN_DOMAIN}">Create one at ${MAIN_DOMAIN}</a></p>`);
}
const siteDir = path.join(dataDir, clientId, 'site');
if (!fs.existsSync(siteDir)) {
return res.status(404).send('Site files not found');
}
// Map paths to files
let filePath;
const urlPath = req.path.replace(/\/$/, '') || '';
if (urlPath === '' || urlPath === '/') {
filePath = path.join(siteDir, 'index.html');
} else if (urlPath === '/contact') {
filePath = path.join(siteDir, 'contact.html');
} else if (urlPath === '/privacy-policy') {
filePath = path.join(siteDir, 'privacy-policy.html');
} else if (urlPath === '/terms') {
filePath = path.join(siteDir, 'terms.html');
} else if (urlPath.startsWith('/assets/')) {
filePath = path.join(siteDir, urlPath);
} else if (urlPath.startsWith('/data/')) {
// Allow serving logos etc from data dir
filePath = path.join(__dirname, urlPath);
} else {
return res.status(404).send('Page not found');
}
if (fs.existsSync(filePath)) {
return res.sendFile(filePath);
}
return res.status(404).send('Page not found');
});
// ─── Main domain routes ───
app.use('/public', express.static(path.join(__dirname, 'public')));
app.use('/data', express.static(path.join(__dirname, 'data')));
// Routes
const apiRoutes = require('./routes/api');
const sitesRoutes = require('./routes/sites');
app.use('/api', apiRoutes);
app.use('/sites', sitesRoutes);
// Main form page
app.get('/', (req, res) => {
res.render('form');
});
// Results page
app.get('/results/:clientId', (req, res) => {
const { clientId } = req.params;
const configPath = path.join(dataDir, clientId, 'config.json');
if (!fs.existsSync(configPath)) {
return res.status(404).send('Project not found');
}
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
res.render('packet/results', { config, clientId });
});
// Download zip
app.get('/download/:clientId', (req, res) => {
const { clientId } = req.params;
const zipPath = path.join(dataDir, clientId, 'compliance-packet.zip');
if (!fs.existsSync(zipPath)) {
return res.status(404).send('Zip not found');
}
const config = JSON.parse(fs.readFileSync(path.join(dataDir, clientId, 'config.json'), 'utf8'));
const safeName = config.businessName.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
res.download(zipPath, `${safeName}-a2p-compliance-packet.zip`);
});
// Export for use in API routes
app.locals.subdomainIndex = subdomainIndex;
app.locals.saveSubdomainIndex = saveSubdomainIndex;
app.locals.MAIN_DOMAIN = MAIN_DOMAIN;
app.listen(PORT, () => {
console.log(`\n🚀 A2P Compliance Wizard running at http://localhost:${PORT}`);
console.log(` Main domain: https://${MAIN_DOMAIN}`);
console.log(` Client sites: https://{name}.${MAIN_DOMAIN}\n`);
});