Productionize EventSphere platform

This commit is contained in:
Austin A
2026-04-25 21:02:19 +01:00
commit 1f1d30a9f5
171 changed files with 18682 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { randomBytes } from "crypto";
import { PrismaService } from "../../prisma/prisma.service";
import { AuditService } from "../audit/audit.service";
import { QueuesService } from "../queues/queues.service";
import { CreateInviteeDto, PublicRsvpDto, UpdateInviteeDto } from "./dto";
@Injectable()
export class InviteesService {
constructor(
private readonly prisma: PrismaService,
private readonly audit: AuditService,
private readonly queues: QueuesService
) {}
async findAll(tenantId: string) {
return this.prisma.invitee.findMany({
where: { tenantId },
orderBy: { createdAt: "desc" },
include: { event: true }
});
}
async create(tenantId: string, dto: CreateInviteeDto) {
const email = dto.email.toLowerCase().trim();
const event = await this.prisma.event.findFirst({ where: { id: dto.eventId, tenantId } });
if (!event) throw new NotFoundException("Event not found");
const code = randomBytes(10).toString("hex").toUpperCase();
try {
const invitee = await this.prisma.invitee.create({
data: {
tenantId,
eventId: dto.eventId,
fullName: dto.fullName,
email,
phone: dto.phone?.trim().length ? dto.phone : null,
code
}
});
const baseUrl = (process.env.PUBLIC_WEB_URL ?? "http://localhost:3000").replace(/\/$/, "");
const subject = `Invitation: ${event.name}`;
const text = `Hello ${invitee.fullName},\n\nYou are invited to ${event.name}.\nRSVP here: ${baseUrl}/invite/${invitee.code}\n\nThank you.`;
await this.queues.queue("communications").add("sendEmail", {
tenantId,
to: invitee.email,
subject,
text,
inviteeId: invitee.id
});
await this.audit.log("invitees.create", undefined, { tenantId, entityType: "Invitee", entityId: invitee.id, metadata: { eventId: dto.eventId } });
return invitee;
} catch (e: any) {
if (typeof e?.message === "string" && e.message.includes("Invitee_tenantId_eventId_email_key")) {
throw new BadRequestException("Invitee already exists for this event");
}
throw e;
}
}
async findOne(tenantId: string, id: string) {
const invitee = await this.prisma.invitee.findFirst({
where: { id, tenantId },
include: { event: true, rsvps: { orderBy: { createdAt: "desc" } } }
});
if (!invitee) throw new NotFoundException("Invitee not found");
return invitee;
}
async update(tenantId: string, id: string, dto: UpdateInviteeDto) {
await this.findOne(tenantId, id);
const invitee = await this.prisma.invitee.update({
where: { id },
data: {
fullName: dto.fullName,
email: dto.email ? dto.email.toLowerCase().trim() : undefined,
phone: dto.phone === undefined ? undefined : dto.phone?.trim().length ? dto.phone : null,
status: dto.status
}
});
await this.audit.log("invitees.update", undefined, { tenantId, entityType: "Invitee", entityId: id });
return invitee;
}
async remove(tenantId: string, id: string) {
await this.findOne(tenantId, id);
await this.prisma.invitee.delete({ where: { id } });
await this.audit.log("invitees.delete", undefined, { tenantId, entityType: "Invitee", entityId: id });
return { ok: true };
}
async publicGetInviteeByCode(code: string) {
const invitee = await this.prisma.invitee.findFirst({
where: { code },
include: { event: true }
});
if (!invitee) throw new NotFoundException("Invite not found");
return {
invitee: {
code: invitee.code,
fullName: invitee.fullName,
email: invitee.email,
phone: invitee.phone,
status: invitee.status
},
event: {
id: invitee.event.id,
name: invitee.event.name,
slug: invitee.event.slug,
startsAt: invitee.event.startsAt,
venue: invitee.event.venue,
status: invitee.event.status
}
};
}
async publicRsvp(code: string, dto: PublicRsvpDto) {
const invitee = await this.prisma.invitee.findFirst({ where: { code } });
if (!invitee) throw new NotFoundException("Invite not found");
const rsvp = await this.prisma.rSVP.create({
data: {
tenantId: invitee.tenantId,
eventId: invitee.eventId,
inviteeId: invitee.id,
response: dto.response,
note: dto.note?.trim().length ? dto.note : null
}
});
await this.prisma.invitee.update({ where: { id: invitee.id }, data: { status: "rsvped" } });
await this.audit.log("rsvps.public", undefined, { tenantId: invitee.tenantId, entityType: "RSVP", entityId: rsvp.id, metadata: { inviteeId: invitee.id, response: dto.response } });
return { ok: true };
}
}