const express = require("express"); const cors = require("cors"); const helmet = require("helmet"); const rateLimit = require("express-rate-limit"); const { Client, LocalAuth } = require("whatsapp-web.js"); const qrcode = require("qrcode-terminal"); const { loadEnvFile } = require("./src/utils/env"); const { initSchema } = require("./src/db"); const { requestLogger } = require("./src/utils/logger"); loadEnvFile(); const app = express(); const PORT = process.env.PORT || 3000; const dashboardOrigin = process.env.VERIFLO_DASHBOARD_ORIGIN || 'http://localhost:5174'; const whatsappEnabled = process.env.VERIFLO_ENABLE_WHATSAPP_CLIENT !== 'false'; // Initialize SQLite Schema initSchema(); // --- Global Security & Middleware --- app.use(helmet()); app.use(cors({ origin: [dashboardOrigin, 'http://localhost:5173'], credentials: false, })); app.use(express.json()); // Request logging middleware - logs all requests with timestamps to file app.use(requestLogger); // Import Routers const authRoutes = require('./src/routes/auth'); const userRoutes = require('./src/routes/user'); const otpRoutes = require('./src/routes/otp'); const analyticsRoutes = require('./src/routes/analytics'); const adminRoutes = require('./src/routes/admin'); // Trust proxy if running behind Nginx/Vercel to get real IPs for analytics app.set('trust proxy', 1); // Global Rate Limiter — 5000 reqs / 15 mins per IP (development-friendly, still prevents abuse) const globalLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5000, standardHeaders: true, legacyHeaders: false, message: { error: 'Too many requests from this IP. Please try again later.' }, skip: (req) => req.ip === '127.0.0.1' || req.ip === '::1', // Skip rate limiting for localhost }); app.use(globalLimiter); // --- WhatsApp Client Wrapper --- // We attach the client and its state to `app.locals` so routes can access it app.locals.waReady = false; app.locals.waInitError = null; app.locals.waClient = null; if (whatsappEnabled) { const client = new Client({ authStrategy: new LocalAuth(), puppeteer: { headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-features=site-per-process', '--ignore-certificate-errors', '--ignore-certificate-errors-spki-list' ] } }); client.on("qr", (qr) => { console.log("Scan this QR code with WhatsApp:"); qrcode.generate(qr, { small: true }); }); client.on("ready", () => { app.locals.waReady = true; app.locals.waInitError = null; console.log("WhatsApp client is ready!"); }); client.on("authenticated", () => { console.log("Authenticated successfully."); }); client.on("auth_failure", (msg) => { app.locals.waInitError = typeof msg === 'string' ? msg : 'WhatsApp authentication failed.'; console.error("Authentication failed:", msg); }); client.on("disconnected", (reason) => { app.locals.waReady = false; app.locals.waInitError = typeof reason === 'string' ? reason : 'WhatsApp client disconnected.'; console.log("Client disconnected:", reason); }); client.initialize().catch((error) => { app.locals.waReady = false; app.locals.waInitError = error.message; console.error("WhatsApp client failed to initialize:", error.message); }); app.locals.waClient = client; } else { app.locals.waInitError = 'WhatsApp client disabled by configuration.'; console.log('WhatsApp client initialization skipped because VERIFLO_ENABLE_WHATSAPP_CLIENT=false'); } // --- Express Routes --- app.get("/status", (req, res) => { res.json({ connected: app.locals.waReady, whatsapp_enabled: whatsappEnabled, whatsapp_error: app.locals.waInitError, timestamp: new Date(), port: PORT, dashboard_origin: dashboardOrigin, }); }); // --- Register Internal Dashboard APIs --- app.use('/api/auth', authRoutes); app.use('/api/user', userRoutes); app.use('/api/user/analytics', analyticsRoutes); app.use('/api/admin', adminRoutes); // --- Register Public Facing Edge SDK APIs --- app.use('/v1/otp', otpRoutes); // Legacy /send route temporarily preserved for reference, // but it will be replaced by the secured /v1/otp/send SDK endpoint later app.post("/send", async (req, res) => { if (!app.locals.waReady) return res.status(503).json({ error: "WhatsApp not ready" }); res.status(410).json({ error: "Use /v1/otp/send instead" }); }); // --- Server Startup --- app.listen(PORT, () => { console.log(`Veriflo API server running on http://localhost:${PORT}`); });