"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const express_1 = __importDefault(require("express")); const zod_1 = require("zod"); const express_rate_limit_1 = __importDefault(require("express-rate-limit")); const redirect_tracker_service_1 = require("../services/redirect-tracker.service"); const auth_middleware_1 = require("../middleware/auth.middleware"); const logger_1 = require("../lib/logger"); const router = express_1.default.Router(); const redirectTracker = new redirect_tracker_service_1.RedirectTrackerService(); router.get('/health', (req, res) => { res.json({ success: true, status: 200, data: { version: 'v2', timestamp: new Date().toISOString(), environment: process.env.NODE_ENV || 'development' } }); }); const trackingLimiter = (0, express_rate_limit_1.default)({ windowMs: 60 * 60 * 1000, max: 200, message: { success: false, error: 'Rate limit exceeded', message: 'Too many tracking requests. Please try again later.' }, standardHeaders: true, legacyHeaders: false, keyGenerator: (req) => { return req.user ? `user:${req.user.id}` : `ip:${req.ip}`; }, }); const anonymousTrackingLimiter = (0, express_rate_limit_1.default)({ windowMs: 60 * 60 * 1000, max: 50, message: { success: false, error: 'Rate limit exceeded', message: 'Anonymous users are limited to 50 requests per hour. Please register for higher limits.' }, standardHeaders: true, legacyHeaders: false, skip: (req) => !!req.user, }); const trackUrlSchema = zod_1.z.object({ url: zod_1.z.string().min(1, 'URL is required'), method: zod_1.z.enum(['GET', 'POST', 'HEAD']).default('GET'), userAgent: zod_1.z.string().optional(), headers: zod_1.z.record(zod_1.z.string()).optional(), projectId: zod_1.z.string().optional(), followJS: zod_1.z.boolean().default(false), maxHops: zod_1.z.number().min(1).max(20).default(10), timeout: zod_1.z.number().min(1000).max(30000).default(15000), }); const listChecksSchema = zod_1.z.object({ projectId: zod_1.z.string(), limit: zod_1.z.number().min(1).max(100).default(50), offset: zod_1.z.number().min(0).default(0), }); router.post('/test', async (req, res) => { res.json({ success: true, message: 'Test endpoint working' }); }); router.post('/track', auth_middleware_1.optionalAuth, async (req, res) => { try { const validatedData = trackUrlSchema.parse(req.body); let { url } = validatedData; if (!url.startsWith('http://') && !url.startsWith('https://')) { url = 'http://' + url; } if (!validatedData.projectId) { if (req.user) { const userMembership = req.user.memberships[0]; if (userMembership) { validatedData.projectId = 'default-project'; } } else { validatedData.projectId = 'anonymous-project'; } } const userId = req.user?.id || 'anonymous-user'; const result = await redirectTracker.trackUrl({ ...validatedData, url }, userId); logger_1.logger.info(`Enhanced tracking completed: ${url}`, { userId: userId, isAnonymous: !req.user, projectId: validatedData.projectId, checkId: result.id, status: result.status, redirectCount: result.redirectCount }); res.json({ success: true, status: 200, data: { check: result, url, method: result.method, redirectCount: result.redirectCount, finalUrl: result.finalUrl, finalStatusCode: result.hops[result.hops.length - 1]?.statusCode, }, meta: { version: 'v2', enhanced: true, persisted: true, checkId: result.id, } }); } catch (error) { logger_1.logger.error('Enhanced tracking failed:', error); if (error instanceof zod_1.z.ZodError) { return res.status(400).json({ success: false, error: 'Validation error', message: error.errors[0]?.message || 'Invalid input', details: error.errors }); } res.status(500).json({ success: false, error: 'Tracking failed', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); router.get('/track/:checkId', auth_middleware_1.optionalAuth, async (req, res) => { try { const { checkId } = req.params; if (!checkId) { return res.status(400).json({ success: false, error: 'Check ID required' }); } const check = await redirectTracker.getCheck(checkId, req.user?.id); if (!check) { return res.status(404).json({ success: false, error: 'Check not found', message: 'The requested check does not exist or you do not have access to it' }); } res.json({ success: true, status: 200, data: { check }, meta: { version: 'v2', checkId: check.id, } }); } catch (error) { logger_1.logger.error('Failed to retrieve check:', error); res.status(500).json({ success: false, error: 'Failed to retrieve check', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); router.get('/projects/:projectId/checks', auth_middleware_1.requireAuth, async (req, res) => { try { const { projectId } = req.params; const { limit = 50, offset = 0 } = req.query; const validatedData = listChecksSchema.parse({ projectId, limit: Number(limit), offset: Number(offset), }); if (!projectId) { return res.status(400).json({ success: false, error: 'Project ID required' }); } const checks = await redirectTracker.listChecks(validatedData.projectId, validatedData.limit, validatedData.offset); res.json({ success: true, status: 200, data: { checks, pagination: { limit: validatedData.limit, offset: validatedData.offset, total: checks.length, } }, meta: { version: 'v2', projectId: validatedData.projectId, } }); } catch (error) { logger_1.logger.error('Failed to list checks:', error); if (error instanceof zod_1.z.ZodError) { return res.status(400).json({ success: false, error: 'Validation error', message: error.errors[0]?.message || 'Invalid input', details: error.errors }); } res.status(500).json({ success: false, error: 'Failed to list checks', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); router.get('/checks/recent', auth_middleware_1.requireAuth, async (req, res) => { try { const { limit = 20 } = req.query; const checks = await redirectTracker.listChecks('anonymous-project', Number(limit), 0); res.json({ success: true, status: 200, data: { checks, message: 'Cross-project recent checks will be implemented in a future phase' }, meta: { version: 'v2', userId: req.user.id, } }); } catch (error) { logger_1.logger.error('Failed to get recent checks:', error); res.status(500).json({ success: false, error: 'Failed to get recent checks', message: error instanceof Error ? error.message : 'Unknown error occurred' }); } }); router.post('/track/bulk', auth_middleware_1.requireAuth, async (req, res) => { try { logger_1.logger.info(`Bulk tracking request from user: ${req.user.email}`); res.json({ success: true, status: 200, data: { message: 'Bulk tracking will be implemented in Phase 6', bulkJobId: null, }, meta: { version: 'v2', feature: 'bulk-tracking', phase: '6 (future)', } }); } catch (error) { logger_1.logger.error('Bulk tracking placeholder error:', error); res.status(500).json({ success: false, error: 'Bulk tracking not yet available', message: 'This feature will be implemented in Phase 6' }); } }); exports.default = router; //# sourceMappingURL=tracking.routes.js.map