mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
156 lines
7.0 KiB
JavaScript
156 lines
7.0 KiB
JavaScript
/***********************************************************************************************************************************************************************
|
|
* File Name : notification.controller.js
|
|
* Type : Controller (Client)
|
|
* Description : Per-user notification management.
|
|
* GET /client/notifications — paginated list for the auth user
|
|
* GET /client/notifications/unseen — unseen count
|
|
* PATCH /client/notifications/:id/seen — mark one as seen
|
|
* PATCH /client/notifications/seen-all — mark all as seen
|
|
* DELETE /client/notifications/clear-all — delete all notifications
|
|
*
|
|
* Author: Kenneth Obsequio (@lash0000)
|
|
* Date Created: Jun. 19, 2026
|
|
***********************************************************************************************************************************************************************/
|
|
const UserNotification = require('../../models/notifications/user_notification.mdl');
|
|
const mdl_Assets = require('../../models/assets/assets.mdl');
|
|
const mediaToken = require('../../services/mediaToken.service');
|
|
const R = require('../../utils/response.util');
|
|
const { notInFutureOrExpired } = require('../../utils/notificationVisibility.util');
|
|
|
|
const STICKY_LIMIT = 2;
|
|
const IMAGE_INCLUDE = {
|
|
model: mdl_Assets,
|
|
as: 'image',
|
|
attributes: ["asset_id", "display_name", "file_url", "thumbnail_url", "storage_provider", "storage_key", "file_type", "mime_type"],
|
|
required: false,
|
|
};
|
|
|
|
// Mirrors controllers/admin/advertisements.controller.js's attachImageStreamToken
|
|
// — kept duplicated rather than shared across the admin/client boundary.
|
|
async function attachImageStreamToken(image, req) {
|
|
if (!image || image.storage_provider !== 's3' || !mediaToken.SUPPORTED_TYPES.includes(image.file_type)) {
|
|
return image;
|
|
}
|
|
const ip = mediaToken.resolveIp(req);
|
|
const { token } = await mediaToken.issueForAsset(image, req.user?.user_id, ip);
|
|
image.stream_token = token;
|
|
image.file_url = null;
|
|
image.thumbnail_url = null;
|
|
delete image.storage_key;
|
|
return image;
|
|
}
|
|
|
|
// ─── GET /client/notifications ────────────────────────────────────────────────
|
|
async function list(req, res) {
|
|
try {
|
|
const userId = req.user.user_id;
|
|
const page = Math.max(1, parseInt(req.query.page) || 1);
|
|
const limit = Math.min(50, parseInt(req.query.limit) || 20);
|
|
const offset = (page - 1) * limit;
|
|
|
|
const { count, rows } = await UserNotification.findAndCountAll({
|
|
where: { user_id: userId, show_in_notifications: true, ...notInFutureOrExpired() },
|
|
order: [['createdAt', 'DESC']],
|
|
limit,
|
|
offset,
|
|
});
|
|
|
|
return R.success(res, 'Notifications fetched.', {
|
|
notifications: rows,
|
|
pagination: { page, limit, total: count, pages: Math.ceil(count / limit) },
|
|
});
|
|
} catch (err) {
|
|
console.error('[CLIENT NOTIFICATION] list error:', err);
|
|
return R.error(res, 'Failed to fetch notifications.');
|
|
}
|
|
}
|
|
|
|
// ─── GET /client/notifications/unseen ────────────────────────────────────────
|
|
async function unseenCount(req, res) {
|
|
if (!req.user) return R.success(res, 'Unseen count fetched.', { count: null });
|
|
try {
|
|
const count = await UserNotification.count({
|
|
where: { user_id: req.user.user_id, seen: false, show_in_notifications: true, ...notInFutureOrExpired() },
|
|
});
|
|
return R.success(res, 'Unseen count fetched.', { count });
|
|
} catch (err) {
|
|
console.error('[CLIENT NOTIFICATION] unseenCount error:', err);
|
|
return R.error(res, 'Failed to fetch unseen count.');
|
|
}
|
|
}
|
|
|
|
// ─── GET /client/notifications/sticky ─────────────────────────────────────
|
|
async function stickyAnnouncement(req, res) {
|
|
try {
|
|
const rows = await UserNotification.findAll({
|
|
where: {
|
|
user_id: req.user.user_id,
|
|
seen: false,
|
|
show_in_sticky: true,
|
|
type: "announcement",
|
|
...notInFutureOrExpired(),
|
|
},
|
|
include: [IMAGE_INCLUDE],
|
|
order: [["createdAt", "DESC"]],
|
|
limit: STICKY_LIMIT,
|
|
});
|
|
|
|
const notifications = await Promise.all(rows.map(async (row) => {
|
|
const json = row.toJSON();
|
|
if (json.image) await attachImageStreamToken(json.image, req);
|
|
return json;
|
|
}));
|
|
|
|
return R.success(res, "Sticky alerts fetched.", { announcements: notifications });
|
|
} catch (err) {
|
|
console.error("[CLIENT NOTIFICATION] stickyAnnouncement error:", err);
|
|
return R.error(res, "Failed to fetch sticky announcement.");
|
|
}
|
|
}
|
|
|
|
// ─── PATCH /client/notifications/:id/seen ────────────────────────────────────
|
|
async function markSeen(req, res) {
|
|
try {
|
|
const notification = await UserNotification.findOne({
|
|
where: { notification_id: req.params.id, user_id: req.user.user_id },
|
|
});
|
|
if (!notification) return R.error(res, 'Notification not found.', 404);
|
|
|
|
await notification.update({ seen: true, seen_at: new Date() });
|
|
return R.success(res, 'Notification marked as seen.', notification);
|
|
} catch (err) {
|
|
console.error('[CLIENT NOTIFICATION] markSeen error:', err);
|
|
return R.error(res, 'Failed to mark notification as seen.');
|
|
}
|
|
}
|
|
|
|
// ─── PATCH /client/notifications/seen-all ────────────────────────────────────
|
|
async function markAllSeen(req, res) {
|
|
try {
|
|
const now = new Date();
|
|
const [count] = await UserNotification.update(
|
|
{ seen: true, seen_at: now },
|
|
{ where: { user_id: req.user.user_id, seen: false } }
|
|
);
|
|
return R.success(res, `${count} notification(s) marked as seen.`, { count });
|
|
} catch (err) {
|
|
console.error('[CLIENT NOTIFICATION] markAllSeen error:', err);
|
|
return R.error(res, 'Failed to mark all notifications as seen.');
|
|
}
|
|
}
|
|
|
|
// ─── DELETE /client/notifications/clear-all ──────────────────────────────────
|
|
async function clearAll(req, res) {
|
|
try {
|
|
const count = await UserNotification.destroy({
|
|
where: { user_id: req.user.user_id },
|
|
});
|
|
return R.success(res, `${count} notification(s) cleared.`, { count });
|
|
} catch (err) {
|
|
console.error('[CLIENT NOTIFICATION] clearAll error:', err);
|
|
return R.error(res, 'Failed to clear notifications.');
|
|
}
|
|
}
|
|
|
|
module.exports = { list, unseenCount, stickyAnnouncement, markSeen, markAllSeen, clearAll };
|