Your commit message
This commit is contained in:
@@ -121,3 +121,15 @@ CREATE TABLE IF NOT EXISTS webhook_logs (
|
||||
CREATE INDEX IF NOT EXISTS idx_webhook_logs_user_id ON webhook_logs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_webhook_logs_monitor_id ON webhook_logs(monitor_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_webhook_logs_created_at ON webhook_logs(created_at);
|
||||
|
||||
-- Waitlist leads table (pre-launch signups)
|
||||
CREATE TABLE IF NOT EXISTS waitlist_leads (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
source VARCHAR(50) DEFAULT 'landing_page',
|
||||
referrer TEXT,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_waitlist_leads_email ON waitlist_leads(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_waitlist_leads_created_at ON waitlist_leads(created_at);
|
||||
|
||||
@@ -41,11 +41,13 @@ app.get('/health', async (_req, res) => {
|
||||
});
|
||||
|
||||
import testRoutes from './routes/test';
|
||||
import waitlistRoutes from './routes/waitlist';
|
||||
|
||||
// Routes
|
||||
app.use('/api/auth', authLimiter, authRoutes);
|
||||
app.use('/api/monitors', authMiddleware, monitorRoutes);
|
||||
app.use('/api/settings', authMiddleware, settingsRoutes);
|
||||
app.use('/api/waitlist', waitlistRoutes); // Public route - no auth required
|
||||
app.use('/test', testRoutes);
|
||||
|
||||
// 404 handler
|
||||
|
||||
95
backend/src/routes/waitlist.ts
Normal file
95
backend/src/routes/waitlist.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { Router } from 'express';
|
||||
import { pool } 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 pool.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 pool.query('SELECT COUNT(*) FROM waitlist_leads');
|
||||
const position = parseInt(countResult.rows[0].count, 10);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'You\'re on the list!',
|
||||
position,
|
||||
alreadySignedUp: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Insert new lead
|
||||
await pool.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 pool.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) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'validation_error',
|
||||
message: error.errors[0].message,
|
||||
});
|
||||
}
|
||||
|
||||
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 pool.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
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user