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(`

Site not found

No business site exists at ${subdomain}.${MAIN_DOMAIN}

Create one at ${MAIN_DOMAIN}

`); } 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`); });