import express from 'express' import path from 'path' import { fileURLToPath } from 'url' import { existsSync, mkdirSync, chmodSync } from 'fs' import sqlite3 from 'better-sqlite3' import z from 'zod' // --- Setup --- const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const app = express() const dbPath = path.join(__dirname, '../db/queuenorth.db') const dbDir = path.dirname(dbPath) // Create db directory if it doesn't exist if (!existsSync(dbDir)) { mkdirSync(dbDir, { recursive: true }) // Try to set writable permissions, ignore if running as non-root try { chmodSync(dbDir, 0o755) } catch (e) {} } // Middleware app.use(express.json()) app.use(express.urlencoded({ extended: true })) app.use(express.static(path.join(__dirname, '../dist'))) // --- Database --- const db = sqlite3(dbPath) // Initialize schema const initSchema = () => { // Leads table db.exec(` CREATE TABLE IF NOT EXISTS leads ( id INTEGER PRIMARY KEY AUTOINCREMENT, company TEXT NOT NULL, name TEXT NOT NULL, email TEXT NOT NULL, phone TEXT, zip TEXT, message TEXT, service_interest TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `) // Support requests table db.exec(` CREATE TABLE IF NOT EXISTS support_requests ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, company TEXT NOT NULL, email TEXT NOT NULL, phone TEXT, issue TEXT NOT NULL, priority TEXT DEFAULT 'medium', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `) } initSchema() // --- Validation Schemas --- const leadSchema = z.object({ company: z.string().min(1, 'Company name is required'), name: z.string().min(1, 'Name is required'), email: z.string().email('Valid email is required'), phone: z.string().optional(), zip: z.string().optional(), message: z.string().optional(), service_interest: z.string().optional(), }) const supportSchema = z.object({ name: z.string().min(1, 'Name is required'), company: z.string().min(1, 'Company name is required'), email: z.string().email('Valid email is required'), phone: z.string().optional(), issue: z.string().min(10, 'Please provide more details about your issue'), priority: z.enum(['low', 'medium', 'high']).optional(), }) // --- API Routes --- // Health check app.get('/api/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }) }) // Submit lead app.post('/api/leads', (req, res) => { try { const parsed = leadSchema.safeParse(req.body) if (!parsed.success) { return res.status(400).json({ error: 'Validation failed', details: parsed.error.format(), }) } const stmt = db.prepare(` INSERT INTO leads (company, name, email, phone, zip, message, service_interest) VALUES (?, ?, ?, ?, ?, ?, ?) `) stmt.run( parsed.data.company, parsed.data.name, parsed.data.email, parsed.data.phone || null, parsed.data.zip || null, parsed.data.message || null, parsed.data.service_interest || null ) res.json({ success: true, message: 'Thanks! We\'ll be in touch shortly.' }) } catch (err) { console.error('Error submitting lead:', err) res.status(500).json({ error: 'Failed to submit lead' }) } }) // Submit support request app.post('/api/support', (req, res) => { try { const parsed = supportSchema.safeParse(req.body) if (!parsed.success) { return res.status(400).json({ error: 'Validation failed', details: parsed.error.format(), }) } const stmt = db.prepare(` INSERT INTO support_requests (name, company, email, phone, issue, priority) VALUES (?, ?, ?, ?, ?, ?) `) stmt.run( parsed.data.name, parsed.data.company, parsed.data.email, parsed.data.phone || null, parsed.data.issue, parsed.data.priority || 'medium' ) res.json({ success: true, message: 'Thanks! We\'ll get back to you soon.' }) } catch (err) { console.error('Error submitting support request:', err) res.status(500).json({ error: 'Failed to submit support request' }) } }) // --- Start Server --- const PORT = process.env.SERVER_PORT || 3001 app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`) console.log(`Health check: http://localhost:${PORT}/api/health`) })