Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-07-12 12:39:17 +08:00
parent 82ea9c77c4
commit ea3e82e54c
47 changed files with 1301 additions and 481 deletions
+53 -11
View File
@@ -12,7 +12,44 @@
* Date Created: Jun. 19, 2026
***********************************************************************************************************************************************************************/
const AdminNotification = require('../../models/notifications/admin_notification.mdl');
const StickyBannerSetting = require('../../models/notifications/sticky_banner_setting.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 = 3;
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.
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;
}
// One shared banner image for the whole rotating sticky bar (see
// controllers/admin/notificationBroadcasts.controller.js's
// getStickyBannerSetting/updateStickyBannerSetting) — not per-announcement.
async function resolveSharedBannerImage(req) {
const setting = await StickyBannerSetting.findOne({ where: { id: 1 }, include: [IMAGE_INCLUDE] });
if (!setting?.image) return null;
const image = setting.toJSON().image;
await attachImageStreamToken(image, req);
return image;
}
// ─── GET /admin/notifications ─────────────────────────────────────────────────
async function list(req, res) {
@@ -25,7 +62,7 @@ async function list(req, res) {
order: [['createdAt', 'DESC']],
limit,
offset,
where: { show_in_notifications: true },
where: { show_in_notifications: true, ...notInFutureOrExpired() },
});
return R.success(res, 'Notifications fetched.', {
@@ -41,7 +78,7 @@ async function list(req, res) {
// ─── GET /admin/notifications/unseen ─────────────────────────────────────────
async function unseenCount(req, res) {
try {
const count = await AdminNotification.count({ where: { seen: false, show_in_notifications: true } });
const count = await AdminNotification.count({ where: { seen: false, show_in_notifications: true, ...notInFutureOrExpired() } });
return R.success(res, 'Unseen count fetched.', { count });
} catch (err) {
console.error('[NOTIFICATION] unseenCount error:', err);
@@ -54,16 +91,21 @@ async function unseenCount(req, res) {
// banner for every admin. Whoever dismisses it first dismisses it for all.
async function stickyAnnouncement(req, res) {
try {
const notification = await AdminNotification.findOne({
where: {
seen: false,
show_in_sticky: true,
type: 'announcement',
},
order: [['createdAt', 'DESC']],
});
const [notifications, bannerImage] = await Promise.all([
AdminNotification.findAll({
where: {
seen: false,
show_in_sticky: true,
type: 'announcement',
...notInFutureOrExpired(),
},
order: [['createdAt', 'DESC']],
limit: STICKY_LIMIT,
}),
resolveSharedBannerImage(req),
]);
return R.success(res, 'Sticky announcement fetched.', { announcement: notification });
return R.success(res, 'Sticky announcements fetched.', { announcements: notifications, bannerImage });
} catch (err) {
console.error('[NOTIFICATION] stickyAnnouncement error:', err);
return R.error(res, 'Failed to fetch sticky announcement.');