Restrict cockpit summary to own data + shared areas for tenants
This commit is contained in:
parent
ffea782e96
commit
efcbcab0d8
@ -1,22 +1,15 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
import { Router, Response } from 'express';
|
||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
import { PaymentService } from '../modules/banking/paymentService';
|
import { PaymentService } from '../modules/banking/paymentService';
|
||||||
import { requireAuth } from '../middleware/auth';
|
import { AuthedRequest, requireAuth } from '../middleware/auth';
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
const paymentService = new PaymentService(prisma);
|
const paymentService = new PaymentService(prisma);
|
||||||
|
|
||||||
/** Router für das Vermieter-Cockpit-Dashboard (aggregierte Übersichtsdaten). */
|
/** Router für das Cockpit-Dashboard (aggregierte Übersichtsdaten). */
|
||||||
export const cockpitRouter = Router();
|
export const cockpitRouter = Router();
|
||||||
|
|
||||||
// Erfordert Login: sensible Miet- und Kontaktdaten dürfen nicht öffentlich sein.
|
const TICKET_SELECT = {
|
||||||
cockpitRouter.get('/cockpit/summary', requireAuth, async (_req: Request, res: Response) => {
|
|
||||||
const [trafficLights, openTickets, rooms] = await Promise.all([
|
|
||||||
paymentService.getRentTrafficLights(),
|
|
||||||
prisma.ticket.findMany({
|
|
||||||
where: { status: { in: ['OPEN', 'IN_PROGRESS'] } },
|
|
||||||
orderBy: { createdAt: 'desc' },
|
|
||||||
select: {
|
|
||||||
id: true,
|
id: true,
|
||||||
title: true,
|
title: true,
|
||||||
category: true,
|
category: true,
|
||||||
@ -24,7 +17,25 @@ cockpitRouter.get('/cockpit/summary', requireAuth, async (_req: Request, res: Re
|
|||||||
status: true,
|
status: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
room: { select: { roomNumber: true } },
|
room: { select: { roomNumber: true } },
|
||||||
},
|
creator: { select: { id: true, fullName: true } },
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// Erfordert Login: sensible Miet- und Kontaktdaten dürfen nicht öffentlich sein.
|
||||||
|
//
|
||||||
|
// Berechtigungsregel: Mieter dürfen NUR ihre eigenen Miet-/Vertragsdaten und
|
||||||
|
// Tickets sehen (plus Gemeinschaftsbereich-Tickets ohne Zimmerzuordnung) —
|
||||||
|
// niemals die Miet-/Kontaktdaten oder privaten Zimmer-Tickets anderer Mieter.
|
||||||
|
// Vermieter/Admin sehen weiterhin die vollständige Übersicht über die WG.
|
||||||
|
cockpitRouter.get('/cockpit/summary', requireAuth, async (req: AuthedRequest, res: Response) => {
|
||||||
|
const isLandlord = req.user!.role === 'LANDLORD' || req.user!.role === 'ADMIN';
|
||||||
|
|
||||||
|
if (isLandlord) {
|
||||||
|
const [trafficLights, openTickets, rooms] = await Promise.all([
|
||||||
|
paymentService.getRentTrafficLights(),
|
||||||
|
prisma.ticket.findMany({
|
||||||
|
where: { status: { in: ['OPEN', 'IN_PROGRESS'] } },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
select: TICKET_SELECT,
|
||||||
}),
|
}),
|
||||||
prisma.room.findMany({
|
prisma.room.findMany({
|
||||||
select: { id: true, roomNumber: true, sizeSqm: true, status: true },
|
select: { id: true, roomNumber: true, sizeSqm: true, status: true },
|
||||||
@ -39,7 +50,8 @@ cockpitRouter.get('/cockpit/summary', requireAuth, async (_req: Request, res: Re
|
|||||||
totalWarmRentSum += light.amountDue;
|
totalWarmRentSum += light.amountDue;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({
|
return res.status(200).json({
|
||||||
|
role: req.user!.role,
|
||||||
generatedAt: new Date().toISOString(),
|
generatedAt: new Date().toISOString(),
|
||||||
rooms,
|
rooms,
|
||||||
trafficLights,
|
trafficLights,
|
||||||
@ -53,4 +65,48 @@ cockpitRouter.get('/cockpit/summary', requireAuth, async (_req: Request, res: Re
|
|||||||
},
|
},
|
||||||
openTickets,
|
openTickets,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Mieter-Ansicht: nur eigene Daten + Gemeinschaftsbereich ---
|
||||||
|
const me = await prisma.user.findUnique({
|
||||||
|
where: { id: req.user!.id },
|
||||||
|
select: { roomId: true },
|
||||||
|
});
|
||||||
|
const ownRoomId = me?.roomId ?? null;
|
||||||
|
|
||||||
|
const [allTrafficLights, openTickets] = await Promise.all([
|
||||||
|
paymentService.getRentTrafficLights(),
|
||||||
|
prisma.ticket.findMany({
|
||||||
|
where: {
|
||||||
|
status: { in: ['OPEN', 'IN_PROGRESS'] },
|
||||||
|
OR: [
|
||||||
|
{ creatorId: req.user!.id },
|
||||||
|
{ roomId: null },
|
||||||
|
...(ownRoomId ? [{ roomId: ownRoomId }] : []),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
select: TICKET_SELECT,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Eigenen aktiven Vertrag laden, um die passende Ampel aus der Gesamtliste herauszufiltern
|
||||||
|
// (getRentTrafficLights liefert alle Verträge — hier wird strikt auf den eigenen reduziert).
|
||||||
|
const myContract = await prisma.contract.findFirst({
|
||||||
|
where: { userId: req.user!.id, isActive: true },
|
||||||
|
include: { room: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
let myRent = null;
|
||||||
|
if (myContract) {
|
||||||
|
myRent = allTrafficLights.find((l) => l.contractId === myContract.id) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
role: req.user!.role,
|
||||||
|
generatedAt: new Date().toISOString(),
|
||||||
|
myRoom: myContract?.room ? { id: myContract.room.id, roomNumber: myContract.room.roomNumber } : null,
|
||||||
|
myRent,
|
||||||
|
openTickets,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user