Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | require('dotenv').config({ quiet: true }); const express = require('express'); const helmet = require('helmet'); const corsMiddleware = require('./middleware/cors'); const { authMiddleware } = require('./middleware/auth'); const { generalLimiter, writeLimiter, sensitiveLimiter } = require('./middleware/rateLimit'); const { startCronJobs } = require('./cron'); require('./utils/firebase'); // Initialize Firebase before routes const { patchConsole } = require('./utils/consoleLogger'); // Route all console.log/warn/error through structured logger patchConsole(); // Catch unhandled promise rejections (e.g., fire-and-forget in cron jobs) process.on('unhandledRejection', (reason) => { // eslint-disable-next-line no-console console.error('Unhandled promise rejection:', reason); }); const app = express(); app.set('trust proxy', 1); const PORT = process.env.PORT || 3000; // Middleware app.use(helmet()); app.use(corsMiddleware); app.use(express.json({ limit: '1mb' })); // Request/response logging (after body parsing, before auth) const logger = require('./utils/loggerInstance'); const { createRequestLogger } = require('./middleware/requestLogger'); app.use(createRequestLogger(logger)); // Health check (no auth) app.get('/api/health', (req, res) => { res.json({ status: 'ok', timestamp: Date.now() }); }); // Auth routes (mounted BEFORE auth middleware — these handle their own auth) app.use('/api', require('./routes/auth')); // Auth middleware for all /api routes (except health, log-config, auth, and pre-auth endpoints) app.use('/api', (req, res, next) => { if ( req.path === '/health' || req.path === '/log-config' || req.path.startsWith('/auth/') || (req.method === 'GET' && req.path === '/config/startingScreens') || (req.path.startsWith('/test/') && process.env.NODE_ENV !== 'production') || (req.method === 'GET' && /^\/users\/[^/]+\/data-export\/download$/.test(req.path)) ) return next(); authMiddleware(req, res, next); }); // General rate limit on all API routes app.use('/api', generalLimiter); // Stricter limits on write-heavy routes app.use('/api/conversations', writeLimiter); app.use('/api/economy/gacha', writeLimiter); app.use('/api/economy/gift', writeLimiter); app.use('/api/economy/gift-direct', writeLimiter); app.use('/api/economy/gift-batch', writeLimiter); app.use('/api/economy/backpack-send', writeLimiter); app.use('/api/translate', writeLimiter); // Strictest limits on sensitive operations app.use('/api/economy/purchase', sensitiveLimiter); app.use('/api/economy/trial-claim', sensitiveLimiter); app.use('/api/economy/trial-activate', sensitiveLimiter); app.use('/api/reports', sensitiveLimiter); app.use('/api/appeals', sensitiveLimiter); app.use('/api/users/:uniqueId/delete', sensitiveLimiter); app.use('/api/users/:uniqueId/data-export', sensitiveLimiter); // Mount route modules app.use('/api', require('./routes/config')); app.use('/api', require('./routes/users')); app.use('/api', require('./routes/economy')); app.use('/api', require('./routes/livekit')); app.use('/api', require('./routes/reports')); app.use('/api', require('./routes/notifications')); app.use('/api', require('./routes/rooms')); app.use('/api', require('./routes/data-export')); app.use('/api', require('./routes/conversations')); app.use('/api', require('./routes/banners')); app.use('/api', require('./routes/fun-facts')); app.use('/api', require('./routes/admin-users')); app.use('/api', require('./routes/admin-economy')); app.use('/api', require('./routes/admin-gifts')); app.use('/api', require('./routes/admin-cleanup')); app.use('/api', require('./routes/admin-backup')); app.use('/api', require('./routes/admin-logs')); app.use('/api', require('./routes/admin-log-config')); app.use('/api', require('./routes/storage')); app.use('/api', require('./routes/device-info')); app.use('/api', require('./routes/admin-bans')); app.use('/api', require('./routes/admin-devices')); app.use('/api', require('./routes/admin-temp-id')); app.use('/api', require('./routes/admin-alerts')); app.use('/api', require('./routes/translate')); const { createLogsRouter } = require('./routes/logs'); app.use('/api', createLogsRouter(logger)); // Dev-only routes if (process.env.NODE_ENV !== 'production') { app.use('/api', require('./routes/test-helpers')); app.use('/api', require('./routes/admin-migrate')); } // 404 handler app.use((req, res) => { res.status(404).json({ error: 'Not found' }); }); // Error handler app.use((err, req, res, _next) => { logger.log({ level: 'ERROR', source: 'server', message: 'Unhandled error', error: err.message, stack: err.stack, path: req.path, method: req.method, }); res.status(500).json({ error: 'Internal server error' }); }); // Start server app.listen(PORT, () => { // eslint-disable-next-line no-console console.log(`ShyTalk API listening on port ${PORT}`); startCronJobs(); }); |