full
This commit is contained in:
@@ -1,6 +1,17 @@
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const jwt = require('jsonwebtoken')
|
||||
const NotificationModel = require('../../../models/NotificationModel')
|
||||
const jMoment = require('moment-jalaali')
|
||||
const { getOrSetCache, invalidateCache } = require('../../../utils/cache')
|
||||
|
||||
function unreadNotificationsCacheKey (userId) {
|
||||
return `unread:notifications:${userId}`
|
||||
}
|
||||
// شمارندهی نوتیفهای نخونده خیلی پرتکرار خونده میشه (بج روی آیکون زنگوله، تقریباً
|
||||
// هر بار باز شدنِ اپ) — TTLِ کوتاه بهجای شمارندهی incremental، چون نوتیفهای جدید از
|
||||
// دهها نقطهی مختلفِ کد (لایک/کامنت/فالو/...) ساخته میشن و ردیابیِ دقیقِ همهشون
|
||||
// برای invalidate کردن عملی نیست؛ فقط نقطهی "خوندهشدنِ همه" (پایین) صریحاً invalidate میشه
|
||||
const UNREAD_NOTIFICATIONS_CACHE_TTL = 20
|
||||
|
||||
const getNotifications = async (req, res, next) => {
|
||||
try {
|
||||
@@ -28,15 +39,43 @@ const getNotifications = async (req, res, next) => {
|
||||
let notifications = await NotificationModel.paginate(filter, options)
|
||||
const totalPages = notifications.totalPages
|
||||
const totalItems = notifications.totalDocs
|
||||
const actorIds = [
|
||||
...new Set(
|
||||
notifications.docs
|
||||
.map((n) => n.actor_id)
|
||||
.filter(Boolean)
|
||||
.map(String)
|
||||
)
|
||||
]
|
||||
const actors = actorIds.length
|
||||
? await UserModel.find({ _id: { $in: actorIds } })
|
||||
.select('user_name profile_image first_name last_name')
|
||||
.lean()
|
||||
: []
|
||||
const actorMap = new Map(actors.map((a) => [String(a._id), a]))
|
||||
|
||||
notifications = notifications?.docs.map(notification => {
|
||||
const jDate = jMoment(notification.createdAt).format('jYYYY-jMM-jDD HH:mm')
|
||||
const actorDoc = notification.actor_id
|
||||
? actorMap.get(String(notification.actor_id))
|
||||
: null
|
||||
return {
|
||||
...notification._doc,
|
||||
createdAt: jDate
|
||||
createdAt: jDate,
|
||||
actor: actorDoc
|
||||
? {
|
||||
_id: String(actorDoc._id),
|
||||
user_name: actorDoc.user_name,
|
||||
profile_image: actorDoc.profile_image,
|
||||
first_name: actorDoc.first_name,
|
||||
last_name: actorDoc.last_name
|
||||
}
|
||||
: null
|
||||
}
|
||||
})
|
||||
// تغییر وضعیت "read" اعلانها به "true"
|
||||
await NotificationModel.updateMany({ user_id: userId }, { read: true })
|
||||
invalidateCache(unreadNotificationsCacheKey(userId)).catch(() => {})
|
||||
res.json({
|
||||
notifications,
|
||||
totalPages, // ارسال تعداد کل صفحات
|
||||
@@ -55,7 +94,11 @@ const unreadNotifications = async (req, res, next) => {
|
||||
const userId = decodedToken.id
|
||||
|
||||
// دریافت تعداد اعلانهای جدید برای کاربر
|
||||
const newNotificationsCount = await NotificationModel.countDocuments({ user_id: userId, read: false }).exec()
|
||||
const newNotificationsCount = await getOrSetCache(
|
||||
unreadNotificationsCacheKey(userId),
|
||||
UNREAD_NOTIFICATIONS_CACHE_TTL,
|
||||
() => NotificationModel.countDocuments({ user_id: userId, read: false }).exec()
|
||||
)
|
||||
|
||||
res.json({ newNotificationsCount })
|
||||
} catch (error) {
|
||||
@@ -63,6 +106,77 @@ const unreadNotifications = async (req, res, next) => {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getNotifications, unreadNotifications
|
||||
const registerPushToken = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
const userId = decodedToken.id
|
||||
const { fcmToken, platform } = req.body
|
||||
if (!fcmToken) return res.status(400).json({ message: 'fcmToken الزامی است' })
|
||||
|
||||
// توکنهای وب (مرورگر) جدا از توکنهای اندروید ذخیره میشن
|
||||
const field = platform === 'web' ? 'fcm_web_tokens' : 'fcm_tokens'
|
||||
|
||||
// یه توکنِ دستگاه (fcmToken) مختص یه اکانتِ خاص نیست — وقتی کاربر از یه اکانت خارج
|
||||
// و با اکانت دیگهای وارد میشه، همون توکن قبلی دوباره ثبت میشه، ولی تا الان از
|
||||
// داکیومنت اکانت قبلی هیچوقت پاک نمیشد؛ نتیجهش این بود که اعلانهای اکانت قبلی
|
||||
// هم همچنان روی همین دستگاه میرسید. اول از همهی کاربرهای دیگه پاک میشه
|
||||
await UserModel.updateMany(
|
||||
{ _id: { $ne: userId } },
|
||||
{ $pull: { fcm_tokens: fcmToken, fcm_web_tokens: fcmToken } }
|
||||
)
|
||||
|
||||
// اضافهکردن توکن جدید و نگهداشتن حداکثر ۵ توکن آخر (چند دستگاه، ولی بدون رشد بینهایت)
|
||||
await UserModel.updateOne({ _id: userId }, { $pull: { [field]: fcmToken } })
|
||||
await UserModel.updateOne(
|
||||
{ _id: userId },
|
||||
{ $push: { [field]: { $each: [fcmToken], $slice: -5 } } }
|
||||
)
|
||||
|
||||
res.json({ ok: true })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const markBroadcastSeen = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
const userId = decodedToken.id
|
||||
const { id } = req.params
|
||||
|
||||
await NotificationModel.updateOne(
|
||||
{ user_id: userId, broadcast_id: id },
|
||||
{ push_delivered_seen: true }
|
||||
)
|
||||
res.json({ ok: true })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const markBroadcastClicked = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
const userId = decodedToken.id
|
||||
const { id } = req.params
|
||||
|
||||
await NotificationModel.updateOne(
|
||||
{ user_id: userId, broadcast_id: id },
|
||||
{ push_clicked: true }
|
||||
)
|
||||
res.json({ ok: true })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getNotifications, unreadNotifications, registerPushToken, markBroadcastSeen, markBroadcastClicked
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user