website-monitor/backend/src/routes/waitlist.ts

135 lines
3.7 KiB
TypeScript

import { Router } from 'express';
import { query } from '../db';
import { z } from 'zod';
const router = Router();
// Validation schema
const waitlistSchema = z.object({
email: z.string().email('Invalid email address'),
source: z.string().optional().default('landing_page'),
referrer: z.string().optional(),
});
// POST /api/waitlist - Add email to waitlist
router.post('/', async (req, res) => {
try {
const data = waitlistSchema.parse(req.body);
// Check if email already exists
const existing = await query(
'SELECT id FROM waitlist_leads WHERE email = $1',
[data.email.toLowerCase()]
);
if (existing.rows.length > 0) {
// Already on waitlist - return success anyway (don't reveal they're already signed up)
const countResult = await query('SELECT COUNT(*) FROM waitlist_leads');
const position = parseInt(countResult.rows[0].count, 10);
res.json({
success: true,
message: 'You\'re on the list!',
position,
alreadySignedUp: true,
});
return;
}
// Insert new lead
await query(
'INSERT INTO waitlist_leads (email, source, referrer) VALUES ($1, $2, $3)',
[data.email.toLowerCase(), data.source, data.referrer || null]
);
// Get current position (total count)
const countResult = await query('SELECT COUNT(*) FROM waitlist_leads');
const position = parseInt(countResult.rows[0].count, 10);
console.log(`✅ Waitlist signup: ${data.email} (Position #${position})`);
res.json({
success: true,
message: 'You\'re on the list!',
position,
});
} catch (error) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
error: 'validation_error',
message: error.errors[0].message,
});
return;
}
console.error('Waitlist signup error:', error);
res.status(500).json({
success: false,
error: 'server_error',
message: 'Failed to join waitlist. Please try again.',
});
}
});
// GET /api/waitlist/count - Get current waitlist count (public)
router.get('/count', async (_req, res) => {
try {
const result = await query('SELECT COUNT(*) FROM waitlist_leads');
const count = parseInt(result.rows[0].count, 10);
// Add a base number to make it look more impressive at launch
const displayCount = count + 430; // Starting with "430+ waiting"
res.json({
success: true,
count: displayCount,
});
} catch (error) {
console.error('Waitlist count error:', error);
res.status(500).json({
success: false,
count: 430, // Fallback to base number
});
}
});
// GET /api/waitlist/admin - Get waitlist leads (Admin only)
router.get('/admin', async (req, res) => {
try {
const adminPassword = process.env.ADMIN_PASSWORD;
const providedPassword = req.headers['x-admin-password'];
if (!adminPassword || providedPassword !== adminPassword) {
res.status(401).json({
success: false,
message: 'Unauthorized',
});
return;
}
// Get stats
const countResult = await query('SELECT COUNT(*) FROM waitlist_leads');
const total = parseInt(countResult.rows[0].count, 10);
// Get leads
const leadsResult = await query(
'SELECT * FROM waitlist_leads ORDER BY created_at DESC LIMIT 100'
);
res.json({
success: true,
total,
leads: leadsResult.rows,
});
} catch (error) {
console.error('Waitlist admin error:', error);
res.status(500).json({
success: false,
message: 'Server error',
});
}
});
export default router;