const { createClient } = require('redis') // یک کلاینتِ Redis مشترک برای کل اپ (presence، کش، rate-limit، session) — Redis // اختیاریه: اگه سرورش در دسترس نباشه (چه موقع boot، چه بعداً قطع بشه)، هیچ‌جای اپ // نباید کرش کنه یا هنگ کنه. برای همین getRedisClient() به‌جای خودِ کلاینت، فقط وقتی // واقعاً "ready"ه اون رو برمی‌گردونه؛ صدازننده‌ها همیشه باید مسیرِ fallback // (مستقیم Mongo/memory) رو هم داشته باشن، دقیقاً عین چیزی که توی lastOnline.js و // middlewares/index.js پیاده شده let client = null let isReady = false function buildRedisUrl () { if (process.env.REDIS_URL) return process.env.REDIS_URL const host = process.env.REDIS_HOST || '127.0.0.1' const port = process.env.REDIS_PORT || '6379' const password = process.env.REDIS_PASSWORD || '' const db = process.env.REDIS_DB || '0' const auth = password ? `:${encodeURIComponent(password)}@` : '' return `redis://${auth}${host}:${port}/${db}` } function getClient () { if (client) return client client = createClient({ url: buildRedisUrl(), socket: { // بک‌آف نمایی با سقف ۱۰ ثانیه — تا ابد retry می‌کنه (وقتی سرورِ Redis بعداً بالا // اومد خودش وصل می‌شه)، ولی هیچ‌وقت کل اپ رو برای اتصال بلاک نمی‌کنه چون // connectRedis() پایین await نمی‌شه reconnectStrategy: (retries) => Math.min(retries * 200, 10000), // بدون این، وقتی REDIS_HOST به یه آدرسِ اشتباه/غیرقابل‌دسترس (نه صرفاً «رد شد»، // بلکه ساکت هیچ پاسخی نمی‌ده) اشاره کنه، تلاشِ اتصال می‌تونه تا timeoutِ پیش‌فرضِ // TCP سطحِ سیستم‌عامل (روی بعضی سیستم‌ها ۱۰۰+ ثانیه) طول بکشه — و هر درخواستی که // منتظرِ همون تلاشِ اتصاله (مثلاً session store) به همون اندازه کند می‌شه connectTimeout: 5000 } }) // کتابخونه‌ی redis v4+: بدون این listener، هر خطای اتصال کل پروسه‌ی Node رو // کرش می‌کنه (unhandled 'error' event) — این مهم‌ترین خطِ این فایله client.on('error', (err) => { isReady = false console.error('[redis] connection error:', err.message) }) client.on('ready', () => { isReady = true console.log('[redis] connected') }) client.on('end', () => { isReady = false }) return client } // عمداً await نمی‌شه توی boot — اگه Redis در دسترس نباشه، بوت‌شدنِ کل سرور نباید // معطلِ چندین‌بار retry بمونه؛ اتصال توی بک‌گراند انجام می‌شه و هر وقت آماده شد // isReady=true می‌شه function connectRedis () { const c = getClient() c.connect().catch((err) => { console.error('[redis] initial connection failed — app continues without Redis:', err.message) }) } // همه‌جای اپ باید از همین تابع استفاده کنن، نه مستقیم از getRawClient() — چون فقط // وقتی واقعاً متصله چیزی برمی‌گردونه function getRedisClient () { return isReady ? client : null } // فقط برای jaهایی مثل connect-redis که خودشون client رو نگه می‌دارن و باید همون // اول (حتی قبل از وصل‌شدن) بهش رفرنس بدیم — چون کتابخونه‌ی redis v4 دستورهای // قبل از اتصال رو صف می‌کنه (offline queue)، نه اینکه فوراً خطا بده function getRawClient () { return getClient() } module.exports = { connectRedis, getRedisClient, getRawClient }