diff --git a/apps/api/v2/src/lib/modules/regular-booking.module.ts b/apps/api/v2/src/lib/modules/regular-booking.module.ts index 2767becd06..a209bd5bc7 100644 --- a/apps/api/v2/src/lib/modules/regular-booking.module.ts +++ b/apps/api/v2/src/lib/modules/regular-booking.module.ts @@ -4,6 +4,8 @@ import { PrismaFeaturesRepository } from "@/lib/repositories/prisma-features.rep import { PrismaHostRepository } from "@/lib/repositories/prisma-host.repository"; import { PrismaOOORepository } from "@/lib/repositories/prisma-ooo.repository"; import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository"; +import { Logger } from "@/lib/logger.bridge"; +import { BookingEventHandlerService } from "@/lib/services/booking-event-handler.service"; import { CheckBookingAndDurationLimitsService } from "@/lib/services/check-booking-and-duration-limits.service"; import { CheckBookingLimitsService } from "@/lib/services/check-booking-limits.service"; import { HashedLinkService } from "@/lib/services/hashed-link.service"; @@ -21,6 +23,13 @@ import { Module } from "@nestjs/common"; PrismaHostRepository, PrismaOOORepository, PrismaUserRepository, + { + provide: Logger, + useFactory: () => { + return new Logger(); + }, + }, + BookingEventHandlerService, CheckBookingAndDurationLimitsService, CheckBookingLimitsService, HashedLinkService, diff --git a/apps/api/v2/src/lib/services/booking-cancel.service.ts b/apps/api/v2/src/lib/services/booking-cancel.service.ts index 6433db3c67..3a252770a4 100644 --- a/apps/api/v2/src/lib/services/booking-cancel.service.ts +++ b/apps/api/v2/src/lib/services/booking-cancel.service.ts @@ -1,7 +1,7 @@ import { PrismaWriteService } from "@/modules/prisma/prisma-write.service"; import { Injectable } from "@nestjs/common"; -import { BookingCancelService as BaseBookingCancelService } from "@calcom/features/bookings/lib/handleCancelBooking"; +import { BookingCancelService as BaseBookingCancelService } from "@calcom/platform-libraries/bookings"; @Injectable() export class BookingCancelService extends BaseBookingCancelService { diff --git a/apps/api/v2/src/lib/services/booking-event-handler.service.ts b/apps/api/v2/src/lib/services/booking-event-handler.service.ts new file mode 100644 index 0000000000..5a9f49dd94 --- /dev/null +++ b/apps/api/v2/src/lib/services/booking-event-handler.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from "@nestjs/common"; + +import { BookingEventHandlerService as BaseBookingEventHandlerService } from "@calcom/platform-libraries/bookings"; + +import { Logger } from "@/lib/logger.bridge"; + +import { HashedLinkService } from "./hashed-link.service"; + +@Injectable() +export class BookingEventHandlerService extends BaseBookingEventHandlerService { + constructor(hashedLinkService: HashedLinkService, bridgeLogger: Logger) { + super({ + log: bridgeLogger, + hashedLinkService, + }); + } +} + diff --git a/apps/api/v2/src/lib/services/regular-booking.service.ts b/apps/api/v2/src/lib/services/regular-booking.service.ts index 8666b86a8f..2fb77d1001 100644 --- a/apps/api/v2/src/lib/services/regular-booking.service.ts +++ b/apps/api/v2/src/lib/services/regular-booking.service.ts @@ -1,5 +1,6 @@ import { PrismaBookingRepository } from "@/lib/repositories/prisma-booking.repository"; import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository"; +import { BookingEventHandlerService } from "@/lib/services/booking-event-handler.service"; import { CheckBookingAndDurationLimitsService } from "@/lib/services/check-booking-and-duration-limits.service"; import { HashedLinkService } from "@/lib/services/hashed-link.service"; import { LuckyUserService } from "@/lib/services/lucky-user.service"; @@ -17,7 +18,8 @@ export class RegularBookingService extends BaseRegularBookingService { bookingRepository: PrismaBookingRepository, hashedLinkService: HashedLinkService, luckyUserService: LuckyUserService, - userRepository: PrismaUserRepository + userRepository: PrismaUserRepository, + bookingEventHandler: BookingEventHandlerService ) { super({ checkBookingAndDurationLimitsService, @@ -26,6 +28,7 @@ export class RegularBookingService extends BaseRegularBookingService { hashedLinkService, luckyUserService, userRepository, + bookingEventHandler, }); } } diff --git a/packages/features/booking-audit/di/ActorRepository.module.ts b/packages/features/booking-audit/di/ActorRepository.module.ts new file mode 100644 index 0000000000..25748704b4 --- /dev/null +++ b/packages/features/booking-audit/di/ActorRepository.module.ts @@ -0,0 +1,23 @@ +import { PrismaAuditActorRepository } from "@calcom/features/booking-audit/lib/repository/PrismaAuditActorRepository"; +import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens"; +import { bindModuleToClassOnToken } from "@calcom/features/di/di"; +import { moduleLoader as prismaModuleLoader } from "@calcom/features/di/modules/Prisma"; +import { createModule } from "../../di/di"; + +export const actorRepositoryModule = createModule(); +const token = BOOKING_AUDIT_DI_TOKENS.ACTOR_REPOSITORY; +const moduleToken = BOOKING_AUDIT_DI_TOKENS.ACTOR_REPOSITORY_MODULE; +const loadModule = bindModuleToClassOnToken({ + module: actorRepositoryModule, + moduleToken, + token, + classs: PrismaAuditActorRepository, + depsMap: { + prismaClient: prismaModuleLoader, + }, +}); + +export const moduleLoader = { + token, + loadModule, +}; diff --git a/packages/features/booking-audit/di/BookingAuditRepository.module.ts b/packages/features/booking-audit/di/BookingAuditRepository.module.ts new file mode 100644 index 0000000000..cb273a5d26 --- /dev/null +++ b/packages/features/booking-audit/di/BookingAuditRepository.module.ts @@ -0,0 +1,23 @@ +import { PrismaBookingAuditRepository } from "@calcom/features/booking-audit/lib/repository/PrismaBookingAuditRepository"; +import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens"; +import { bindModuleToClassOnToken } from "@calcom/features/di/di"; +import { moduleLoader as prismaModuleLoader } from "@calcom/features/di/modules/Prisma"; +import { createModule } from "../../di/di"; + +export const bookingAuditRepositoryModule = createModule(); +const token = BOOKING_AUDIT_DI_TOKENS.BOOKING_AUDIT_REPOSITORY; +const moduleToken = BOOKING_AUDIT_DI_TOKENS.BOOKING_AUDIT_REPOSITORY_MODULE; +const loadModule = bindModuleToClassOnToken({ + module: bookingAuditRepositoryModule, + moduleToken, + token, + classs: PrismaBookingAuditRepository, + depsMap: { + prismaClient: prismaModuleLoader, + }, +}); + +export const moduleLoader = { + token, + loadModule, +}; diff --git a/packages/features/booking-audit/di/BookingAuditService.module.ts b/packages/features/booking-audit/di/BookingAuditService.module.ts new file mode 100644 index 0000000000..c1f57dc0c6 --- /dev/null +++ b/packages/features/booking-audit/di/BookingAuditService.module.ts @@ -0,0 +1,26 @@ +import { BookingAuditService } from "@calcom/features/booking-audit/lib/service/BookingAuditService"; +import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens"; +import { moduleLoader as bookingAuditRepositoryModuleLoader } from "@calcom/features/booking-audit/di/BookingAuditRepository.module"; +import { moduleLoader as actorRepositoryModuleLoader } from "@calcom/features/booking-audit/di/ActorRepository.module"; + +import { createModule, bindModuleToClassOnToken } from "../../di/di"; + +export const bookingAuditServiceModule = createModule(); +const token = BOOKING_AUDIT_DI_TOKENS.BOOKING_AUDIT_SERVICE; +const moduleToken = BOOKING_AUDIT_DI_TOKENS.BOOKING_AUDIT_SERVICE_MODULE; + +const loadModule = bindModuleToClassOnToken({ + module: bookingAuditServiceModule, + moduleToken, + token, + classs: BookingAuditService, + depsMap: { + bookingAuditRepository: bookingAuditRepositoryModuleLoader, + actorRepository: actorRepositoryModuleLoader, + }, +}); + +export const moduleLoader = { + token, + loadModule +}; diff --git a/packages/features/booking-audit/di/tokens.ts b/packages/features/booking-audit/di/tokens.ts new file mode 100644 index 0000000000..4a93906a18 --- /dev/null +++ b/packages/features/booking-audit/di/tokens.ts @@ -0,0 +1,8 @@ +export const BOOKING_AUDIT_DI_TOKENS = { + BOOKING_AUDIT_SERVICE: Symbol("BookingAuditService"), + BOOKING_AUDIT_SERVICE_MODULE: Symbol("BookingAuditServiceModule"), + BOOKING_AUDIT_REPOSITORY: Symbol("BookingAuditRepository"), + BOOKING_AUDIT_REPOSITORY_MODULE: Symbol("BookingAuditRepositoryModule"), + ACTOR_REPOSITORY: Symbol("ActorRepository"), + ACTOR_REPOSITORY_MODULE: Symbol("ActorRepositoryModule"), +}; diff --git a/packages/features/booking-audit/lib/repository/IAuditActorRepository.ts b/packages/features/booking-audit/lib/repository/IAuditActorRepository.ts index 51ff14ec29..950ecabc07 100644 --- a/packages/features/booking-audit/lib/repository/IAuditActorRepository.ts +++ b/packages/features/booking-audit/lib/repository/IAuditActorRepository.ts @@ -13,5 +13,9 @@ type AuditActor = { export interface IAuditActorRepository { findByUserUuid(userUuid: string): Promise; findSystemActorOrThrow(): Promise; + // TODO: To be implemented in followup PR + // upsertUserActor(userUuid: string): Promise; + // upsertGuestActor(email: string, name?: string, phone?: string): Promise; + // findByAttendeeId(attendeeId: number): Promise; } diff --git a/packages/features/booking-audit/lib/repository/IBookingAuditRepository.ts b/packages/features/booking-audit/lib/repository/IBookingAuditRepository.ts index d75d6c7e5a..b9e5c0027c 100644 --- a/packages/features/booking-audit/lib/repository/IBookingAuditRepository.ts +++ b/packages/features/booking-audit/lib/repository/IBookingAuditRepository.ts @@ -1,11 +1,12 @@ +import type { JsonValue } from "@calcom/types/Json"; + export type BookingAuditType = "RECORD_CREATED" | "RECORD_UPDATED" | "RECORD_DELETED" -export type BookingAuditAction = "CREATED" | "CANCELLED" | "ACCEPTED" | "REJECTED" | "PENDING" | "AWAITING_HOST" | "RESCHEDULED" | "ATTENDEE_ADDED" | "ATTENDEE_REMOVED" | "CANCELLATION_REASON_UPDATED" | "REJECTION_REASON_UPDATED" | "ASSIGNMENT_REASON_UPDATED" | "REASSIGNMENT_REASON_UPDATED" | "LOCATION_CHANGED" | "HOST_NO_SHOW_UPDATED" | "ATTENDEE_NO_SHOW_UPDATED" | "RESCHEDULE_REQUESTED" +export type BookingAuditAction = "CREATED" | "CANCELLED" | "ACCEPTED" | "REJECTED" | "PENDING" | "AWAITING_HOST" | "RESCHEDULED" | "ATTENDEE_ADDED" | "ATTENDEE_REMOVED" | "REASSIGNMENT" | "LOCATION_CHANGED" | "HOST_NO_SHOW_UPDATED" | "ATTENDEE_NO_SHOW_UPDATED" | "RESCHEDULE_REQUESTED" export type BookingAuditCreateInput = { bookingUid: string; actorId: string; action: BookingAuditAction; - data: unknown; - createdAt: Date; + data: JsonValue; type: BookingAuditType; timestamp: Date; } @@ -14,9 +15,12 @@ type BookingAudit = { id: string; bookingUid: string; actorId: string; - action: string; - data: unknown; + action: BookingAuditAction; + type: BookingAuditType; + timestamp: Date; createdAt: Date; + updatedAt: Date; + data: JsonValue; } export interface IBookingAuditRepository { diff --git a/packages/features/booking-audit/lib/repository/PrismaBookingAuditRepository.ts b/packages/features/booking-audit/lib/repository/PrismaBookingAuditRepository.ts index 7a1c29b22b..e1b7f7bad9 100644 --- a/packages/features/booking-audit/lib/repository/PrismaBookingAuditRepository.ts +++ b/packages/features/booking-audit/lib/repository/PrismaBookingAuditRepository.ts @@ -10,7 +10,14 @@ export class PrismaBookingAuditRepository implements IBookingAuditRepository { async create(bookingAudit: BookingAuditCreateInput) { return this.deps.prismaClient.bookingAudit.create({ - data: bookingAudit, + data: { + bookingUid: bookingAudit.bookingUid, + actorId: bookingAudit.actorId, + action: bookingAudit.action, + type: bookingAudit.type, + timestamp: bookingAudit.timestamp, + data: bookingAudit.data === null ? undefined : bookingAudit.data, + }, }); } } diff --git a/packages/features/booking-audit/lib/service/BookingAuditService.ts b/packages/features/booking-audit/lib/service/BookingAuditService.ts new file mode 100644 index 0000000000..d57d74262a --- /dev/null +++ b/packages/features/booking-audit/lib/service/BookingAuditService.ts @@ -0,0 +1,67 @@ +import type { JsonValue } from "@calcom/types/Json"; +import logger from "@calcom/lib/logger"; +import type { IBookingAuditRepository, BookingAuditType, BookingAuditAction } from "../repository/IBookingAuditRepository"; +import type { IAuditActorRepository } from "../repository/IAuditActorRepository"; +import { safeStringify } from "@calcom/lib/safeStringify"; + +interface BookingAuditServiceDeps { + bookingAuditRepository: IBookingAuditRepository; + actorRepository: IAuditActorRepository; +} + +type CreateBookingAuditInput = { + bookingUid: string; + actorId: string; + type: BookingAuditType; + action: BookingAuditAction; + data: JsonValue; + timestamp: Date; // Required: actual time of the booking change (business event) +}; + +type BookingAudit = { + id: string; + bookingUid: string; + actorId: string; + type: BookingAuditType; + action: BookingAuditAction; + timestamp: Date; + createdAt: Date; + updatedAt: Date; + data: JsonValue; +}; + +/** + * BookingAuditService - Central service for all booking audit operations + * Handles both write (audit creation) and read (display) operations + * Each action service manages its own schema versioning + */ +export class BookingAuditService { + private readonly bookingAuditRepository: IBookingAuditRepository; + + constructor(private readonly deps: BookingAuditServiceDeps) { + this.bookingAuditRepository = deps.bookingAuditRepository; + } + + /** + * TODO: TO be integrated with public methods in followup PR + */ + async createAuditRecord(input: CreateBookingAuditInput): Promise { + // Log only non-sensitive metadata, excluding the data field which may contain PII + logger.info("Creating audit record", safeStringify({ + bookingUid: input.bookingUid, + actorId: input.actorId, + type: input.type, + action: input.action, + timestamp: input.timestamp, + })); + + return this.bookingAuditRepository.create({ + bookingUid: input.bookingUid, + actorId: input.actorId, + type: input.type, + action: input.action, + timestamp: input.timestamp, + data: input.data, + }); + } +} \ No newline at end of file diff --git a/packages/features/bookings/di/BookingEventHandlerService.container.ts b/packages/features/bookings/di/BookingEventHandlerService.container.ts new file mode 100644 index 0000000000..c8b542287d --- /dev/null +++ b/packages/features/bookings/di/BookingEventHandlerService.container.ts @@ -0,0 +1,15 @@ +import { createContainer } from "@calcom/features/di/di"; + +import { + type BookingEventHandlerService, + moduleLoader as bookingEventHandlerServiceModule, +} from "./BookingEventHandlerService.module"; + +const container = createContainer(); + +export function getBookingEventHandlerService() { + bookingEventHandlerServiceModule.loadModule(container); + + return container.get(bookingEventHandlerServiceModule.token); +} + diff --git a/packages/features/bookings/di/BookingEventHandlerService.module.ts b/packages/features/bookings/di/BookingEventHandlerService.module.ts new file mode 100644 index 0000000000..b7be1b0856 --- /dev/null +++ b/packages/features/bookings/di/BookingEventHandlerService.module.ts @@ -0,0 +1,30 @@ +import { BookingEventHandlerService } from "@calcom/features/bookings/lib/onBookingEvents/BookingEventHandlerService"; +import { bindModuleToClassOnToken, createModule } from "@calcom/features/di/di"; +import { DI_TOKENS } from "@calcom/features/di/tokens"; +import { moduleLoader as hashedLinkServiceModuleLoader } from "@calcom/features/hashedLink/di/HashedLinkService.module"; +import { moduleLoader as bookingAuditServiceModuleLoader } from "@calcom/features/booking-audit/di/BookingAuditService.module"; +import { moduleLoader as loggerModuleLoader } from "@calcom/features/di/shared/services/logger.service"; + +const thisModule = createModule(); +const token = DI_TOKENS.BOOKING_EVENT_HANDLER_SERVICE; +const moduleToken = DI_TOKENS.BOOKING_EVENT_HANDLER_SERVICE_MODULE; + +const loadModule = bindModuleToClassOnToken({ + module: thisModule, + moduleToken, + token, + classs: BookingEventHandlerService, + depsMap: { + hashedLinkService: hashedLinkServiceModuleLoader, + bookingAuditService: bookingAuditServiceModuleLoader, + log: loggerModuleLoader, + }, +}); + +export const moduleLoader = { + token, + loadModule, +}; + +export type { BookingEventHandlerService }; + diff --git a/packages/features/bookings/di/RegularBookingService.module.ts b/packages/features/bookings/di/RegularBookingService.module.ts index a5370a9533..3d587319fd 100644 --- a/packages/features/bookings/di/RegularBookingService.module.ts +++ b/packages/features/bookings/di/RegularBookingService.module.ts @@ -1,3 +1,4 @@ +import { moduleLoader as bookingAuditServiceModuleLoader } from "@calcom/features/booking-audit/di/BookingAuditService.module"; import { RegularBookingService } from "@calcom/features/bookings/lib/service/RegularBookingService"; import { bindModuleToClassOnToken, createModule } from "@calcom/features/di/di"; import { moduleLoader as bookingRepositoryModuleLoader } from "@calcom/features/di/modules/Booking"; @@ -7,6 +8,7 @@ import { moduleLoader as userRepositoryModuleLoader } from "@calcom/features/di/ import { DI_TOKENS } from "@calcom/features/di/tokens"; import { moduleLoader as hashedLinkServiceModuleLoader } from "@calcom/features/hashedLink/di/HashedLinkService.module"; import { moduleLoader as prismaModuleLoader } from "@calcom/features/di/modules/Prisma"; +import { moduleLoader as bookingEventHandlerModuleLoader } from "@calcom/features/bookings/di/BookingEventHandlerService.module"; const thisModule = createModule(); const token = DI_TOKENS.REGULAR_BOOKING_SERVICE; @@ -24,6 +26,7 @@ const loadModule = bindModuleToClassOnToken({ luckyUserService: luckyUserServiceModuleLoader, userRepository: userRepositoryModuleLoader, hashedLinkService: hashedLinkServiceModuleLoader, + bookingEventHandler: bookingEventHandlerModuleLoader, }, }); diff --git a/packages/features/bookings/di/tokens.ts b/packages/features/bookings/di/tokens.ts index 560c2663a2..0479c8bfdc 100644 --- a/packages/features/bookings/di/tokens.ts +++ b/packages/features/bookings/di/tokens.ts @@ -7,4 +7,6 @@ export const BOOKING_DI_TOKENS = { INSTANT_BOOKING_CREATE_SERVICE_MODULE: Symbol("InstantBookingCreateServiceModule"), BOOKING_CANCEL_SERVICE: Symbol("BookingCancelService"), BOOKING_CANCEL_SERVICE_MODULE: Symbol("BookingCancelServiceModule"), + BOOKING_EVENT_HANDLER_SERVICE: Symbol("BookingEventHandlerService"), + BOOKING_EVENT_HANDLER_SERVICE_MODULE: Symbol("BookingEventHandlerServiceModule"), }; diff --git a/packages/features/bookings/lib/onBookingEvents/BookingEventHandlerService.ts b/packages/features/bookings/lib/onBookingEvents/BookingEventHandlerService.ts index 68e8aa5d84..824b070ec9 100644 --- a/packages/features/bookings/lib/onBookingEvents/BookingEventHandlerService.ts +++ b/packages/features/bookings/lib/onBookingEvents/BookingEventHandlerService.ts @@ -1,13 +1,15 @@ -import type { Logger } from "tslog"; - +import type { BookingAuditService } from "@calcom/features/booking-audit/lib/service/BookingAuditService"; import type { HashedLinkService } from "@calcom/features/hashedLink/lib/service/HashedLinkService"; +import type { ISimpleLogger } from "@calcom/features/di/shared/services/logger.service"; import { safeStringify } from "@calcom/lib/safeStringify"; import type { BookingCreatedPayload, BookingRescheduledPayload } from "./types"; interface BookingEventHandlerDeps { - log: Logger; + log: ISimpleLogger; hashedLinkService: HashedLinkService; + //TODO: To be made required in followup PR + bookingAuditService?: BookingAuditService; } export class BookingEventHandlerService { diff --git a/packages/features/bookings/lib/service/RegularBookingService.ts b/packages/features/bookings/lib/service/RegularBookingService.ts index 36ccdebba6..276a6986af 100644 --- a/packages/features/bookings/lib/service/RegularBookingService.ts +++ b/packages/features/bookings/lib/service/RegularBookingService.ts @@ -417,6 +417,7 @@ export interface IBookingServiceDependencies { luckyUserService: LuckyUserService; userRepository: UserRepository; hashedLinkService: HashedLinkService; + bookingEventHandler: BookingEventHandlerService; } /** @@ -2259,10 +2260,7 @@ async function handler( // Add more fields here when needed const bookingRescheduledPayload = bookingCreatedPayload; - const bookingEventHandler = new BookingEventHandlerService({ - log: tracingLogger, - hashedLinkService: deps.hashedLinkService, - }); + const bookingEventHandler = deps.bookingEventHandler; // TODO: Incrementally move all stuff that happens after a booking is created to these handlers if (originalRescheduledBooking) { diff --git a/packages/features/bookings/lib/types/actor.ts b/packages/features/bookings/lib/types/actor.ts new file mode 100644 index 0000000000..ed08805987 --- /dev/null +++ b/packages/features/bookings/lib/types/actor.ts @@ -0,0 +1,89 @@ +/** + * Represents the entity that performed a booking action + * Uses discriminating union for type-safe actor identification + */ +export type Actor = ActorById | UserActor | AttendeeActor | GuestActor; + +/** + * Actor referenced by existing ID in AuditActor table + * Use this for System actors or when you already have an actor ID + */ +type ActorById = { + identifiedBy: "id"; + id: string; // UUID of existing actor (including System actor) +}; + +/** + * Actor identified by user UUID + * Will be upserted in AuditActor table + */ +type UserActor = { + identifiedBy: "user"; + userUuid: string; +}; + +/** + * Actor identified by attendee ID + * Must already exist in AuditActor table + */ +type AttendeeActor = { + identifiedBy: "attendee"; + attendeeId: number; +}; + +/** + * Actor identified by guest data (email, name, phone) + * Will be upserted in AuditActor table + */ +type GuestActor = { + identifiedBy: "guest"; + email?: string; + name?: string; + phone?: string; +}; + +// System actor ID constant +export const SYSTEM_ACTOR_ID = "00000000-0000-0000-0000-000000000000"; + +/** + * Creates an Actor representing a User by UUID + */ +export function makeUserActor(userUuid: string): UserActor { + return { + identifiedBy: "user", + userUuid, + }; +} + +/** + * Creates an Actor representing the System + * System actors must be referenced by ID (requires migration) + */ +export function makeSystemActor(): ActorById { + return { + identifiedBy: "id", + id: SYSTEM_ACTOR_ID, + }; +} + +/** + * Creates an Actor representing a Guest (non-registered attendee) + */ +export function makeGuestActor(email: string, name?: string, phone?: string): GuestActor { + return { + identifiedBy: "guest", + email, + name, + phone, + }; +} + +/** + * Creates an Actor by existing actor ID + */ +export function makeActorById(id: string): ActorById { + return { + identifiedBy: "id", + id, + }; +} \ No newline at end of file diff --git a/packages/features/di/shared/services/logger.service.ts b/packages/features/di/shared/services/logger.service.ts index 68abe17367..71d008c6b0 100644 --- a/packages/features/di/shared/services/logger.service.ts +++ b/packages/features/di/shared/services/logger.service.ts @@ -1,13 +1,38 @@ -import { createModule } from "@evyweb/ioctopus"; - -import type { ILogger } from "@calcom/features/webhooks/lib/interface/infrastructure"; - +import { bindModuleToClassOnToken, createModule } from "@calcom/features/di/di"; +import { Logger } from "tslog"; import { SHARED_TOKENS } from "../shared.tokens"; +import { loggerConfig } from "@calcom/lib/logger"; -export const loggerServiceModule = createModule(); +/** + * Minimal logger interface that supports common logging methods. + * This allows both tslog Logger and bridge Logger implementations to be used interchangeably. + * Can be used by any module that needs a simple logger interface. + */ +export interface ISimpleLogger { + debug(...args: unknown[]): void; + error(...args: unknown[]): void; + info(...args: unknown[]): void; + warn(...args: unknown[]): void; +} -// Bind logger with proper factory that respects IoC -loggerServiceModule.bind(SHARED_TOKENS.LOGGER).toFactory(async (): Promise => { - const loggerModule = await import("@calcom/lib/logger"); - return loggerModule.default; +const thisModule = createModule(); +const token = SHARED_TOKENS.LOGGER; +const moduleToken = SHARED_TOKENS.LOGGER_MODULE; +class LoggerService extends Logger { + constructor() { + super(loggerConfig); + } +} +const loadModule = bindModuleToClassOnToken({ + module: thisModule, + moduleToken, + token, + classs: LoggerService, + depsMap: {}, }); + +export const moduleLoader = { + token, + loadModule, +}; + diff --git a/packages/features/di/shared/shared.tokens.ts b/packages/features/di/shared/shared.tokens.ts index e0c49da3ef..99c22a6749 100644 --- a/packages/features/di/shared/shared.tokens.ts +++ b/packages/features/di/shared/shared.tokens.ts @@ -2,4 +2,5 @@ export const SHARED_TOKENS = { // Infrastructure services TASKER: Symbol("ITasker"), LOGGER: Symbol("ILogger"), + LOGGER_MODULE: Symbol("ILoggerModule"), } as const; diff --git a/packages/features/di/tokens.ts b/packages/features/di/tokens.ts index 8709ce90b3..4b8436ec15 100644 --- a/packages/features/di/tokens.ts +++ b/packages/features/di/tokens.ts @@ -1,4 +1,5 @@ import { BOOKING_DI_TOKENS } from "@calcom/features/bookings/di/tokens"; +import { BOOKING_AUDIT_DI_TOKENS } from "@calcom/features/booking-audit/di/tokens"; import { HASHED_LINK_DI_TOKENS } from "@calcom/features/hashedLink/di/tokens"; import { ORGANIZATION_DI_TOKENS } from "@calcom/features/ee/organizations/di/tokens"; import { WATCHLIST_DI_TOKENS } from "./watchlist/Watchlist.tokens"; @@ -56,10 +57,9 @@ export const DI_TOKENS = { ATTRIBUTE_REPOSITORY_MODULE: Symbol("AttributeRepositoryModule"), MEMBERSHIP_SERVICE: Symbol("MembershipService"), MEMBERSHIP_SERVICE_MODULE: Symbol("MembershipServiceModule"), - // Booking service tokens + ...BOOKING_AUDIT_DI_TOKENS, ...BOOKING_DI_TOKENS, ...HASHED_LINK_DI_TOKENS, - // Watchlist service tokens ...WATCHLIST_DI_TOKENS, ...ORGANIZATION_DI_TOKENS, }; diff --git a/packages/features/di/watchlist/containers/watchlist.ts b/packages/features/di/watchlist/containers/watchlist.ts index 82c123f750..df59a4378b 100644 --- a/packages/features/di/watchlist/containers/watchlist.ts +++ b/packages/features/di/watchlist/containers/watchlist.ts @@ -1,6 +1,6 @@ import { createContainer } from "@evyweb/ioctopus"; -import { loggerServiceModule } from "@calcom/features/di/shared/services/logger.service"; +import { moduleLoader as loggerModuleLoader } from "@calcom/features/di/shared/services/logger.service"; import { taskerServiceModule } from "@calcom/features/di/shared/services/tasker.service"; import { SHARED_TOKENS } from "@calcom/features/di/shared/shared.tokens"; import { @@ -21,8 +21,8 @@ import { watchlistModule } from "../modules/Watchlist.module"; export const watchlistContainer = createContainer(); prismaModuleLoader.loadModule(watchlistContainer); +loggerModuleLoader.loadModule(watchlistContainer); -watchlistContainer.load(SHARED_TOKENS.LOGGER, loggerServiceModule); watchlistContainer.load(SHARED_TOKENS.TASKER, taskerServiceModule); watchlistContainer.load(WATCHLIST_DI_TOKENS.GLOBAL_WATCHLIST_REPOSITORY, watchlistModule); diff --git a/packages/features/di/webhooks/containers/webhook.ts b/packages/features/di/webhooks/containers/webhook.ts index e791be15c2..7070635796 100644 --- a/packages/features/di/webhooks/containers/webhook.ts +++ b/packages/features/di/webhooks/containers/webhook.ts @@ -1,6 +1,6 @@ import { createContainer } from "@evyweb/ioctopus"; -import { loggerServiceModule } from "../../shared/services/logger.service"; +import { moduleLoader as loggerModuleLoader } from "../../shared/services/logger.service"; import { taskerServiceModule } from "../../shared/services/tasker.service"; import { SHARED_TOKENS } from "../../shared/shared.tokens"; import { WEBHOOK_TOKENS } from "../Webhooks.tokens"; @@ -9,7 +9,7 @@ import { webhookModule } from "../modules/Webhook.module"; export const webhookContainer = createContainer(); // Load shared infrastructure -webhookContainer.load(SHARED_TOKENS.LOGGER, loggerServiceModule); +loggerModuleLoader.loadModule(webhookContainer); webhookContainer.load(SHARED_TOKENS.TASKER, taskerServiceModule); // Load webhook module diff --git a/packages/lib/logger.ts b/packages/lib/logger.ts index 5d60e8dd78..ffe779bb8a 100644 --- a/packages/lib/logger.ts +++ b/packages/lib/logger.ts @@ -1,8 +1,8 @@ -import { Logger } from "tslog"; +import { Logger, ISettingsParam } from "tslog"; import { IS_PRODUCTION } from "./constants"; -const logger = new Logger({ +export const loggerConfig: ISettingsParam = { minLevel: parseInt(process.env.NEXT_PUBLIC_LOGGER_LEVEL || "4"), maskValuesOfKeys: ["password", "passwordConfirmation", "credentials", "credential"], prettyLogTimeZone: IS_PRODUCTION ? "UTC" : "local", @@ -15,6 +15,8 @@ const logger = new Logger({ dateIsoStr: "blue", }, type: IS_PRODUCTION ? "json" : "pretty", -}); +}; + +const logger = new Logger(loggerConfig); export default logger; diff --git a/packages/platform/libraries/bookings.ts b/packages/platform/libraries/bookings.ts index db2be613f1..fff13ec38e 100644 --- a/packages/platform/libraries/bookings.ts +++ b/packages/platform/libraries/bookings.ts @@ -4,6 +4,8 @@ export { CheckBookingAndDurationLimitsService } from "@calcom/features/bookings/ export { RegularBookingService } from "@calcom/features/bookings/lib/service/RegularBookingService"; export { RecurringBookingService } from "@calcom/features/bookings/lib/service/RecurringBookingService"; export { InstantBookingCreateService } from "@calcom/features/bookings/lib/service/InstantBookingCreateService"; +export { BookingEventHandlerService } from "@calcom/features/bookings/lib/onBookingEvents/BookingEventHandlerService"; +export { BookingCancelService } from "@calcom/features/bookings/lib/handleCancelBooking"; export type { InstantBookingCreateResult, RegularBookingCreateResult,