99 lines
3.4 KiB
JavaScript
99 lines
3.4 KiB
JavaScript
|
|
// Notifications route — in-app notification management for users
|
||
|
|
// Provides unread notifications, counts, and mark-as-read operations.
|
||
|
|
|
||
|
|
const express = require('express');
|
||
|
|
const pool = require('../db');
|
||
|
|
const { requireAuth } = require('../middleware/auth');
|
||
|
|
|
||
|
|
function createNotificationsRouter() {
|
||
|
|
const router = express.Router();
|
||
|
|
|
||
|
|
// All routes require authentication
|
||
|
|
router.use(requireAuth());
|
||
|
|
|
||
|
|
/**
|
||
|
|
* GET /api/notifications
|
||
|
|
* Returns unread notifications for the current user, ordered by newest first.
|
||
|
|
* Limited to 50 results.
|
||
|
|
*/
|
||
|
|
router.get('/', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { rows } = await pool.query(
|
||
|
|
`SELECT id, type, title, message, issue_number, read, created_at
|
||
|
|
FROM notifications
|
||
|
|
WHERE username = $1 AND read = FALSE
|
||
|
|
ORDER BY created_at DESC
|
||
|
|
LIMIT 50`,
|
||
|
|
[req.user.username]
|
||
|
|
);
|
||
|
|
res.json(rows);
|
||
|
|
} catch (err) {
|
||
|
|
console.error('[Notifications] Error fetching notifications:', err.message);
|
||
|
|
res.status(500).json({ error: 'Failed to fetch notifications' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* GET /api/notifications/count
|
||
|
|
* Returns the unread notification count for the current user (for badge display).
|
||
|
|
*/
|
||
|
|
router.get('/count', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const { rows } = await pool.query(
|
||
|
|
`SELECT COUNT(*)::int AS unread
|
||
|
|
FROM notifications
|
||
|
|
WHERE username = $1 AND read = FALSE`,
|
||
|
|
[req.user.username]
|
||
|
|
);
|
||
|
|
res.json({ unread: rows[0].unread });
|
||
|
|
} catch (err) {
|
||
|
|
console.error('[Notifications] Error fetching count:', err.message);
|
||
|
|
res.status(500).json({ error: 'Failed to fetch notification count' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* PATCH /api/notifications/:id/read
|
||
|
|
* Marks a single notification as read. Only the owning user can mark their own.
|
||
|
|
*/
|
||
|
|
router.patch('/:id/read', async (req, res) => {
|
||
|
|
const { id } = req.params;
|
||
|
|
try {
|
||
|
|
const result = await pool.query(
|
||
|
|
`UPDATE notifications SET read = TRUE
|
||
|
|
WHERE id = $1 AND username = $2`,
|
||
|
|
[id, req.user.username]
|
||
|
|
);
|
||
|
|
if (result.rowCount === 0) {
|
||
|
|
return res.status(404).json({ error: 'Notification not found' });
|
||
|
|
}
|
||
|
|
res.json({ status: 'ok' });
|
||
|
|
} catch (err) {
|
||
|
|
console.error('[Notifications] Error marking read:', err.message);
|
||
|
|
res.status(500).json({ error: 'Failed to mark notification as read' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* POST /api/notifications/read-all
|
||
|
|
* Marks all notifications as read for the current user.
|
||
|
|
*/
|
||
|
|
router.post('/read-all', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const result = await pool.query(
|
||
|
|
`UPDATE notifications SET read = TRUE
|
||
|
|
WHERE username = $1 AND read = FALSE`,
|
||
|
|
[req.user.username]
|
||
|
|
);
|
||
|
|
res.json({ status: 'ok', marked: result.rowCount });
|
||
|
|
} catch (err) {
|
||
|
|
console.error('[Notifications] Error marking all read:', err.message);
|
||
|
|
res.status(500).json({ error: 'Failed to mark notifications as read' });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
return router;
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = createNotificationsRouter;
|