Your commit message

This commit is contained in:
2026-01-19 22:24:25 +01:00
parent 818779ab07
commit 9fa8045c26
15 changed files with 392 additions and 221 deletions

View File

@@ -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);

View File

@@ -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

View 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;