const bodyParser = require('body-parser') const session = require('express-session') const { RedisStore } = require('connect-redis') const { getRedisClient, getRawClient } = require('../utils/redisClient') const lastOnline = require('./lastOnline') const SESSION_OPTIONS = { secret: process.env.SESSION_SECRET || process.env.JWT_SECRET || 'change-me-in-production', resave: false, saveUninitialized: false, cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', }, } // قبلاً MemoryStoreِ پیش‌فرضِ express-session بود — برای production مناسب نیست (نه // بین چند اینستنس‌سرور مشترکه، نه محدودیتی داره). الان وقتی Redis وصله از یه store // مشترک استفاده می‌شه. عین ratedLimiter (createRateLimiter.js): دو نمونه‌ی کاملاً // جدا ساخته می‌شه و روی *هر* درخواست بر اساسِ آماده‌بودنِ فعلیِ Redis سوییچ می‌شه — // نه اینکه همیشه به Redis وصل بشه و فقط منتظرِ خطا بمونه. اون روش (client خام، // بدون چکِ آماده‌بودن) وقتی Redis اصلاً هیچ‌وقت در دسترس نبود (مثلاً یه دیپلویِ // تازه که هنوز Redis روش ست نشده)، هر درخواست رو تا انقضای connectTimeout کند // می‌کرد؛ این نسخه اون تأخیر رو صفر می‌کنه چون اصلاً منتظرِ Redis نمی‌مونه const redisSession = session({ ...SESSION_OPTIONS, store: new RedisStore({ client: getRawClient(), prefix: 'sess:' }), }) const memorySession = session(SESSION_OPTIONS) // نکته: اگه Redis وسطِ یه session بیفته (بعد از ساخته‌شدن یه سشنِ Redis-محور)، // سوییچ به memorySession یعنی اون سشنِ خاص موقتاً دیده نمی‌شه — قابل‌قبوله چون این // اپ اصلی‌ترین احرازهویتش JWTه، نه سشن (req.session جای دیگه‌ای توی این کدبیس // استفاده نمی‌شه) function dispatchSession (req, res, next) { const store = getRedisClient() ? redisSession : memorySession store(req, res, (err) => { if (err) { // fallback نهایی برای همون ریسِ نادرِ «Redis درست وسطِ همین درخواست قطع شد» — // به‌جای next(err) (که express-session پیش‌فرضش ۵۰۰-کردنِ کل درخواسته)، فقط // لاگ می‌کنیم و بدون سشن ادامه می‌دیم console.error('[session] store error — continuing without session:', err.message) return next() } next() }) } module.exports = (app) => { // express-form-data قبلاً اینجا global و بدون هیچ استثنایی نصب بود — بدنه‌ی هر // درخواست multipart رو (پست/استوری ویدیو و هر آپلودِ دیگه) کامل می‌خوند و مصرف // می‌کرد، درحالی‌که هیچ‌جای کدبیس از خروجیِ خودش استفاده نمی‌کرد؛ فقط باعث می‌شد // وقتی نوبت به multer (همونی که این روت‌ها واقعاً استفاده می‌کنن) می‌رسید، دیگه // هیچ بایتی از بدنه باقی نمونده باشه (busboy با «Unexpected end of form» شکست می‌خورد) app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })) app.use(dispatchSession) app.use(lastOnline) }