162 lines
4.2 KiB
JavaScript
162 lines
4.2 KiB
JavaScript
|
|
import express from 'express'
|
||
|
|
import path from 'path'
|
||
|
|
import { fileURLToPath } from 'url'
|
||
|
|
import { existsSync, mkdirSync } 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')
|
||
|
|
|
||
|
|
// Create db directory if it doesn't exist
|
||
|
|
if (!existsSync(path.dirname(dbPath))) {
|
||
|
|
mkdirSync(path.dirname(dbPath), { recursive: true })
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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`)
|
||
|
|
})
|