feat: structured logging with timestamps, request logging, and submission details (v0.4.6)

This commit is contained in:
null 2026-05-13 18:31:52 -05:00
parent 6bfd804313
commit 39ee1fe537
1 changed files with 45 additions and 13 deletions

View File

@ -19,9 +19,32 @@ if (!existsSync(dbDir)) {
try { chmodSync(dbDir, 0o755) } catch (e) {}
}
// --- Logger ---
const LOG_LEVELS = { error: 0, warn: 1, info: 2, debug: 3 }
const currentLevel = LOG_LEVELS[process.env.LOG_LEVEL?.toLowerCase()] ?? LOG_LEVELS.info
const log = {
info: (...args) => { if (currentLevel >= LOG_LEVELS.info) console.log(`[${new Date().toISOString()}] INFO `, ...args) },
warn: (...args) => { if (currentLevel >= LOG_LEVELS.warn) console.warn(`[${new Date().toISOString()}] WARN `, ...args) },
error: (...args) => { if (currentLevel >= LOG_LEVELS.error) console.error(`[${new Date().toISOString()}] ERROR`, ...args) },
debug: (...args) => { if (currentLevel >= LOG_LEVELS.debug) console.debug(`[${new Date().toISOString()}] DEBUG`, ...args) },
}
// Middleware
app.use(express.json({ limit: '1mb' }))
app.use(express.urlencoded({ extended: true, limit: '1mb' }))
// Request logging middleware
app.use((req, res, next) => {
const start = Date.now()
res.on('finish', () => {
const ms = Date.now() - start
const level = res.statusCode >= 500 ? 'error' : res.statusCode >= 400 ? 'warn' : 'info'
log[level](`${req.method} ${req.originalUrl} ${res.statusCode} ${ms}ms`)
})
next()
})
app.use(express.static(path.join(__dirname, '../dist')))
// --- Database ---
@ -139,14 +162,14 @@ async function getZohoAccessToken() {
if (data.access_token) {
zohoAccessToken = data.access_token
zohoTokenExpiry = Date.now() + (data.expires_in || 3600) * 1000
console.log('[Zoho] Access token acquired, expires in', data.expires_in || 3600, 'seconds')
log.info('[Zoho] Access token acquired, expires in', data.expires_in || 3600, 'seconds')
return zohoAccessToken
} else {
console.error('[Zoho] Token exchange failed:', JSON.stringify(data))
log.error('[Zoho] Token exchange failed:', JSON.stringify(data))
return null
}
} catch (err) {
console.error('[Zoho] Token acquisition error:', err.message)
log.error('[Zoho] Token acquisition error:', err.message)
return null
}
}
@ -157,7 +180,7 @@ async function forwardToZoho(leadData) {
try {
const accessToken = await getZohoAccessToken()
if (!accessToken) {
console.error('[Zoho] No access token available, skipping lead forwarding')
log.warn('[Zoho] No access token available, skipping lead forwarding')
return
}
@ -187,13 +210,13 @@ async function forwardToZoho(leadData) {
if (response.ok) {
const result = await response.json()
console.log('[Zoho] Lead forwarded successfully:', result.data?.[0]?.details?.id || 'no id returned')
log.info('[Zoho] Lead forwarded successfully:', result.data?.[0]?.details?.id || 'no id returned')
} else {
const text = await response.text()
console.error(`[Zoho] Lead forwarding failed (${response.status}):`, text)
log.error(`[Zoho] Lead forwarding failed (${response.status}):`, text)
}
} catch (err) {
console.error('[Zoho] Forwarding error:', err.message)
log.error('[Zoho] Forwarding error:', err.message)
}
}
@ -238,7 +261,7 @@ app.post('/api/leads', (req, res) => {
VALUES (?, ?, ?, ?, ?, ?, ?)
`)
stmt.run(
const result = stmt.run(
sanitized.company,
sanitized.name,
sanitized.email,
@ -248,12 +271,14 @@ app.post('/api/leads', (req, res) => {
sanitized.service_interest || null
)
log.info(`Lead submitted: ${sanitized.email} from ${sanitized.company} (id: ${result.lastInsertRowid})`)
// Fire-and-forget Zoho forwarding (best-effort, non-blocking)
forwardToZoho(sanitized)
res.json({ success: true, message: 'Thanks! We\'ll be in touch shortly.' })
} catch (err) {
console.error('Error submitting lead:', err)
log.error('Error submitting lead:', err)
res.status(500).json({ error: 'Failed to submit lead' })
}
})
@ -291,7 +316,7 @@ app.post('/api/support', (req, res) => {
VALUES (?, ?, ?, ?, ?, ?)
`)
stmt.run(
const result = stmt.run(
sanitized.name,
sanitized.company,
sanitized.email,
@ -300,9 +325,11 @@ app.post('/api/support', (req, res) => {
sanitized.priority || 'medium'
)
log.info(`Support request submitted: ${sanitized.email} from ${sanitized.company} priority=${sanitized.priority || 'medium'} (id: ${result.lastInsertRowid})`)
res.json({ success: true, message: 'Thanks! We\'ll get back to you soon.' })
} catch (err) {
console.error('Error submitting support request:', err)
log.error('Error submitting support request:', err)
res.status(500).json({ error: 'Failed to submit support request' })
}
})
@ -311,6 +338,11 @@ app.post('/api/support', (req, res) => {
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`)
log.info(`Server running on http://localhost:${PORT}`)
log.info(`Health check: http://localhost:${PORT}/api/health`)
if (ZOHO_ENABLED) {
log.info(`Zoho CRM forwarding: ENABLED (domain: ${ZOHO_API_DOMAIN})`)
} else {
log.info('Zoho CRM forwarding: DISABLED (set ZOHO_ENABLED=true to enable)')
}
})