const express = require("express") const multer = require("multer") const requireAuth = require("../middleware/requireAuth.js") const { getUserProfile, markAllNotificationsRead, getUserNotificationsPage, changePassword, } = require("../services/profile.service") const { endpoints } = require("@shared/contracts") const router = express.Router() const upload = multer({ dest: "uploads/" }) const { updateUserAvatar } = require("../services/avatar.service") const { enqueueAuditFromRequest, buildAuditMeta } = require("../services/audit.service") const { AUDIT_ACTIONS } = require("../services/auditActions") const { account } = endpoints function attachNotificationExtras(validatedList = [], sourceList = []) { const extrasById = new Map( (Array.isArray(sourceList) ? sourceList : []).map((item) => [ Number(item?.id), item?.extras ?? null, ]) ) return (Array.isArray(validatedList) ? validatedList : []).map((item) => ({ ...item, extras: extrasById.get(Number(item?.id)) ?? null, })) } router.post( "/avatar", requireAuth, upload.single("file"), async (req, res) => { try { const updatedUser = await updateUserAvatar(req.auth.userId, req.file) enqueueAuditFromRequest( req, AUDIT_ACTIONS.ACCOUNT.AVATAR_UPDATE, buildAuditMeta({ entityType: "USER", entityId: req.auth.userId, after: { avatarUrl: updatedUser.avatarUrl ?? null }, }) ) res.json( account.avatarUploadResponseSchema.parse({ message: "Avatar updated", user: updatedUser, }) ) } catch (err) { console.error(err) res.status(400).json({ error: err.message }) } } ) router.get("/me", requireAuth, async (req, res) => { try { const user = await getUserProfile(req.auth.userId) const payload = account.accountMeResponseSchema.parse(user) payload.notifications = attachNotificationExtras(payload.notifications, user?.notifications) res.json(payload) } catch (err) { res.status(400).json({ error: err.message }) } }) router.get("/notifications/read", requireAuth, async (req, res) => { try { await markAllNotificationsRead(req.auth.userId) enqueueAuditFromRequest( req, AUDIT_ACTIONS.ACCOUNT.NOTIFICATIONS_READ, buildAuditMeta({ entityType: "USER", entityId: req.auth.userId, extra: { action: "mark_all_read" }, }) ) res.sendStatus(200) } catch (err) { res.status(400).json({ error: err.message }) } }) router.get("/notifications", requireAuth, async (req, res) => { try { const input = account.accountNotificationsListRequestSchema.parse(req.query) const payload = await getUserNotificationsPage(req.auth.userId, input.page, 10) const validated = account.accountNotificationsListResponseSchema.parse(payload) validated.results = attachNotificationExtras(validated.results, payload?.results) res.json(validated) } catch (err) { res.status(400).json({ error: err.message }) } }) router.post("/password", requireAuth, async (req, res) => { try { const input = account.accountPasswordChangeRequestSchema.parse(req.body) const payload = await changePassword(req.auth.userId, input) enqueueAuditFromRequest( req, AUDIT_ACTIONS.ACCOUNT.PASSWORD_CHANGE, buildAuditMeta({ entityType: "USER", entityId: req.auth.userId, }) ) res.json(account.accountPasswordChangeResponseSchema.parse(payload)) } catch (err) { res.status(400).json({ error: err.message }) } }) module.exports = router