Sistemato attività
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { Request, Response, NextFunction, Router } from 'express';
|
||||
import { HttpError } from '../../errors';
|
||||
import * as notificheService from './notifiche.service';
|
||||
|
||||
function asyncHandler(
|
||||
handler: (req: Request, res: Response, next: NextFunction) => Promise<void>,
|
||||
) {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
handler(req, res, next).catch(next);
|
||||
};
|
||||
}
|
||||
|
||||
function parseId(req: Request, next: NextFunction): number | undefined {
|
||||
const id = Number(req.params.id);
|
||||
if (!Number.isInteger(id)) {
|
||||
next(new HttpError(400, "l'id deve essere un numero intero"));
|
||||
return undefined;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
export const notificheRouter = Router();
|
||||
|
||||
notificheRouter.get(
|
||||
'/',
|
||||
asyncHandler(async (req, res) => {
|
||||
res.status(200).json(await notificheService.listNotifiche(req.auth!));
|
||||
}),
|
||||
);
|
||||
|
||||
notificheRouter.get(
|
||||
'/non-lette/count',
|
||||
asyncHandler(async (req, res) => {
|
||||
res.status(200).json({ count: await notificheService.countNonLette(req.auth!) });
|
||||
}),
|
||||
);
|
||||
|
||||
notificheRouter.put(
|
||||
'/letta-tutte',
|
||||
asyncHandler(async (req, res) => {
|
||||
await notificheService.segnaTutteLette(req.auth!);
|
||||
res.status(204).end();
|
||||
}),
|
||||
);
|
||||
|
||||
notificheRouter.put(
|
||||
'/:id/letta',
|
||||
asyncHandler(async (req, res, next) => {
|
||||
const id = parseId(req, next);
|
||||
if (id === undefined) return;
|
||||
|
||||
await notificheService.segnaLetta(id, req.auth!);
|
||||
res.status(204).end();
|
||||
}),
|
||||
);
|
||||
|
||||
export default notificheRouter;
|
||||
@@ -0,0 +1,95 @@
|
||||
import { TipoNotifica } from '@prisma/client';
|
||||
import { prisma } from '../../db/prisma';
|
||||
import { HttpError } from '../../errors';
|
||||
import { AuthContext } from '../../middlewares/auth.types';
|
||||
import { NotificaDto } from '../../types/dto';
|
||||
|
||||
const RUOLI_MODERAZIONE = ['admin', 'moderatore'];
|
||||
|
||||
function isModeratore(auth: AuthContext): boolean {
|
||||
return RUOLI_MODERAZIONE.some((ruolo) => auth.roles.includes(ruolo));
|
||||
}
|
||||
|
||||
function toDto(entity: {
|
||||
id: number;
|
||||
tipo: TipoNotifica;
|
||||
messaggio: string;
|
||||
link: string | null;
|
||||
letta: boolean;
|
||||
dataCreazione: Date;
|
||||
}): NotificaDto {
|
||||
return {
|
||||
id: entity.id,
|
||||
tipo: entity.tipo,
|
||||
messaggio: entity.messaggio,
|
||||
link: entity.link,
|
||||
letta: entity.letta,
|
||||
dataCreazione: entity.dataCreazione,
|
||||
};
|
||||
}
|
||||
|
||||
// Le notifiche "broadcast" (destinatarioId null) sono la coda di moderazione condivisa tra
|
||||
// admin/moderatore: sono visibili solo a chi ha uno di questi ruoli, oltre alle proprie
|
||||
// notifiche personali.
|
||||
function whereVisibili(auth: AuthContext) {
|
||||
return isModeratore(auth)
|
||||
? { OR: [{ destinatarioId: auth.userId }, { destinatarioId: null }] }
|
||||
: { destinatarioId: auth.userId };
|
||||
}
|
||||
|
||||
export async function listNotifiche(auth: AuthContext): Promise<NotificaDto[]> {
|
||||
const entities = await prisma.notifica.findMany({
|
||||
where: whereVisibili(auth),
|
||||
orderBy: { dataCreazione: 'desc' },
|
||||
take: 50,
|
||||
});
|
||||
return entities.map(toDto);
|
||||
}
|
||||
|
||||
export async function countNonLette(auth: AuthContext): Promise<number> {
|
||||
return prisma.notifica.count({ where: { ...whereVisibili(auth), letta: false } });
|
||||
}
|
||||
|
||||
export async function segnaLetta(id: number, auth: AuthContext): Promise<void> {
|
||||
const entity = await prisma.notifica.findUnique({ where: { id } });
|
||||
if (!entity) {
|
||||
throw new HttpError(404, 'notifica non trovata');
|
||||
}
|
||||
const visibile =
|
||||
entity.destinatarioId === auth.userId || (entity.destinatarioId === null && isModeratore(auth));
|
||||
if (!visibile) {
|
||||
throw new HttpError(403, 'non puoi accedere a questa notifica');
|
||||
}
|
||||
|
||||
await prisma.notifica.update({ where: { id }, data: { letta: true } });
|
||||
}
|
||||
|
||||
export async function segnaTutteLette(auth: AuthContext): Promise<void> {
|
||||
await prisma.notifica.updateMany({
|
||||
where: { ...whereVisibili(auth), letta: false },
|
||||
data: { letta: true },
|
||||
});
|
||||
}
|
||||
|
||||
// Notifica destinata a chiunque abbia ruolo admin/moderatore (coda di moderazione condivisa,
|
||||
// vedi whereVisibili): usata per segnalare nuove proposte/attività in attesa di revisione.
|
||||
export async function creaBroadcastModerazione(
|
||||
tipo: TipoNotifica,
|
||||
messaggio: string,
|
||||
link?: string,
|
||||
): Promise<void> {
|
||||
await prisma.notifica.create({
|
||||
data: { tipo, messaggio, link: link ?? null, destinatarioId: null },
|
||||
});
|
||||
}
|
||||
|
||||
export async function creaPersonale(
|
||||
tipo: TipoNotifica,
|
||||
messaggio: string,
|
||||
destinatarioId: string,
|
||||
link?: string,
|
||||
): Promise<void> {
|
||||
await prisma.notifica.create({
|
||||
data: { tipo, messaggio, link: link ?? null, destinatarioId },
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user