894 lines
22 KiB
TypeScript
894 lines
22 KiB
TypeScript
import { prisma } from "../database";
|
|
import type {
|
|
House,
|
|
HouseMembership,
|
|
HouseInvitation,
|
|
HouseRequest,
|
|
HouseRole,
|
|
InvitationStatus,
|
|
RequestStatus,
|
|
Prisma,
|
|
} from "@/prisma/generated/prisma/client";
|
|
import {
|
|
ValidationError,
|
|
NotFoundError,
|
|
ConflictError,
|
|
ForbiddenError,
|
|
} from "../errors";
|
|
|
|
const HOUSE_NAME_MIN_LENGTH = 3;
|
|
const HOUSE_NAME_MAX_LENGTH = 50;
|
|
const HOUSE_DESCRIPTION_MAX_LENGTH = 500;
|
|
|
|
export interface CreateHouseInput {
|
|
name: string;
|
|
description?: string | null;
|
|
creatorId: string;
|
|
}
|
|
|
|
export interface UpdateHouseInput {
|
|
name?: string;
|
|
description?: string | null;
|
|
}
|
|
|
|
export interface InviteUserInput {
|
|
houseId: string;
|
|
inviterId: string;
|
|
inviteeId: string;
|
|
}
|
|
|
|
export interface RequestToJoinInput {
|
|
houseId: string;
|
|
requesterId: string;
|
|
}
|
|
|
|
/**
|
|
* Service de gestion des maisons
|
|
*/
|
|
export class HouseService {
|
|
/**
|
|
* Récupère une maison par son ID
|
|
*/
|
|
async getHouseById(
|
|
id: string,
|
|
include?: Prisma.HouseInclude
|
|
): Promise<House | null> {
|
|
return prisma.house.findUnique({
|
|
where: { id },
|
|
include,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Récupère toutes les maisons avec pagination
|
|
*/
|
|
async getAllHouses(options?: {
|
|
skip?: number;
|
|
take?: number;
|
|
include?: Prisma.HouseInclude;
|
|
orderBy?: Prisma.HouseOrderByWithRelationInput;
|
|
}): Promise<House[]> {
|
|
return prisma.house.findMany({
|
|
skip: options?.skip,
|
|
take: options?.take,
|
|
include: options?.include,
|
|
orderBy: options?.orderBy || { createdAt: "desc" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Recherche des maisons par nom
|
|
*/
|
|
async searchHouses(
|
|
searchTerm: string,
|
|
options?: {
|
|
skip?: number;
|
|
take?: number;
|
|
include?: Prisma.HouseInclude;
|
|
}
|
|
): Promise<House[]> {
|
|
return prisma.house.findMany({
|
|
where: {
|
|
name: {
|
|
contains: searchTerm,
|
|
mode: "insensitive",
|
|
},
|
|
},
|
|
skip: options?.skip,
|
|
take: options?.take,
|
|
include: options?.include,
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Récupère les maisons d'un utilisateur
|
|
*/
|
|
async getUserHouses(
|
|
userId: string,
|
|
include?: Prisma.HouseInclude
|
|
): Promise<House[]> {
|
|
const memberships = await prisma.houseMembership.findMany({
|
|
where: { userId },
|
|
include: {
|
|
house: {
|
|
include: include,
|
|
},
|
|
},
|
|
});
|
|
|
|
return memberships.map((m) => m.house);
|
|
}
|
|
|
|
/**
|
|
* Récupère la maison d'un utilisateur (s'il en a une)
|
|
*/
|
|
async getUserHouse(
|
|
userId: string,
|
|
include?: Prisma.HouseInclude
|
|
): Promise<House | null> {
|
|
const membership = await prisma.houseMembership.findFirst({
|
|
where: { userId },
|
|
include: {
|
|
house: {
|
|
include: include,
|
|
},
|
|
},
|
|
orderBy: { joinedAt: "asc" }, // Première maison jointe
|
|
});
|
|
|
|
return membership?.house || null;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un utilisateur est membre d'une maison
|
|
*/
|
|
async isUserMemberOfHouse(
|
|
userId: string,
|
|
houseId: string
|
|
): Promise<boolean> {
|
|
const membership = await prisma.houseMembership.findUnique({
|
|
where: {
|
|
houseId_userId: {
|
|
houseId,
|
|
userId,
|
|
},
|
|
},
|
|
});
|
|
return !!membership;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un utilisateur est propriétaire ou admin d'une maison
|
|
*/
|
|
async isUserOwnerOrAdmin(
|
|
userId: string,
|
|
houseId: string
|
|
): Promise<boolean> {
|
|
const membership = await prisma.houseMembership.findUnique({
|
|
where: {
|
|
houseId_userId: {
|
|
houseId,
|
|
userId,
|
|
},
|
|
},
|
|
});
|
|
|
|
return membership?.role === "OWNER" || membership?.role === "ADMIN";
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un utilisateur est propriétaire d'une maison
|
|
*/
|
|
async isUserOwner(userId: string, houseId: string): Promise<boolean> {
|
|
const membership = await prisma.houseMembership.findUnique({
|
|
where: {
|
|
houseId_userId: {
|
|
houseId,
|
|
userId,
|
|
},
|
|
},
|
|
});
|
|
|
|
return membership?.role === "OWNER";
|
|
}
|
|
|
|
/**
|
|
* Récupère le rôle d'un utilisateur dans une maison
|
|
*/
|
|
async getUserRole(
|
|
userId: string,
|
|
houseId: string
|
|
): Promise<HouseRole | null> {
|
|
const membership = await prisma.houseMembership.findUnique({
|
|
where: {
|
|
houseId_userId: {
|
|
houseId,
|
|
userId,
|
|
},
|
|
},
|
|
});
|
|
|
|
return membership?.role || null;
|
|
}
|
|
|
|
/**
|
|
* Récupère les membres d'une maison
|
|
*/
|
|
async getHouseMembers(
|
|
houseId: string,
|
|
include?: Prisma.HouseMembershipInclude
|
|
): Promise<HouseMembership[]> {
|
|
return prisma.houseMembership.findMany({
|
|
where: { houseId },
|
|
include,
|
|
orderBy: [
|
|
{ role: "asc" }, // OWNER, ADMIN, MEMBER
|
|
{ joinedAt: "asc" },
|
|
],
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Crée une nouvelle maison
|
|
*/
|
|
async createHouse(data: CreateHouseInput): Promise<House> {
|
|
// Validation
|
|
if (!data.name || data.name.trim().length === 0) {
|
|
throw new ValidationError("Le nom de la maison est requis", "name");
|
|
}
|
|
|
|
if (
|
|
data.name.length < HOUSE_NAME_MIN_LENGTH ||
|
|
data.name.length > HOUSE_NAME_MAX_LENGTH
|
|
) {
|
|
throw new ValidationError(
|
|
`Le nom de la maison doit contenir entre ${HOUSE_NAME_MIN_LENGTH} et ${HOUSE_NAME_MAX_LENGTH} caractères`,
|
|
"name"
|
|
);
|
|
}
|
|
|
|
if (data.description && data.description.length > HOUSE_DESCRIPTION_MAX_LENGTH) {
|
|
throw new ValidationError(
|
|
`La description ne peut pas dépasser ${HOUSE_DESCRIPTION_MAX_LENGTH} caractères`,
|
|
"description"
|
|
);
|
|
}
|
|
|
|
// Vérifier si l'utilisateur est déjà dans une maison
|
|
const existingMembership = await prisma.houseMembership.findFirst({
|
|
where: { userId: data.creatorId },
|
|
});
|
|
|
|
if (existingMembership) {
|
|
throw new ConflictError(
|
|
"Vous êtes déjà membre d'une maison. Vous devez quitter votre maison actuelle avant d'en créer une nouvelle."
|
|
);
|
|
}
|
|
|
|
// Vérifier si le nom est déjà pris
|
|
const existingHouse = await prisma.house.findFirst({
|
|
where: {
|
|
name: {
|
|
equals: data.name.trim(),
|
|
mode: "insensitive",
|
|
},
|
|
},
|
|
});
|
|
|
|
if (existingHouse) {
|
|
throw new ConflictError("Ce nom de maison est déjà utilisé");
|
|
}
|
|
|
|
// Créer la maison et ajouter le créateur comme OWNER
|
|
return prisma.house.create({
|
|
data: {
|
|
name: data.name.trim(),
|
|
description: data.description?.trim() || null,
|
|
creatorId: data.creatorId,
|
|
memberships: {
|
|
create: {
|
|
userId: data.creatorId,
|
|
role: "OWNER",
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Met à jour une maison
|
|
*/
|
|
async updateHouse(
|
|
houseId: string,
|
|
userId: string,
|
|
data: UpdateHouseInput
|
|
): Promise<House> {
|
|
// Vérifier que l'utilisateur est propriétaire ou admin
|
|
const isAuthorized = await this.isUserOwnerOrAdmin(userId, houseId);
|
|
if (!isAuthorized) {
|
|
throw new ForbiddenError(
|
|
"Vous n'avez pas les permissions pour modifier cette maison"
|
|
);
|
|
}
|
|
|
|
const updateData: Prisma.HouseUpdateInput = {};
|
|
|
|
if (data.name !== undefined) {
|
|
if (!data.name || data.name.trim().length === 0) {
|
|
throw new ValidationError("Le nom de la maison est requis", "name");
|
|
}
|
|
|
|
if (
|
|
data.name.length < HOUSE_NAME_MIN_LENGTH ||
|
|
data.name.length > HOUSE_NAME_MAX_LENGTH
|
|
) {
|
|
throw new ValidationError(
|
|
`Le nom de la maison doit contenir entre ${HOUSE_NAME_MIN_LENGTH} et ${HOUSE_NAME_MAX_LENGTH} caractères`,
|
|
"name"
|
|
);
|
|
}
|
|
|
|
// Vérifier si le nom est déjà pris par une autre maison
|
|
const existingHouse = await prisma.house.findFirst({
|
|
where: {
|
|
name: {
|
|
equals: data.name.trim(),
|
|
mode: "insensitive",
|
|
},
|
|
NOT: { id: houseId },
|
|
},
|
|
});
|
|
|
|
if (existingHouse) {
|
|
throw new ConflictError("Ce nom de maison est déjà utilisé");
|
|
}
|
|
|
|
updateData.name = data.name.trim();
|
|
}
|
|
|
|
if (data.description !== undefined) {
|
|
if (data.description && data.description.length > HOUSE_DESCRIPTION_MAX_LENGTH) {
|
|
throw new ValidationError(
|
|
`La description ne peut pas dépasser ${HOUSE_DESCRIPTION_MAX_LENGTH} caractères`,
|
|
"description"
|
|
);
|
|
}
|
|
updateData.description = data.description?.trim() || null;
|
|
}
|
|
|
|
return prisma.house.update({
|
|
where: { id: houseId },
|
|
data: updateData,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Supprime une maison
|
|
*/
|
|
async deleteHouse(houseId: string, userId: string): Promise<void> {
|
|
// Vérifier que l'utilisateur est propriétaire
|
|
const isOwner = await this.isUserOwner(userId, houseId);
|
|
if (!isOwner) {
|
|
throw new ForbiddenError(
|
|
"Seul le propriétaire peut supprimer la maison"
|
|
);
|
|
}
|
|
|
|
await prisma.house.delete({
|
|
where: { id: houseId },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Invite un utilisateur à rejoindre une maison
|
|
*/
|
|
async inviteUser(data: InviteUserInput): Promise<HouseInvitation> {
|
|
// Vérifier que l'inviteur est membre de la maison
|
|
const isMember = await this.isUserMemberOfHouse(
|
|
data.inviterId,
|
|
data.houseId
|
|
);
|
|
if (!isMember) {
|
|
throw new ForbiddenError(
|
|
"Vous devez être membre de la maison pour inviter quelqu'un"
|
|
);
|
|
}
|
|
|
|
// Vérifier que l'invité n'est pas déjà membre
|
|
const isAlreadyMember = await this.isUserMemberOfHouse(
|
|
data.inviteeId,
|
|
data.houseId
|
|
);
|
|
if (isAlreadyMember) {
|
|
throw new ConflictError("Cet utilisateur est déjà membre de la maison");
|
|
}
|
|
|
|
// Vérifier qu'il n'y a pas déjà une invitation en attente
|
|
const existingInvitation = await prisma.houseInvitation.findUnique({
|
|
where: {
|
|
houseId_inviteeId: {
|
|
houseId: data.houseId,
|
|
inviteeId: data.inviteeId,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (existingInvitation && existingInvitation.status === "PENDING") {
|
|
throw new ConflictError(
|
|
"Une invitation est déjà en attente pour cet utilisateur"
|
|
);
|
|
}
|
|
|
|
// Vérifier que l'invité n'est pas déjà dans une autre maison
|
|
const existingMembership = await prisma.houseMembership.findFirst({
|
|
where: { userId: data.inviteeId },
|
|
});
|
|
|
|
if (existingMembership) {
|
|
throw new ConflictError(
|
|
"Cet utilisateur est déjà membre d'une autre maison"
|
|
);
|
|
}
|
|
|
|
// Créer l'invitation
|
|
return prisma.houseInvitation.create({
|
|
data: {
|
|
houseId: data.houseId,
|
|
inviterId: data.inviterId,
|
|
inviteeId: data.inviteeId,
|
|
status: "PENDING",
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Accepte une invitation
|
|
*/
|
|
async acceptInvitation(
|
|
invitationId: string,
|
|
userId: string
|
|
): Promise<HouseMembership> {
|
|
const invitation = await prisma.houseInvitation.findUnique({
|
|
where: { id: invitationId },
|
|
});
|
|
|
|
if (!invitation) {
|
|
throw new NotFoundError("Invitation");
|
|
}
|
|
|
|
if (invitation.inviteeId !== userId) {
|
|
throw new ForbiddenError("Cette invitation ne vous est pas destinée");
|
|
}
|
|
|
|
if (invitation.status !== "PENDING") {
|
|
throw new ConflictError("Cette invitation n'est plus valide");
|
|
}
|
|
|
|
// Vérifier que l'utilisateur n'est pas déjà dans une maison
|
|
const existingMembership = await prisma.houseMembership.findFirst({
|
|
where: { userId },
|
|
});
|
|
|
|
if (existingMembership) {
|
|
throw new ConflictError(
|
|
"Vous êtes déjà membre d'une maison. Vous devez quitter votre maison actuelle avant d'accepter cette invitation."
|
|
);
|
|
}
|
|
|
|
// Créer le membership et mettre à jour l'invitation
|
|
return prisma.$transaction(async (tx) => {
|
|
const membership = await tx.houseMembership.create({
|
|
data: {
|
|
houseId: invitation.houseId,
|
|
userId: invitation.inviteeId,
|
|
role: "MEMBER",
|
|
},
|
|
});
|
|
|
|
await tx.houseInvitation.update({
|
|
where: { id: invitationId },
|
|
data: { status: "ACCEPTED" },
|
|
});
|
|
|
|
// Annuler toutes les autres invitations en attente pour cet utilisateur
|
|
await tx.houseInvitation.updateMany({
|
|
where: {
|
|
inviteeId: userId,
|
|
status: "PENDING",
|
|
id: { not: invitationId },
|
|
},
|
|
data: { status: "CANCELLED" },
|
|
});
|
|
|
|
// Annuler toutes les demandes en attente pour cet utilisateur
|
|
await tx.houseRequest.updateMany({
|
|
where: {
|
|
requesterId: userId,
|
|
status: "PENDING",
|
|
},
|
|
data: { status: "CANCELLED" },
|
|
});
|
|
|
|
return membership;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Refuse une invitation
|
|
*/
|
|
async rejectInvitation(invitationId: string, userId: string): Promise<void> {
|
|
const invitation = await prisma.houseInvitation.findUnique({
|
|
where: { id: invitationId },
|
|
});
|
|
|
|
if (!invitation) {
|
|
throw new NotFoundError("Invitation");
|
|
}
|
|
|
|
if (invitation.inviteeId !== userId) {
|
|
throw new ForbiddenError("Cette invitation ne vous est pas destinée");
|
|
}
|
|
|
|
if (invitation.status !== "PENDING") {
|
|
throw new ConflictError("Cette invitation n'est plus valide");
|
|
}
|
|
|
|
await prisma.houseInvitation.update({
|
|
where: { id: invitationId },
|
|
data: { status: "REJECTED" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Annule une invitation (par l'inviteur)
|
|
*/
|
|
async cancelInvitation(invitationId: string, userId: string): Promise<void> {
|
|
const invitation = await prisma.houseInvitation.findUnique({
|
|
where: { id: invitationId },
|
|
});
|
|
|
|
if (!invitation) {
|
|
throw new NotFoundError("Invitation");
|
|
}
|
|
|
|
if (invitation.inviterId !== userId) {
|
|
throw new ForbiddenError(
|
|
"Vous ne pouvez annuler que vos propres invitations"
|
|
);
|
|
}
|
|
|
|
if (invitation.status !== "PENDING") {
|
|
throw new ConflictError("Cette invitation n'est plus valide");
|
|
}
|
|
|
|
await prisma.houseInvitation.update({
|
|
where: { id: invitationId },
|
|
data: { status: "CANCELLED" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Demande à rejoindre une maison
|
|
*/
|
|
async requestToJoin(data: RequestToJoinInput): Promise<HouseRequest> {
|
|
// Vérifier que l'utilisateur n'est pas déjà membre
|
|
const isMember = await this.isUserMemberOfHouse(
|
|
data.requesterId,
|
|
data.houseId
|
|
);
|
|
if (isMember) {
|
|
throw new ConflictError("Vous êtes déjà membre de cette maison");
|
|
}
|
|
|
|
// Vérifier que l'utilisateur n'est pas déjà dans une autre maison
|
|
const existingMembership = await prisma.houseMembership.findFirst({
|
|
where: { userId: data.requesterId },
|
|
});
|
|
|
|
if (existingMembership) {
|
|
throw new ConflictError(
|
|
"Vous êtes déjà membre d'une maison. Vous devez quitter votre maison actuelle avant de faire une demande."
|
|
);
|
|
}
|
|
|
|
// Vérifier qu'il n'y a pas déjà une demande en attente
|
|
const existingRequest = await prisma.houseRequest.findUnique({
|
|
where: {
|
|
houseId_requesterId: {
|
|
houseId: data.houseId,
|
|
requesterId: data.requesterId,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (existingRequest && existingRequest.status === "PENDING") {
|
|
throw new ConflictError(
|
|
"Une demande est déjà en attente pour cette maison"
|
|
);
|
|
}
|
|
|
|
// Créer la demande
|
|
return prisma.houseRequest.create({
|
|
data: {
|
|
houseId: data.houseId,
|
|
requesterId: data.requesterId,
|
|
status: "PENDING",
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Accepte une demande d'adhésion
|
|
*/
|
|
async acceptRequest(
|
|
requestId: string,
|
|
userId: string
|
|
): Promise<HouseMembership> {
|
|
const request = await prisma.houseRequest.findUnique({
|
|
where: { id: requestId },
|
|
include: { house: true },
|
|
});
|
|
|
|
if (!request) {
|
|
throw new NotFoundError("Demande");
|
|
}
|
|
|
|
// Vérifier que l'utilisateur est propriétaire ou admin de la maison
|
|
const isAuthorized = await this.isUserOwnerOrAdmin(
|
|
userId,
|
|
request.houseId
|
|
);
|
|
if (!isAuthorized) {
|
|
throw new ForbiddenError(
|
|
"Vous n'avez pas les permissions pour accepter cette demande"
|
|
);
|
|
}
|
|
|
|
if (request.status !== "PENDING") {
|
|
throw new ConflictError("Cette demande n'est plus valide");
|
|
}
|
|
|
|
// Vérifier que le demandeur n'est pas déjà dans une maison
|
|
const existingMembership = await prisma.houseMembership.findFirst({
|
|
where: { userId: request.requesterId },
|
|
});
|
|
|
|
if (existingMembership) {
|
|
throw new ConflictError(
|
|
"Cet utilisateur est déjà membre d'une autre maison"
|
|
);
|
|
}
|
|
|
|
// Créer le membership et mettre à jour la demande
|
|
return prisma.$transaction(async (tx) => {
|
|
const membership = await tx.houseMembership.create({
|
|
data: {
|
|
houseId: request.houseId,
|
|
userId: request.requesterId,
|
|
role: "MEMBER",
|
|
},
|
|
});
|
|
|
|
await tx.houseRequest.update({
|
|
where: { id: requestId },
|
|
data: { status: "ACCEPTED" },
|
|
});
|
|
|
|
// Annuler toutes les autres demandes en attente pour cet utilisateur
|
|
await tx.houseRequest.updateMany({
|
|
where: {
|
|
requesterId: request.requesterId,
|
|
status: "PENDING",
|
|
id: { not: requestId },
|
|
},
|
|
data: { status: "CANCELLED" },
|
|
});
|
|
|
|
// Annuler toutes les invitations en attente pour cet utilisateur
|
|
await tx.houseInvitation.updateMany({
|
|
where: {
|
|
inviteeId: request.requesterId,
|
|
status: "PENDING",
|
|
},
|
|
data: { status: "CANCELLED" },
|
|
});
|
|
|
|
return membership;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Refuse une demande d'adhésion
|
|
*/
|
|
async rejectRequest(requestId: string, userId: string): Promise<void> {
|
|
const request = await prisma.houseRequest.findUnique({
|
|
where: { id: requestId },
|
|
});
|
|
|
|
if (!request) {
|
|
throw new NotFoundError("Demande");
|
|
}
|
|
|
|
// Vérifier que l'utilisateur est propriétaire ou admin de la maison
|
|
const isAuthorized = await this.isUserOwnerOrAdmin(
|
|
userId,
|
|
request.houseId
|
|
);
|
|
if (!isAuthorized) {
|
|
throw new ForbiddenError(
|
|
"Vous n'avez pas les permissions pour refuser cette demande"
|
|
);
|
|
}
|
|
|
|
if (request.status !== "PENDING") {
|
|
throw new ConflictError("Cette demande n'est plus valide");
|
|
}
|
|
|
|
await prisma.houseRequest.update({
|
|
where: { id: requestId },
|
|
data: { status: "REJECTED" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Annule une demande (par le demandeur)
|
|
*/
|
|
async cancelRequest(requestId: string, userId: string): Promise<void> {
|
|
const request = await prisma.houseRequest.findUnique({
|
|
where: { id: requestId },
|
|
});
|
|
|
|
if (!request) {
|
|
throw new NotFoundError("Demande");
|
|
}
|
|
|
|
if (request.requesterId !== userId) {
|
|
throw new ForbiddenError(
|
|
"Vous ne pouvez annuler que vos propres demandes"
|
|
);
|
|
}
|
|
|
|
if (request.status !== "PENDING") {
|
|
throw new ConflictError("Cette demande n'est plus valide");
|
|
}
|
|
|
|
await prisma.houseRequest.update({
|
|
where: { id: requestId },
|
|
data: { status: "CANCELLED" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Quitte une maison
|
|
*/
|
|
async leaveHouse(houseId: string, userId: string): Promise<void> {
|
|
const membership = await prisma.houseMembership.findUnique({
|
|
where: {
|
|
houseId_userId: {
|
|
houseId,
|
|
userId,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!membership) {
|
|
throw new NotFoundError("Membre");
|
|
}
|
|
|
|
// Le propriétaire ne peut pas quitter sa maison
|
|
if (membership.role === "OWNER") {
|
|
throw new ForbiddenError(
|
|
"Le propriétaire ne peut pas quitter sa maison. Vous devez d'abord transférer la propriété ou supprimer la maison."
|
|
);
|
|
}
|
|
|
|
await prisma.houseMembership.delete({
|
|
where: {
|
|
houseId_userId: {
|
|
houseId,
|
|
userId,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Récupère les invitations reçues par un utilisateur
|
|
*/
|
|
async getUserInvitations(
|
|
userId: string,
|
|
status?: InvitationStatus
|
|
): Promise<HouseInvitation[]> {
|
|
return prisma.houseInvitation.findMany({
|
|
where: {
|
|
inviteeId: userId,
|
|
...(status && { status }),
|
|
},
|
|
include: {
|
|
house: true,
|
|
inviter: {
|
|
select: {
|
|
id: true,
|
|
username: true,
|
|
avatar: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Récupère les demandes d'une maison
|
|
*/
|
|
async getHouseRequests(
|
|
houseId: string,
|
|
status?: RequestStatus
|
|
): Promise<HouseRequest[]> {
|
|
return prisma.houseRequest.findMany({
|
|
where: {
|
|
houseId,
|
|
...(status && { status }),
|
|
},
|
|
include: {
|
|
requester: {
|
|
select: {
|
|
id: true,
|
|
username: true,
|
|
avatar: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Récupère les invitations envoyées par une maison
|
|
*/
|
|
async getHouseInvitations(
|
|
houseId: string,
|
|
status?: InvitationStatus
|
|
): Promise<HouseInvitation[]> {
|
|
return prisma.houseInvitation.findMany({
|
|
where: {
|
|
houseId,
|
|
...(status && { status }),
|
|
},
|
|
include: {
|
|
invitee: {
|
|
select: {
|
|
id: true,
|
|
username: true,
|
|
avatar: true,
|
|
},
|
|
},
|
|
inviter: {
|
|
select: {
|
|
id: true,
|
|
username: true,
|
|
avatar: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Récupère une invitation par son ID (avec seulement houseId)
|
|
*/
|
|
async getInvitationById(
|
|
id: string
|
|
): Promise<{ houseId: string } | null> {
|
|
return prisma.houseInvitation.findUnique({
|
|
where: { id },
|
|
select: { houseId: true },
|
|
});
|
|
}
|
|
}
|
|
|
|
export const houseService = new HouseService();
|
|
|