diff --git a/apps/api/dist/admin/admin-audit.service.js b/apps/api/dist/admin/admin-audit.service.js new file mode 100644 index 0000000000000000000000000000000000000000..b6db58e0470da27e91af6c45b6497619ff04b4db --- /dev/null +++ b/apps/api/dist/admin/admin-audit.service.js @@ -0,0 +1,100 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminAuditService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminAuditService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const audit_service_1 = require("../audit/audit.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_permission_decorator_1 = require("../common/decorators/admin-permission.decorator"); +let AdminAuditService = AdminAuditService_1 = class AdminAuditService { + prisma; + audit; + logger = new common_1.Logger(AdminAuditService_1.name); + constructor(prisma, audit) { + this.prisma = prisma; + this.audit = audit; + } + async list(input = {}) { + return this.audit.listAuditLogs(input); + } + async verify() { + return this.audit.verifyIntegrity(); + } + async stats() { + return this.audit.getAuditStats(); + } + async export(input, exportedBy) { + const { items, total } = await this.audit.listAuditLogs({ + startDate: input.startDate, + endDate: input.endDate, + limit: 10_000, + }); + const integrity = await this.audit.verifyIntegrity(); + await this.audit.log({ + userId: exportedBy, + action: "admin.audit.export", + entityType: "AuditLog", + entityId: "export", + metadata: { + exportedBy, + total, + startDate: input.startDate, + endDate: input.endDate, + chainValid: integrity.valid, + chainBrokenAt: integrity.brokenAt, + }, + }); + return { + exportedAt: new Date().toISOString(), + exportedBy, + total, + chainValid: integrity.valid, + chainBrokenAt: integrity.brokenAt, + items, + }; + } + async getEntry(entryId) { + const entry = await this.prisma.auditLog.findUnique({ + where: { id: entryId }, + select: { + id: true, + userId: true, + workspaceId: true, + action: true, + entityType: true, + entityId: true, + metadata: true, + ipAddress: true, + userAgent: true, + entryHash: true, + previousHash: true, + createdAt: true, + }, + }); + if (!entry) + throw new common_1.NotFoundException(`Audit entry ${entryId} not found`); + return entry; + } +}; +exports.AdminAuditService = AdminAuditService; +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.AUDIT_EXPORT), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, String]), + __metadata("design:returntype", Promise) +], AdminAuditService.prototype, "export", null); +exports.AdminAuditService = AdminAuditService = AdminAuditService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, audit_service_1.AuditService]) +], AdminAuditService); +//# sourceMappingURL=admin-audit.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-audit.service.js.map b/apps/api/dist/admin/admin-audit.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..9d33b2574dce8403296cc744e144f7182634f61c --- /dev/null +++ b/apps/api/dist/admin/admin-audit.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-audit.service.js","sourceRoot":"","sources":["../../src/admin/admin-audit.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAuE;AACvE,6DAAyD;AACzD,0DAAsD;AAEtD,4DAA4D;AAC5D,gGAAyF;AAoBlF,IAAM,iBAAiB,yBAAvB,MAAM,iBAAiB;IAGC;IAAwC;IAFpD,MAAM,GAAG,IAAI,eAAM,CAAC,mBAAiB,CAAC,IAAI,CAAC,CAAC;IAE7D,YAA6B,MAAqB,EAAmB,KAAmB;QAA3D,WAAM,GAAN,MAAM,CAAe;QAAmB,UAAK,GAAL,KAAK,CAAc;IAAG,CAAC;IAE5F,KAAK,CAAC,IAAI,CAAC,QAA6B,EAAE;QACxC,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,MAAM;QACV,OAAO,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;IACpC,CAAC;IAGK,AAAN,KAAK,CAAC,MAAM,CAAC,KAA+C,EAAE,UAAkB;QAC9E,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;YACtD,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;QAGrD,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YACnB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,oBAAoB;YAC5B,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE;gBACR,UAAU;gBACV,KAAK;gBACL,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,UAAU,EAAE,SAAS,CAAC,KAAK;gBAC3B,aAAa,EAAE,SAAS,CAAC,QAAQ;aAClC;SACF,CAAC,CAAC;QAEH,OAAO;YACL,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACpC,UAAU;YACV,KAAK;YACL,UAAU,EAAE,SAAS,CAAC,KAAK;YAC3B,aAAa,EAAE,SAAS,CAAC,QAAQ;YACjC,KAAK;SACN,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAAe;QAC5B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;YAClD,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;YACtB,MAAM,EAAE;gBACN,EAAE,EAAE,IAAI;gBACR,MAAM,EAAE,IAAI;gBACZ,WAAW,EAAE,IAAI;gBACjB,MAAM,EAAE,IAAI;gBACZ,UAAU,EAAE,IAAI;gBAChB,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,IAAI;gBACd,SAAS,EAAE,IAAI;gBACf,SAAS,EAAE,IAAI;gBACf,SAAS,EAAE,IAAI;gBACf,YAAY,EAAE,IAAI;gBAClB,SAAS,EAAE,IAAI;aAChB;SACF,CAAC,CAAC;QACH,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,0BAAiB,CAAC,eAAe,OAAO,YAAY,CAAC,CAAC;QAC5E,OAAO,KAAK,CAAC;IACf,CAAC;CACF,CAAA;AAzEY,8CAAiB;AAkBtB;IADL,IAAA,mDAAsB,EAAC,8BAAe,CAAC,YAAY,CAAC;;;;+CAiCpD;4BAlDU,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAI0B,8BAAa,EAA0B,4BAAY;GAH7E,iBAAiB,CAyE7B"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-auth.service.js b/apps/api/dist/admin/admin-auth.service.js new file mode 100644 index 0000000000000000000000000000000000000000..b1e74e846f5d5d2a9ddf1f69a5fec6daa928613b --- /dev/null +++ b/apps/api/dist/admin/admin-auth.service.js @@ -0,0 +1,87 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminAuthService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminAuthService = void 0; +const common_1 = require("@nestjs/common"); +const server_1 = require("@trpc/server"); +const prisma_service_1 = require("../prisma/prisma.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +const platform_admin_1 = require("../common/platform-admin"); +let AdminAuthService = AdminAuthService_1 = class AdminAuthService { + prisma; + logger = new common_1.Logger(AdminAuthService_1.name); + constructor(prisma) { + this.prisma = prisma; + } + async getAdminContext(userId) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { + id: true, + isAdmin: true, + adminRole: true, + adminPermissions: true, + suspendedAt: true, + }, + }); + if (!user) + return null; + const role = (0, platform_admin_1.deriveAdminRole)(user); + if (!role) + return null; + const permissions = (0, shared_types_1.resolveAdminPermissions)(role, user.adminPermissions); + const suspended = !!user.suspendedAt; + return { + userId: user.id, + isAdmin: !!user.isAdmin, + role, + permissions, + suspended, + }; + } + async requirePermission(userId, permission) { + const ctx = await this.getAdminContext(userId); + if (!ctx) { + throw new server_1.TRPCError({ code: "FORBIDDEN", message: "Admin privileges required" }); + } + if (ctx.suspended) { + throw new server_1.TRPCError({ code: "FORBIDDEN", message: "Account is suspended" }); + } + if (!ctx.permissions.has(permission)) { + throw new server_1.TRPCError({ + code: "FORBIDDEN", + message: `Insufficient permissions: ${permission} required`, + }); + } + return ctx; + } + async requireAnyPermission(userId, ...permissions) { + const ctx = await this.getAdminContext(userId); + if (!ctx || ctx.suspended) { + throw new server_1.TRPCError({ code: "FORBIDDEN", message: "Admin privileges required" }); + } + const hasAny = permissions.some((p) => ctx.permissions.has(p)); + if (!hasAny) { + throw new server_1.TRPCError({ + code: "FORBIDDEN", + message: `Insufficient permissions: one of ${permissions.join(", ")} required`, + }); + } + return ctx; + } +}; +exports.AdminAuthService = AdminAuthService; +exports.AdminAuthService = AdminAuthService = AdminAuthService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], AdminAuthService); +//# sourceMappingURL=admin-auth.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-auth.service.js.map b/apps/api/dist/admin/admin-auth.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..41a884bf605f6015eccbcbd7e538628641cd145f --- /dev/null +++ b/apps/api/dist/admin/admin-auth.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-auth.service.js","sourceRoot":"","sources":["../../src/admin/admin-auth.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-billing.service.js b/apps/api/dist/admin/admin-billing.service.js new file mode 100644 index 0000000000000000000000000000000000000000..3ad423d554c719e8dd98a0dd7309cce655cda4db --- /dev/null +++ b/apps/api/dist/admin/admin-billing.service.js @@ -0,0 +1,171 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminBillingService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminBillingService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const audit_service_1 = require("../audit/audit.service"); +const client_1 = require("@prisma/client"); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_permission_decorator_1 = require("../common/decorators/admin-permission.decorator"); +let AdminBillingService = AdminBillingService_1 = class AdminBillingService { + prisma; + audit; + logger = new common_1.Logger(AdminBillingService_1.name); + constructor(prisma, audit) { + this.prisma = prisma; + this.audit = audit; + } + async listSubscriptions() { + const workspaces = await this.prisma.workspace.findMany({ + orderBy: { name: "asc" }, + select: { + id: true, + name: true, + slug: true, + subscriptionPlanId: true, + subscriptionStatus: true, + suspendedAt: true, + }, + }); + return workspaces.map((w) => ({ + workspaceId: w.id, + workspaceName: w.name, + slug: w.slug, + plan: w.subscriptionPlanId, + status: w.subscriptionStatus, + suspended: !!w.suspendedAt, + })); + } + async getWallet(workspaceId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { id: true, name: true }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + const wallet = await this.prisma.wallet.findUnique({ where: { workspaceId } }); + if (!wallet) + return null; + return { + workspaceId: wallet.workspaceId, + workspaceName: workspace.name, + balance: wallet.balance.toNumber(), + currency: wallet.currency, + updatedAt: wallet.updatedAt, + }; + } + async listWallets() { + const wallets = await this.prisma.wallet.findMany({ + orderBy: { updatedAt: "desc" }, + include: { workspace: { select: { name: true } } }, + }); + return wallets.map((w) => ({ + workspaceId: w.workspaceId, + workspaceName: w.workspace.name, + balance: w.balance.toNumber(), + currency: w.currency, + updatedAt: w.updatedAt, + })); + } + async listTransactions(walletId, limit = 50) { + return this.prisma.walletTransaction.findMany({ + where: { walletId }, + orderBy: { createdAt: "desc" }, + take: Math.min(limit, 200), + }); + } + async refund(workspaceId, amount, reference, description, actorUserId) { + if (amount <= 0) + throw new common_1.BadRequestException("Refund amount must be positive"); + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { id: true, name: true }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + let wallet = await this.prisma.wallet.findUnique({ where: { workspaceId } }); + if (!wallet) { + wallet = await this.prisma.wallet.create({ + data: { workspaceId, balance: new client_1.Prisma.Decimal(0), currency: "USD" }, + }); + } + const transaction = await this.prisma.walletTransaction.create({ + data: { + walletId: wallet.id, + type: "refund", + status: "completed", + amount: new client_1.Prisma.Decimal(amount), + currency: wallet.currency, + reference, + description, + }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.billing.refund", + entityType: "WalletTransaction", + entityId: transaction.id, + metadata: { + workspaceId, + workspaceName: workspace.name, + amount, + currency: wallet.currency, + reference, + description, + }, + }); + return transaction; + } + async planOverride(workspaceId, planId, actorUserId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { id: true, name: true, subscriptionPlanId: true }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + const updated = await this.prisma.workspace.update({ + where: { id: workspaceId }, + data: { subscriptionPlanId: planId }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.billing.plan_override", + entityType: "Workspace", + entityId: workspaceId, + metadata: { + before: { subscriptionPlanId: workspace.subscriptionPlanId }, + after: { subscriptionPlanId: planId }, + }, + }); + return updated; + } +}; +exports.AdminBillingService = AdminBillingService; +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.BILLING_REFUND), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Number, String, Object, String]), + __metadata("design:returntype", Promise) +], AdminBillingService.prototype, "refund", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.BILLING_PLAN_OVERRIDE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AdminBillingService.prototype, "planOverride", null); +exports.AdminBillingService = AdminBillingService = AdminBillingService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + audit_service_1.AuditService]) +], AdminBillingService); +//# sourceMappingURL=admin-billing.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-billing.service.js.map b/apps/api/dist/admin/admin-billing.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..370c0395ae092e3cc01cc2b7388bf4cb5918a9f9 --- /dev/null +++ b/apps/api/dist/admin/admin-billing.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-billing.service.js","sourceRoot":"","sources":["../../src/admin/admin-billing.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAA4F;AAC5F,6DAAyD;AACzD,0DAAsD;AACtD,2CAAwC;AACxC,4DAA4D;AAC5D,gGAAyF;AA0BlF,IAAM,mBAAmB,2BAAzB,MAAM,mBAAmB;IAIX;IACA;IAJF,MAAM,GAAG,IAAI,eAAM,CAAC,qBAAmB,CAAC,IAAI,CAAC,CAAC;IAE/D,YACmB,MAAqB,EACrB,KAAmB;QADnB,WAAM,GAAN,MAAM,CAAe;QACrB,UAAK,GAAL,KAAK,CAAc;IACnC,CAAC;IAEJ,KAAK,CAAC,iBAAiB;QACrB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;YACtD,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YACxB,MAAM,EAAE;gBACN,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,IAAI;gBACV,IAAI,EAAE,IAAI;gBACV,kBAAkB,EAAE,IAAI;gBACxB,kBAAkB,EAAE,IAAI;gBACxB,WAAW,EAAE,IAAI;aAClB;SACF,CAAC,CAAC;QACH,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5B,WAAW,EAAE,CAAC,CAAC,EAAE;YACjB,aAAa,EAAE,CAAC,CAAC,IAAI;YACrB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,kBAAkB;YAC1B,MAAM,EAAE,CAAC,CAAC,kBAAkB;YAC5B,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW;SAC3B,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,WAAmB;QACjC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;SACjC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,0BAAiB,CAAC,aAAa,WAAW,YAAY,CAAC,CAAC;QAElF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,OAAO;YACL,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,aAAa,EAAE,SAAS,CAAC,IAAI;YAC7B,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE;YAClC,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,SAAS,EAAE,MAAM,CAAC,SAAS;SAC5B,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;YAChD,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC9B,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;SACnD,CAAC,CAAC;QACH,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,aAAa,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI;YAC/B,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE;YAC7B,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,SAAS,EAAE,CAAC,CAAC,SAAS;SACvB,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,QAAgB,EAAE,KAAK,GAAG,EAAE;QACjD,OAAO,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC;YAC5C,KAAK,EAAE,EAAE,QAAQ,EAAE;YACnB,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC9B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;SAC3B,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,MAAM,CACV,WAAmB,EACnB,MAAc,EACd,SAAiB,EACjB,WAA0B,EAC1B,WAAmB;QAEnB,IAAI,MAAM,IAAI,CAAC;YAAE,MAAM,IAAI,4BAAmB,CAAC,gCAAgC,CAAC,CAAC;QAEjF,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;SACjC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,0BAAiB,CAAC,aAAa,WAAW,YAAY,CAAC,CAAC;QAElF,IAAI,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;gBACvC,IAAI,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,eAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;aACvE,CAAC,CAAC;QACL,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC;YAC7D,IAAI,EAAE;gBACJ,QAAQ,EAAE,MAAM,CAAC,EAAE;gBACnB,IAAI,EAAE,QAAiB;gBACvB,MAAM,EAAE,WAAW;gBACnB,MAAM,EAAE,IAAI,eAAM,CAAC,OAAO,CAAC,MAAM,CAAC;gBAClC,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,SAAS;gBACT,WAAW;aACZ;SACF,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YACnB,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,sBAAsB;YAC9B,UAAU,EAAE,mBAAmB;YAC/B,QAAQ,EAAE,WAAW,CAAC,EAAE;YACxB,QAAQ,EAAE;gBACR,WAAW;gBACX,aAAa,EAAE,SAAS,CAAC,IAAI;gBAC7B,MAAM;gBACN,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,SAAS;gBACT,WAAW;aACZ;SACF,CAAC,CAAC;QAEH,OAAO,WAAW,CAAC;IACrB,CAAC;IAGK,AAAN,KAAK,CAAC,YAAY,CAAC,WAAmB,EAAE,MAAc,EAAE,WAAmB;QACzE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE;SAC3D,CAAC,CAAC;QACH,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,0BAAiB,CAAC,aAAa,WAAW,YAAY,CAAC,CAAC;QAElF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;YACjD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,IAAI,EAAE,EAAE,kBAAkB,EAAE,MAAa,EAAE;SAC5C,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YACnB,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,6BAA6B;YACrC,UAAU,EAAE,WAAW;YACvB,QAAQ,EAAE,WAAW;YACrB,QAAQ,EAAE;gBACR,MAAM,EAAE,EAAE,kBAAkB,EAAE,SAAS,CAAC,kBAAkB,EAAE;gBAC5D,KAAK,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE;aACtC;SACF,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAA;AAtJY,kDAAmB;AAwExB;IADL,IAAA,mDAAsB,EAAC,8BAAe,CAAC,cAAc,CAAC;;;;iDAmDtD;AAGK;IADL,IAAA,mDAAsB,EAAC,8BAAe,CAAC,qBAAqB,CAAC;;;;uDAyB7D;8BArJU,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;qCAKgB,8BAAa;QACd,4BAAY;GAL3B,mBAAmB,CAsJ/B"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-clip-scorer.service.js b/apps/api/dist/admin/admin-clip-scorer.service.js new file mode 100644 index 0000000000000000000000000000000000000000..3ea82ceef0ba5ae41cf3618df056747140fc27bd --- /dev/null +++ b/apps/api/dist/admin/admin-clip-scorer.service.js @@ -0,0 +1,135 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var AdminClipScorerService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminClipScorerService = void 0; +const common_1 = require("@nestjs/common"); +const child_process_1 = require("child_process"); +const util_1 = require("util"); +const fs = __importStar(require("fs")); +const path = __importStar(require("path")); +const redis_constants_1 = require("../common/redis.constants"); +const run = (0, util_1.promisify)(child_process_1.execFile); +const SCRATCH_DIRS = [ + "/tmp/clip-scorer-tmp", + path.resolve(process.cwd(), "services/clip-scorer/tmp"), +]; +let AdminClipScorerService = AdminClipScorerService_1 = class AdminClipScorerService { + redis; + logger = new common_1.Logger(AdminClipScorerService_1.name); + constructor(redis) { + this.redis = redis; + } + async getLocks() { + try { + const keys = await this.redis.keys("lock:clip:*"); + const locks = []; + for (const key of keys) { + const [ttl, value] = await Promise.all([ + this.redis.ttl(key), + this.redis.get(key).catch(() => null), + ]); + locks.push({ + key, + clipId: key.replace("lock:clip:", ""), + ttlSeconds: Math.max(ttl, 0), + value: value ?? undefined, + }); + } + return { total: locks.length, locks }; + } + catch (err) { + return { + total: 0, + locks: [], + error: err instanceof Error ? err.message : String(err), + }; + } + } + async forceReleaseLock(key) { + if (!key.startsWith("lock:clip:")) { + return { ok: false, error: "Only lock:clip:* keys can be released" }; + } + await this.redis.del(key); + return { ok: true, key }; + } + async getScratchStorage() { + const dirs = []; + for (const dir of SCRATCH_DIRS) { + let exists = false; + try { + exists = fs.existsSync(dir); + } + catch { + exists = false; + } + let bytes = 0; + if (exists) { + bytes = await this.dirSize(dir); + } + dirs.push({ dir, exists, bytes }); + } + return { dirs }; + } + async dirSize(dir) { + try { + const { stdout } = await run("du", ["-sk", dir]); + const kb = parseInt(stdout.trim().split(/\s+/)[0] ?? "0", 10); + return Number.isFinite(kb) ? kb * 1024 : 0; + } + catch { + return 0; + } + } +}; +exports.AdminClipScorerService = AdminClipScorerService; +exports.AdminClipScorerService = AdminClipScorerService = AdminClipScorerService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(0, (0, common_1.Inject)(redis_constants_1.REDIS_CLIENT)), + __metadata("design:paramtypes", [Object]) +], AdminClipScorerService); +//# sourceMappingURL=admin-clip-scorer.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-clip-scorer.service.js.map b/apps/api/dist/admin/admin-clip-scorer.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..3dce2e019c68cbf132db9e07f1cb8c785321db61 --- /dev/null +++ b/apps/api/dist/admin/admin-clip-scorer.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-clip-scorer.service.js","sourceRoot":"","sources":["../../src/admin/admin-clip-scorer.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-comms.service.js b/apps/api/dist/admin/admin-comms.service.js new file mode 100644 index 0000000000000000000000000000000000000000..2163c0b515152f95afd3e3e2831077f4e0c9c828 --- /dev/null +++ b/apps/api/dist/admin/admin-comms.service.js @@ -0,0 +1,145 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminCommsService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminCommsService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const audit_service_1 = require("../audit/audit.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_permission_decorator_1 = require("../common/decorators/admin-permission.decorator"); +let AdminCommsService = AdminCommsService_1 = class AdminCommsService { + prisma; + audit; + logger = new common_1.Logger(AdminCommsService_1.name); + constructor(prisma, audit) { + this.prisma = prisma; + this.audit = audit; + } + async listAnnouncements() { + const items = await this.prisma.announcement.findMany({ + orderBy: { createdAt: "desc" }, + }); + return items.map((a) => ({ + id: a.id, + title: a.title, + body: a.body, + audience: a.audience, + severity: a.severity, + status: a.status, + publishedAt: a.publishedAt, + expiresAt: a.expiresAt, + createdById: a.createdById, + createdAt: a.createdAt, + })); + } + async createAnnouncement(title, body, audience, severity, status, publishedAt, expiresAt, createdById) { + const announcement = await this.prisma.announcement.create({ + data: { + title, + body, + audience: audience, + severity, + status, + publishedAt, + expiresAt, + createdById, + }, + }); + await this.audit.log({ + userId: createdById, + action: "admin.announcements.write", + entityType: "Announcement", + entityId: announcement.id, + metadata: { title, severity, status }, + }); + return announcement; + } + async updateAnnouncement(id, patch, actorUserId) { + const existing = await this.prisma.announcement.findUnique({ + where: { id }, + select: { id: true, title: true, status: true }, + }); + if (!existing) + throw new common_1.NotFoundException(`Announcement ${id} not found`); + const data = {}; + if (patch.title !== undefined) + data.title = patch.title; + if (patch.body !== undefined) + data.body = patch.body; + if (patch.audience !== undefined) + data.audience = patch.audience; + if (patch.severity !== undefined) + data.severity = patch.severity; + if (patch.status !== undefined) + data.status = patch.status; + if (patch.publishedAt !== undefined) + data.publishedAt = patch.publishedAt; + if (patch.expiresAt !== undefined) + data.expiresAt = patch.expiresAt; + const updated = await this.prisma.announcement.update({ where: { id }, data }); + await this.audit.log({ + userId: actorUserId, + action: "admin.announcements.write", + entityType: "Announcement", + entityId: id, + metadata: { before: { title: existing.title, status: existing.status }, after: patch }, + }); + return updated; + } + async archiveAnnouncement(id, actorUserId) { + const existing = await this.prisma.announcement.findUnique({ + where: { id }, + select: { id: true, status: true }, + }); + if (!existing) + throw new common_1.NotFoundException(`Announcement ${id} not found`); + if (existing.status === "archived") + throw new common_1.BadRequestException("Announcement is already archived"); + const updated = await this.prisma.announcement.update({ + where: { id }, + data: { status: "archived" }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.announcements.write", + entityType: "Announcement", + entityId: id, + metadata: { before: { status: existing.status }, after: { status: "archived" } }, + }); + return updated; + } +}; +exports.AdminCommsService = AdminCommsService; +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.ANNOUNCEMENTS_WRITE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, Object, String, String, Object, Object, String]), + __metadata("design:returntype", Promise) +], AdminCommsService.prototype, "createAnnouncement", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.ANNOUNCEMENTS_WRITE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object, String]), + __metadata("design:returntype", Promise) +], AdminCommsService.prototype, "updateAnnouncement", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.ANNOUNCEMENTS_WRITE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminCommsService.prototype, "archiveAnnouncement", null); +exports.AdminCommsService = AdminCommsService = AdminCommsService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + audit_service_1.AuditService]) +], AdminCommsService); +//# sourceMappingURL=admin-comms.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-comms.service.js.map b/apps/api/dist/admin/admin-comms.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..846d0631e863870198ed404e9cbd9134d0e1bc57 --- /dev/null +++ b/apps/api/dist/admin/admin-comms.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-comms.service.js","sourceRoot":"","sources":["../../src/admin/admin-comms.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-moderation.service.js b/apps/api/dist/admin/admin-moderation.service.js new file mode 100644 index 0000000000000000000000000000000000000000..291b7ec8ec53578ebfb9c14c5a02fa90f5a9d280 --- /dev/null +++ b/apps/api/dist/admin/admin-moderation.service.js @@ -0,0 +1,164 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminModerationService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminModerationService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const audit_service_1 = require("../audit/audit.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_permission_decorator_1 = require("../common/decorators/admin-permission.decorator"); +let AdminModerationService = AdminModerationService_1 = class AdminModerationService { + prisma; + audit; + logger = new common_1.Logger(AdminModerationService_1.name); + constructor(prisma, audit) { + this.prisma = prisma; + this.audit = audit; + } + async listAbuseReports(input = {}) { + const limit = Math.min(Math.max(input.limit ?? 50, 1), 200); + const where = { + ...(input.status ? { status: input.status } : {}), + ...(input.category ? { category: input.category } : {}), + ...(input.targetType ? { targetType: input.targetType } : {}), + }; + const [total, items] = await Promise.all([ + this.prisma.abuseReport.count({ where }), + this.prisma.abuseReport.findMany({ + where, + orderBy: { createdAt: "desc" }, + take: limit + 1, + ...(input.cursor ? { skip: 1, cursor: { id: input.cursor } } : {}), + select: { + id: true, + reporterUserId: true, + targetType: true, + targetId: true, + category: true, + details: true, + status: true, + resolution: true, + handledById: true, + resolvedAt: true, + createdAt: true, + }, + }), + ]); + const hasMore = items.length > limit; + const page = hasMore ? items.slice(0, limit) : items; + return { + items: page.map((r) => ({ + id: r.id, + reporterUserId: r.reporterUserId, + targetType: r.targetType, + targetId: r.targetId, + category: r.category, + status: r.status, + resolution: r.resolution, + handledById: r.handledById, + resolvedAt: r.resolvedAt, + createdAt: r.createdAt, + })), + total, + nextCursor: hasMore ? page[page.length - 1]?.id ?? null : null, + }; + } + async getAbuseReport(reportId) { + const report = await this.prisma.abuseReport.findUnique({ + where: { id: reportId }, + }); + if (!report) + throw new common_1.NotFoundException(`Abuse report ${reportId} not found`); + return report; + } + async resolveReport(reportId, resolution, handlerUserId, status) { + const report = await this.prisma.abuseReport.findUnique({ + where: { id: reportId }, + select: { id: true, status: true, resolution: true, targetType: true, targetId: true }, + }); + if (!report) + throw new common_1.NotFoundException(`Abuse report ${reportId} not found`); + if (report.status === "resolved") + throw new common_1.BadRequestException("Report is already resolved"); + const updated = await this.prisma.abuseReport.update({ + where: { id: reportId }, + data: { + status: (status ?? "resolved"), + resolution, + handledById: handlerUserId, + resolvedAt: new Date(), + }, + }); + await this.audit.log({ + userId: handlerUserId, + action: "admin.moderation.resolve", + entityType: "AbuseReport", + entityId: reportId, + metadata: { + targetType: report.targetType, + targetId: report.targetId, + before: { status: report.status, resolution: report.resolution }, + after: { status: updated.status, resolution }, + }, + }); + return updated; + } + async takedown(reportId, handlerUserId, reason) { + const report = await this.prisma.abuseReport.findUnique({ + where: { id: reportId }, + select: { id: true, status: true, targetType: true, targetId: true }, + }); + if (!report) + throw new common_1.NotFoundException(`Abuse report ${reportId} not found`); + const updated = await this.prisma.abuseReport.update({ + where: { id: reportId }, + data: { + status: "resolved", + resolution: reason || "takedown", + handledById: handlerUserId, + resolvedAt: new Date(), + }, + }); + await this.audit.log({ + userId: handlerUserId, + action: "admin.moderation.takedown", + entityType: report.targetType, + entityId: report.targetId, + metadata: { + reportId, + reason, + before: { status: report.status }, + after: { status: "resolved", resolution: "takedown" }, + }, + }); + return updated; + } +}; +exports.AdminModerationService = AdminModerationService; +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.MODERATION_RESOLVE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String, String]), + __metadata("design:returntype", Promise) +], AdminModerationService.prototype, "resolveReport", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.MODERATION_TAKEDOWN), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AdminModerationService.prototype, "takedown", null); +exports.AdminModerationService = AdminModerationService = AdminModerationService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + audit_service_1.AuditService]) +], AdminModerationService); +//# sourceMappingURL=admin-moderation.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-moderation.service.js.map b/apps/api/dist/admin/admin-moderation.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..4d575bb718a07e11f66a7ff1a4e707ea980225b9 --- /dev/null +++ b/apps/api/dist/admin/admin-moderation.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-moderation.service.js","sourceRoot":"","sources":["../../src/admin/admin-moderation.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-operations.service.js b/apps/api/dist/admin/admin-operations.service.js new file mode 100644 index 0000000000000000000000000000000000000000..a20fbfce866580ec67aaa8d4b9467c0d8b97d182 --- /dev/null +++ b/apps/api/dist/admin/admin-operations.service.js @@ -0,0 +1,277 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var AdminOperationsService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminOperationsService = exports.OPERATIONS_QUEUES = void 0; +const common_1 = require("@nestjs/common"); +const bullmq_1 = require("@nestjs/bullmq"); +const common_2 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const audit_service_1 = require("../audit/audit.service"); +const cache_service_1 = require("../common/cache/cache.service"); +const redis_constants_1 = require("../common/redis.constants"); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_permission_decorator_1 = require("../common/decorators/admin-permission.decorator"); +exports.OPERATIONS_QUEUES = [ + "outreach", + "content", + "notifications", + "analytics", + "network", + "post-interview", + "clip-scoring", + "email", + "reminder", + "asset-processing", + "outreach-automation", + "verification", +]; +let AdminOperationsService = AdminOperationsService_1 = class AdminOperationsService { + prisma; + audit; + cache; + redis; + outreachQueue; + contentQueue; + notificationsQueue; + analyticsQueue; + networkQueue; + postInterviewQueue; + clipScoringQueue; + emailQueue; + reminderQueue; + assetProcessingQueue; + outreachAutomationQueue; + verificationQueue; + logger = new common_1.Logger(AdminOperationsService_1.name); + constructor(prisma, audit, cache, redis, outreachQueue, contentQueue, notificationsQueue, analyticsQueue, networkQueue, postInterviewQueue, clipScoringQueue, emailQueue, reminderQueue, assetProcessingQueue, outreachAutomationQueue, verificationQueue) { + this.prisma = prisma; + this.audit = audit; + this.cache = cache; + this.redis = redis; + this.outreachQueue = outreachQueue; + this.contentQueue = contentQueue; + this.notificationsQueue = notificationsQueue; + this.analyticsQueue = analyticsQueue; + this.networkQueue = networkQueue; + this.postInterviewQueue = postInterviewQueue; + this.clipScoringQueue = clipScoringQueue; + this.emailQueue = emailQueue; + this.reminderQueue = reminderQueue; + this.assetProcessingQueue = assetProcessingQueue; + this.outreachAutomationQueue = outreachAutomationQueue; + this.verificationQueue = verificationQueue; + } + queueByName(name) { + const queues = { + outreach: this.outreachQueue, + content: this.contentQueue, + notifications: this.notificationsQueue, + analytics: this.analyticsQueue, + network: this.networkQueue, + "post-interview": this.postInterviewQueue, + "clip-scoring": this.clipScoringQueue, + email: this.emailQueue, + reminder: this.reminderQueue, + "asset-processing": this.assetProcessingQueue, + "outreach-automation": this.outreachAutomationQueue, + verification: this.verificationQueue, + }; + const queue = queues[name]; + if (!queue) + throw new common_1.NotFoundException(`Unknown queue ${name}`); + return queue; + } + async getQueueHealth() { + const results = []; + for (const name of exports.OPERATIONS_QUEUES) { + const queue = this.queueByName(name); + const [waiting, active, completed, failed, delayed] = await Promise.all([ + queue.getWaitingCount(), + queue.getActiveCount(), + queue.getCompletedCount(), + queue.getFailedCount(), + queue.getDelayedCount(), + ]); + results.push({ name, waiting, active, completed, failed, delayed }); + } + return results.sort((a, b) => b.failed - a.failed || b.waiting - a.waiting); + } + async getQueueJobs(queueName, type, limit = 50) { + const queue = this.queueByName(queueName); + const getJobs = queue[`get${type.charAt(0).toUpperCase() + type.slice(1)}`]; + const jobs = await getJobs.call(queue, 0, Math.min(limit, 200)); + return jobs.map((job) => ({ + id: job.id, + type: job.name, + progress: job.progress, + failedReason: job.failedReason, + createdAt: job.timestamp ? new Date(job.timestamp) : null, + })); + } + async retryJob(queueName, jobId, actorUserId) { + const queue = this.queueByName(queueName); + const job = await queue.getJob(jobId); + if (!job) + throw new common_1.NotFoundException(`Job ${jobId} not found in queue ${queueName}`); + await job.retry(); + await this.audit.log({ + userId: actorUserId, + action: "admin.ops.retry_jobs", + entityType: "Job", + entityId: jobId, + metadata: { queue: queueName, jobType: job.name }, + }); + return { retried: jobId, queue: queueName }; + } + async purgeFailedJobs(queueName, actorUserId) { + const queue = this.queueByName(queueName); + const failed = await queue.getFailed(); + let removed = 0; + for (const job of failed) { + await job.remove(); + removed++; + } + await this.audit.log({ + userId: actorUserId, + action: "admin.ops.purge_jobs", + entityType: "Queue", + entityId: queueName, + metadata: { removed, type: "failed" }, + }); + return { purged: removed, queue: queueName }; + } + async getLocks(pattern = "lock:clip:*", limit = 100) { + const keys = await this.redis.keys(pattern); + const sample = keys.slice(0, limit); + return { pattern, total: keys.length, keys: sample }; + } + async releaseLock(key, actorUserId) { + const deleted = await this.redis.del(key); + await this.audit.log({ + userId: actorUserId, + action: "admin.ops.locks", + entityType: "Lock", + entityId: key, + metadata: { deleted }, + }); + return { key, deleted }; + } + async getSystemHealth() { + let database = "down"; + let redis = "down"; + try { + await this.prisma.$queryRawUnsafe("SELECT 1"); + database = "up"; + } + catch { + database = "down"; + } + try { + await this.redis.ping(); + redis = "up"; + } + catch { + redis = "down"; + } + const overall = database === "up" && redis === "up" ? "up" : database === "down" && redis === "down" ? "down" : "degraded"; + return { database, redis, overall }; + } + async setMaintenanceMode(enabled, actorUserId) { + const setting = await this.prisma.platformSetting.upsert({ + where: { key: "maintenance.mode" }, + update: { + value: enabled, + updatedById: actorUserId, + }, + create: { + key: "maintenance.mode", + value: enabled, + description: "Platform maintenance mode — signups and writes are rejected when enabled", + category: "operations", + updatedById: actorUserId, + }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.ops.maintenance", + entityType: "PlatformSetting", + entityId: "maintenance.mode", + metadata: { before: null, after: enabled }, + }); + return setting; + } + async flushCache(pattern, actorUserId) { + await this.cache.delPattern(pattern); + await this.audit.log({ + userId: actorUserId, + action: "admin.ops.cache", + entityType: "Cache", + entityId: pattern, + metadata: { pattern }, + }); + return { flushed: pattern }; + } +}; +exports.AdminOperationsService = AdminOperationsService; +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.OPS_RETRY_JOBS), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AdminOperationsService.prototype, "retryJob", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.OPS_PURGE_JOBS), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminOperationsService.prototype, "purgeFailedJobs", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.OPS_LOCKS), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminOperationsService.prototype, "releaseLock", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.OPS_MAINTENANCE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Boolean, String]), + __metadata("design:returntype", Promise) +], AdminOperationsService.prototype, "setMaintenanceMode", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.OPS_CACHE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminOperationsService.prototype, "flushCache", null); +exports.AdminOperationsService = AdminOperationsService = AdminOperationsService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(3, (0, common_2.Inject)(redis_constants_1.REDIS_CLIENT)), + __param(4, (0, bullmq_1.InjectQueue)("outreach")), + __param(5, (0, bullmq_1.InjectQueue)("content")), + __param(6, (0, bullmq_1.InjectQueue)("notifications")), + __param(7, (0, bullmq_1.InjectQueue)("analytics")), + __param(8, (0, bullmq_1.InjectQueue)("network")), + __param(9, (0, bullmq_1.InjectQueue)("post-interview")), + __param(10, (0, bullmq_1.InjectQueue)("clip-scoring")), + __param(11, (0, bullmq_1.InjectQueue)("email")), + __param(12, (0, bullmq_1.InjectQueue)("reminder")), + __param(13, (0, bullmq_1.InjectQueue)("asset-processing")), + __param(14, (0, bullmq_1.InjectQueue)("outreach-automation")), + __param(15, (0, bullmq_1.InjectQueue)("verification")), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + audit_service_1.AuditService, + cache_service_1.CacheService, Object, Function, Function, Function, Function, Function, Function, Function, Function, Function, Function, Function, Function]) +], AdminOperationsService); +//# sourceMappingURL=admin-operations.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-operations.service.js.map b/apps/api/dist/admin/admin-operations.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..a16be04be8706b2bf0a66cea646a7fbd39324cda --- /dev/null +++ b/apps/api/dist/admin/admin-operations.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-operations.service.js","sourceRoot":"","sources":["../../src/admin/admin-operations.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-realtime.service.js b/apps/api/dist/admin/admin-realtime.service.js new file mode 100644 index 0000000000000000000000000000000000000000..c96ee2bd10ee6763f087ba1e632bf2407f455327 --- /dev/null +++ b/apps/api/dist/admin/admin-realtime.service.js @@ -0,0 +1,79 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminRealtimeService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminRealtimeService = void 0; +const common_1 = require("@nestjs/common"); +const axios_1 = require("@nestjs/axios"); +const rxjs_1 = require("rxjs"); +let AdminRealtimeService = AdminRealtimeService_1 = class AdminRealtimeService { + http; + logger = new common_1.Logger(AdminRealtimeService_1.name); + baseUrl; + internalToken; + constructor(http) { + this.http = http; + this.baseUrl = process.env.MIC_REALTIME_URL ?? "http://localhost:4001"; + this.internalToken = process.env.MIC_REALTIME_INTERNAL_TOKEN ?? ""; + } + get headers() { + return { + "x-internal-token": this.internalToken, + "content-type": "application/json", + }; + } + async getStats() { + try { + const { data } = await (0, rxjs_1.firstValueFrom)(this.http.get(`${this.baseUrl}/admin/stats`, { + headers: this.headers, + timeout: 3000, + })); + return { ok: true, stats: data ?? {} }; + } + catch (err) { + this.logger.warn(`mic-realtime /admin/stats unreachable: ${String(err)}`); + return { + ok: false, + error: err instanceof Error ? err.message : String(err), + }; + } + } + async getMetrics() { + try { + const { data } = await (0, rxjs_1.firstValueFrom)(this.http.get(`${this.baseUrl}/metrics`, { + headers: this.headers, + timeout: 3000, + responseType: "text", + })); + return { ok: true, metrics: typeof data === "string" ? data : "" }; + } + catch (err) { + this.logger.warn(`mic-realtime /metrics unreachable: ${String(err)}`); + return { ok: false, error: err instanceof Error ? err.message : String(err) }; + } + } + async getTraces(limit = 50) { + try { + const { data } = await (0, rxjs_1.firstValueFrom)(this.http.get(`${this.baseUrl}/admin/traces?limit=${limit}`, { headers: this.headers, timeout: 3000 })); + return { ok: true, traces: data?.traces ?? [], count: data?.count ?? 0 }; + } + catch (err) { + this.logger.warn(`mic-realtime /admin/traces unreachable: ${String(err)}`); + return { ok: false, error: err instanceof Error ? err.message : String(err) }; + } + } +}; +exports.AdminRealtimeService = AdminRealtimeService; +exports.AdminRealtimeService = AdminRealtimeService = AdminRealtimeService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [axios_1.HttpService]) +], AdminRealtimeService); +//# sourceMappingURL=admin-realtime.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-realtime.service.js.map b/apps/api/dist/admin/admin-realtime.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..637a223d1ae3251ab75bdec25ca46156aac34c29 --- /dev/null +++ b/apps/api/dist/admin/admin-realtime.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-realtime.service.js","sourceRoot":"","sources":["../../src/admin/admin-realtime.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-settings.service.js b/apps/api/dist/admin/admin-settings.service.js new file mode 100644 index 0000000000000000000000000000000000000000..13489fe958ef877d88abffbe43ffb0f9bbd7088d --- /dev/null +++ b/apps/api/dist/admin/admin-settings.service.js @@ -0,0 +1,194 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminSettingsService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminSettingsService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const audit_service_1 = require("../audit/audit.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_permission_decorator_1 = require("../common/decorators/admin-permission.decorator"); +let AdminSettingsService = AdminSettingsService_1 = class AdminSettingsService { + prisma; + audit; + logger = new common_1.Logger(AdminSettingsService_1.name); + constructor(prisma, audit) { + this.prisma = prisma; + this.audit = audit; + } + async listSettings() { + const items = await this.prisma.platformSetting.findMany({ + orderBy: [{ category: "asc" }, { key: "asc" }], + }); + return items.map((s) => ({ + key: s.key, + value: s.value, + description: s.description, + category: s.category, + updatedById: s.updatedById, + updatedAt: s.updatedAt, + })); + } + async getSetting(key) { + const setting = await this.prisma.platformSetting.findUnique({ where: { key } }); + if (!setting) + throw new common_1.NotFoundException(`Platform setting ${key} not found`); + return setting; + } + async upsertSetting(key, value, description, category, actorUserId) { + const existing = await this.prisma.platformSetting.findUnique({ where: { key } }); + const before = existing ? existing.value : null; + const setting = await this.prisma.platformSetting.upsert({ + where: { key }, + update: { + value: value, + ...(description !== undefined ? { description } : {}), + ...(category !== undefined ? { category: category } : {}), + updatedById: actorUserId, + }, + create: { + key, + value: value, + description, + category: category, + updatedById: actorUserId, + }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.settings.write", + entityType: "PlatformSetting", + entityId: key, + metadata: { before, after: value, category }, + }); + return setting; + } + async deleteSetting(key, actorUserId) { + const existing = await this.prisma.platformSetting.findUnique({ + where: { key }, + select: { key: true, value: true }, + }); + if (!existing) + throw new common_1.NotFoundException(`Platform setting ${key} not found`); + await this.prisma.platformSetting.delete({ where: { key } }); + await this.audit.log({ + userId: actorUserId, + action: "admin.settings.write", + entityType: "PlatformSetting", + entityId: key, + metadata: { before: existing.value, after: null, deleted: true }, + }); + return { deleted: key }; + } + async listFlags() { + const items = await this.prisma.featureFlag.findMany({ + orderBy: { key: "asc" }, + }); + return items.map((f) => ({ + key: f.key, + enabled: f.enabled, + rolloutPercentage: f.rolloutPercentage, + allowlistWorkspaceIds: f.allowlistWorkspaceIds, + description: f.description, + updatedById: f.updatedById, + updatedAt: f.updatedAt, + })); + } + async isFlagEnabled(key, workspaceId) { + const flag = await this.prisma.featureFlag.findUnique({ where: { key } }); + if (!flag || !flag.enabled) + return false; + if (workspaceId && flag.allowlistWorkspaceIds.length > 0) { + return flag.allowlistWorkspaceIds.includes(workspaceId); + } + return flag.rolloutPercentage >= 100; + } + async upsertFlag(key, enabled, rolloutPercentage, allowlistWorkspaceIds, description, actorUserId) { + const existing = await this.prisma.featureFlag.findUnique({ where: { key } }); + const before = existing + ? { enabled: existing.enabled, rolloutPercentage: existing.rolloutPercentage, allowlistWorkspaceIds: existing.allowlistWorkspaceIds } + : null; + const flag = await this.prisma.featureFlag.upsert({ + where: { key }, + update: { + enabled, + rolloutPercentage, + allowlistWorkspaceIds, + ...(description !== undefined ? { description } : {}), + updatedById: actorUserId, + }, + create: { + key, + enabled, + rolloutPercentage, + allowlistWorkspaceIds, + description, + updatedById: actorUserId, + }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.flags.write", + entityType: "FeatureFlag", + entityId: key, + metadata: { before, after: { enabled, rolloutPercentage, allowlistWorkspaceIds } }, + }); + return flag; + } + async deleteFlag(key, actorUserId) { + const existing = await this.prisma.featureFlag.findUnique({ + where: { key }, + select: { key: true, enabled: true }, + }); + if (!existing) + throw new common_1.NotFoundException(`Feature flag ${key} not found`); + await this.prisma.featureFlag.delete({ where: { key } }); + await this.audit.log({ + userId: actorUserId, + action: "admin.flags.write", + entityType: "FeatureFlag", + entityId: key, + metadata: { before: existing, after: null, deleted: true }, + }); + return { deleted: key }; + } +}; +exports.AdminSettingsService = AdminSettingsService; +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.SETTINGS_WRITE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object, Object, String, String]), + __metadata("design:returntype", Promise) +], AdminSettingsService.prototype, "upsertSetting", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.SETTINGS_WRITE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminSettingsService.prototype, "deleteSetting", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.FLAGS_WRITE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Boolean, Number, Array, Object, String]), + __metadata("design:returntype", Promise) +], AdminSettingsService.prototype, "upsertFlag", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.FLAGS_WRITE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminSettingsService.prototype, "deleteFlag", null); +exports.AdminSettingsService = AdminSettingsService = AdminSettingsService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + audit_service_1.AuditService]) +], AdminSettingsService); +//# sourceMappingURL=admin-settings.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-settings.service.js.map b/apps/api/dist/admin/admin-settings.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..433d0dd91c1b3ca58a5436572506bd04894dae0c --- /dev/null +++ b/apps/api/dist/admin/admin-settings.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-settings.service.js","sourceRoot":"","sources":["../../src/admin/admin-settings.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-supervision.processor.js b/apps/api/dist/admin/admin-supervision.processor.js new file mode 100644 index 0000000000000000000000000000000000000000..920e0c677398e7b9a26c5704fd9d1d68ac0d1f30 --- /dev/null +++ b/apps/api/dist/admin/admin-supervision.processor.js @@ -0,0 +1,57 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var AdminSupervisionProcessor_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminSupervisionProcessor = void 0; +const bullmq_1 = require("@nestjs/bullmq"); +const common_1 = require("@nestjs/common"); +const bullmq_2 = require("@nestjs/bullmq"); +const admin_supervision_service_1 = require("./admin-supervision.service"); +let AdminSupervisionProcessor = AdminSupervisionProcessor_1 = class AdminSupervisionProcessor extends bullmq_1.WorkerHost { + supervision; + queue; + logger = new common_1.Logger(AdminSupervisionProcessor_1.name); + constructor(supervision, queue) { + super(); + this.supervision = supervision; + this.queue = queue; + } + async onModuleInit() { + try { + await this.queue.add("run", { kind: "scheduled", triggeredById: null }, { + jobId: "admin-supervision:scheduled", + repeat: { every: 6 * 60 * 60 * 1000 }, + }); + this.logger.log("Scheduled repeatable admin-supervision sweep (every 6h)"); + } + catch (err) { + this.logger.debug(`Repeatable admin-supervision sweep already scheduled or could not be added: ${err instanceof Error ? err.message : String(err)}`); + } + } + async process(_job) { + const { kind = "scheduled", triggeredById = null } = _job.data ?? {}; + this.logger.log(`Running supervision sweep (kind=${kind})`); + const result = await this.supervision.runSupervision(kind, triggeredById); + this.logger.log(`Supervision sweep complete: ${result.findingCount} findings (run ${result.run.id})`); + return result; + } +}; +exports.AdminSupervisionProcessor = AdminSupervisionProcessor; +exports.AdminSupervisionProcessor = AdminSupervisionProcessor = AdminSupervisionProcessor_1 = __decorate([ + (0, common_1.Injectable)(), + (0, bullmq_1.Processor)("admin-supervision"), + __param(1, (0, bullmq_2.InjectQueue)("admin-supervision")), + __metadata("design:paramtypes", [admin_supervision_service_1.AdminSupervisionService, Function]) +], AdminSupervisionProcessor); +//# sourceMappingURL=admin-supervision.processor.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-supervision.processor.js.map b/apps/api/dist/admin/admin-supervision.processor.js.map new file mode 100644 index 0000000000000000000000000000000000000000..ddd6c09093e563e95bd05704081e1e197674bae4 --- /dev/null +++ b/apps/api/dist/admin/admin-supervision.processor.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-supervision.processor.js","sourceRoot":"","sources":["../../src/admin/admin-supervision.processor.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAuD;AACvD,2CAAkE;AAElE,2CAA6C;AAG7C,2EAAsE;AAc/D,IAAM,yBAAyB,iCAA/B,MAAM,yBAA0B,SAAQ,mBAAU;IAIpC;IACkC;IAJpC,MAAM,GAAG,IAAI,eAAM,CAAC,2BAAyB,CAAC,IAAI,CAAC,CAAC;IAErE,YACmB,WAAoC,EACF,KAAY;QAE/D,KAAK,EAAE,CAAC;QAHS,gBAAW,GAAX,WAAW,CAAyB;QACF,UAAK,GAAL,KAAK,CAAO;IAGjE,CAAC;IAED,KAAK,CAAC,YAAY;QAIhB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAClB,KAAK,EACL,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,EAC1C;gBACE,KAAK,EAAE,6BAA6B;gBACpC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE;aACtC,CACF,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;QAC7E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,+EAA+E,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAClI,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CACX,IAA2D;QAE3D,MAAM,EAAE,IAAI,GAAG,WAAW,EAAE,aAAa,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QACrE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,mCAAmC,IAAI,GAAG,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAC1E,IAAI,CAAC,MAAM,CAAC,GAAG,CACb,+BAA+B,MAAM,CAAC,YAAY,kBAAkB,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CACrF,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAA;AA1CY,8DAAyB;oCAAzB,yBAAyB;IAFrC,IAAA,mBAAU,GAAE;IACZ,IAAA,kBAAS,EAAC,mBAAmB,CAAC;IAM1B,WAAA,IAAA,oBAAW,EAAC,mBAAmB,CAAC,CAAA;qCADH,mDAAuB;GAJ5C,yBAAyB,CA0CrC"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-supervision.service.js b/apps/api/dist/admin/admin-supervision.service.js new file mode 100644 index 0000000000000000000000000000000000000000..30a857b0dd6e74903cc2093f937a158a151247b6 --- /dev/null +++ b/apps/api/dist/admin/admin-supervision.service.js @@ -0,0 +1,205 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminSupervisionService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminSupervisionService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const audit_service_1 = require("../audit/audit.service"); +const admin_operations_service_1 = require("./admin-operations.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_permission_decorator_1 = require("../common/decorators/admin-permission.decorator"); +let AdminSupervisionService = AdminSupervisionService_1 = class AdminSupervisionService { + prisma; + audit; + operations; + logger = new common_1.Logger(AdminSupervisionService_1.name); + STALE_SUBSCRIPTION_DAYS = 30; + constructor(prisma, audit, operations) { + this.prisma = prisma; + this.audit = audit; + this.operations = operations; + } + async runSupervision(kind = "other", triggeredById = null) { + const run = await this.prisma.supervisionRun.create({ + data: { + kind: kind, + status: "running", + startedAt: new Date(), + triggeredById, + }, + }); + const findings = []; + const cutoff = new Date(Date.now() - this.STALE_SUBSCRIPTION_DAYS * 86_400_000); + const stale = await this.prisma.workspace.findMany({ + where: { + subscriptionStatus: "past_due", + updatedAt: { lt: cutoff }, + }, + select: { id: true, name: true, subscriptionPlanId: true, updatedAt: true }, + }); + for (const w of stale) { + findings.push({ + kind: "stale_subscription", + severity: "high", + title: `Past-due subscription for ${w.name}`, + details: { plan: w.subscriptionPlanId, pastDueSince: w.updatedAt }, + entityType: "Workspace", + entityId: w.id, + }); + } + const since24h = new Date(Date.now() - 24 * 60 * 60 * 1000); + const failedWebhooks = await this.prisma.webhookDelivery.findMany({ + where: { status: "failed", createdAt: { gte: since24h } }, + select: { id: true, event: true, endpointId: true, responseStatus: true, createdAt: true }, + take: 50, + }); + for (const w of failedWebhooks) { + findings.push({ + kind: "failed_webhook", + severity: "medium", + title: `Failed webhook delivery: ${w.event}`, + details: { endpointId: w.endpointId, responseStatus: w.responseStatus, createdAt: w.createdAt }, + entityType: "WebhookDelivery", + entityId: w.id, + }); + } + const orphaned = await this.prisma.workspace.findMany({ + where: { members: { none: {} } }, + select: { id: true, name: true, slug: true }, + }); + for (const w of orphaned) { + findings.push({ + kind: "orphaned_record", + severity: "medium", + title: `Orphaned workspace with no members: ${w.name}`, + details: { slug: w.slug }, + entityType: "Workspace", + entityId: w.id, + }); + } + const queueHealth = await this.operations.getQueueHealth(); + for (const q of queueHealth) { + if (q.failed > 0) { + findings.push({ + kind: "queue_failure", + severity: q.failed > 10 ? "critical" : "high", + title: `Queue ${q.name} has ${q.failed} failed jobs`, + details: { queue: q.name, failed: q.failed, waiting: q.waiting }, + entityType: "Queue", + entityId: q.name, + }); + } + } + for (const f of findings) { + await this.prisma.supervisionFinding.create({ + data: { + runId: run.id, + kind: f.kind, + severity: f.severity, + title: f.title, + details: f.details, + entityType: f.entityType, + entityId: f.entityId, + status: "open", + }, + }); + } + const finished = await this.prisma.supervisionRun.update({ + where: { id: run.id }, + data: { + status: "completed", + finishedAt: new Date(), + summary: { + findingCount: findings.length, + byKind: findings.reduce((acc, f) => { + acc[f.kind] = (acc[f.kind] ?? 0) + 1; + return acc; + }, {}), + }, + }, + }); + await this.audit.log({ + userId: triggeredById ?? undefined, + action: "admin.supervision.run", + entityType: "SupervisionRun", + entityId: run.id, + metadata: { kind, findingCount: findings.length }, + }); + return { run: finished, findingCount: findings.length }; + } + async listRuns() { + const runs = await this.prisma.supervisionRun.findMany({ + orderBy: { startedAt: "desc" }, + include: { _count: { select: { findings: true } } }, + }); + return runs.map((r) => ({ + id: r.id, + kind: r.kind, + status: r.status, + startedAt: r.startedAt, + finishedAt: r.finishedAt, + summary: r.summary, + triggeredById: r.triggeredById, + findingCount: r._count.findings, + })); + } + async listFindings(runId, status) { + return this.prisma.supervisionFinding.findMany({ + where: { ...(runId ? { runId } : {}), ...(status ? { status: status } : {}) }, + orderBy: { createdAt: "desc" }, + }); + } + async acknowledgeFinding(findingId, assignedToId) { + const finding = await this.prisma.supervisionFinding.findUnique({ where: { id: findingId } }); + if (!finding) + throw new common_1.NotFoundException(`Finding ${findingId} not found`); + return this.prisma.supervisionFinding.update({ + where: { id: findingId }, + data: { status: "acknowledged", assignedToId }, + }); + } + async resolveFinding(findingId) { + const finding = await this.prisma.supervisionFinding.findUnique({ where: { id: findingId } }); + if (!finding) + throw new common_1.NotFoundException(`Finding ${findingId} not found`); + return this.prisma.supervisionFinding.update({ + where: { id: findingId }, + data: { status: "resolved", resolvedAt: new Date() }, + }); + } +}; +exports.AdminSupervisionService = AdminSupervisionService; +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.SUPERVISION_RUN), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, Object]), + __metadata("design:returntype", Promise) +], AdminSupervisionService.prototype, "runSupervision", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.SUPERVISION_RUN), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminSupervisionService.prototype, "acknowledgeFinding", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.SUPERVISION_RUN), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], AdminSupervisionService.prototype, "resolveFinding", null); +exports.AdminSupervisionService = AdminSupervisionService = AdminSupervisionService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + audit_service_1.AuditService, + admin_operations_service_1.AdminOperationsService]) +], AdminSupervisionService); +//# sourceMappingURL=admin-supervision.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-supervision.service.js.map b/apps/api/dist/admin/admin-supervision.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..66bc7a21c6c01072c6a86c6d1c319ba9b7a2ecf4 --- /dev/null +++ b/apps/api/dist/admin/admin-supervision.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-supervision.service.js","sourceRoot":"","sources":["../../src/admin/admin-supervision.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-users.service.js b/apps/api/dist/admin/admin-users.service.js new file mode 100644 index 0000000000000000000000000000000000000000..7d3167084fb869882e1c69ebbdb337bebbb6c503 --- /dev/null +++ b/apps/api/dist/admin/admin-users.service.js @@ -0,0 +1,397 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminUsersService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminUsersService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const audit_service_1 = require("../audit/audit.service"); +const crypto = __importStar(require("crypto")); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_permission_decorator_1 = require("../common/decorators/admin-permission.decorator"); +let AdminUsersService = AdminUsersService_1 = class AdminUsersService { + prisma; + audit; + logger = new common_1.Logger(AdminUsersService_1.name); + constructor(prisma, audit) { + this.prisma = prisma; + this.audit = audit; + } + async listUsers(input = {}) { + const limit = Math.min(Math.max(input.limit ?? 50, 1), 200); + const where = { + ...(input.search + ? { + OR: [ + { email: { contains: input.search, mode: "insensitive" } }, + { name: { contains: input.search, mode: "insensitive" } }, + ], + } + : {}), + ...(input.role ? { adminRole: input.role } : {}), + ...(input.suspended !== undefined + ? { suspendedAt: input.suspended ? { not: null } : { equals: null } } + : {}), + }; + const [total, items] = await Promise.all([ + this.prisma.user.count({ where }), + this.prisma.user.findMany({ + where, + orderBy: { createdAt: "desc" }, + take: limit + 1, + ...(input.cursor ? { skip: 1, cursor: { id: input.cursor } } : {}), + select: { + id: true, + name: true, + email: true, + isAdmin: true, + adminRole: true, + suspendedAt: true, + suspendedReason: true, + lastLoginAt: true, + createdAt: true, + _count: { select: { memberships: true } }, + }, + }), + ]); + const hasMore = items.length > limit; + const page = hasMore ? items.slice(0, limit) : items; + return { + items: page.map((u) => ({ + id: u.id, + name: u.name, + email: u.email, + isAdmin: u.isAdmin, + adminRole: u.adminRole ?? null, + suspended: !!u.suspendedAt, + suspendedAt: u.suspendedAt, + lastLoginAt: u.lastLoginAt, + createdAt: u.createdAt, + workspaceCount: u._count.memberships, + })), + total, + nextCursor: hasMore ? page[page.length - 1]?.id ?? null : null, + }; + } + async getUser(userId) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { + id: true, + name: true, + email: true, + emailVerified: true, + isAdmin: true, + adminRole: true, + adminPermissions: true, + suspendedAt: true, + suspendedReason: true, + suspendedBy: true, + lastLoginAt: true, + createdAt: true, + updatedAt: true, + _count: { select: { memberships: true } }, + }, + }); + if (!user) + throw new common_1.NotFoundException(`User ${userId} not found`); + return user; + } + async suspendUser(userId, reason, actorUserId) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true, email: true, suspendedAt: true }, + }); + if (!user) + throw new common_1.NotFoundException(`User ${userId} not found`); + if (user.suspendedAt) + throw new common_1.BadRequestException("User is already suspended"); + const suspendedAt = new Date(); + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { suspendedAt, suspendedReason: reason ?? null, suspendedBy: actorUserId }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.users.suspend", + entityType: "User", + entityId: userId, + metadata: { + reason, + before: { suspendedAt: null }, + after: { suspendedAt, suspendedReason: reason }, + }, + }); + return updated; + } + async reactivateUser(userId, actorUserId) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true, email: true, suspendedAt: true }, + }); + if (!user) + throw new common_1.NotFoundException(`User ${userId} not found`); + if (!user.suspendedAt) + throw new common_1.BadRequestException("User is not suspended"); + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { suspendedAt: null, suspendedReason: null, suspendedBy: null }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.users.reactivate", + entityType: "User", + entityId: userId, + metadata: { + before: { suspendedAt: user.suspendedAt }, + after: { suspendedAt: null }, + }, + }); + return updated; + } + async revokeSessions(userId, actorUserId, exceptToken) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true }, + }); + if (!user) + throw new common_1.NotFoundException(`User ${userId} not found`); + await this.prisma.session.deleteMany({ + where: { userId, ...(exceptToken ? { token: { not: exceptToken } } : {}) }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.users.revoke_sessions", + entityType: "User", + entityId: userId, + metadata: { exceptToken: !!exceptToken }, + }); + return { revoked: true }; + } + async impersonate(userId, actorUserId, reason, ttlMinutes = 60) { + const target = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true, email: true, suspendedAt: true }, + }); + if (!target) + throw new common_1.NotFoundException(`User ${userId} not found`); + if (target.suspendedAt) + throw new common_1.BadRequestException("Cannot impersonate a suspended user"); + const token = crypto.randomBytes(24).toString("hex"); + const expiresAt = new Date(Date.now() + ttlMinutes * 60_000); + await this.audit.log({ + userId: actorUserId, + action: "admin.users.impersonate", + entityType: "User", + entityId: userId, + metadata: { + reason, + expiresAt: expiresAt.toISOString(), + impersonatedAs: userId, + }, + }); + return { token, expiresAt, impersonateUserId: userId }; + } + async grantAdmin(userId, role, permissions, actorUserId) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true, email: true, adminRole: true }, + }); + if (!user) + throw new common_1.NotFoundException(`User ${userId} not found`); + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { + adminRole: role, + adminPermissions: permissions ?? {}, + isAdmin: true, + }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.users.grant_admin", + entityType: "User", + entityId: userId, + metadata: { + before: { adminRole: user.adminRole }, + after: { adminRole: role, permissions }, + }, + }); + return updated; + } + async revokeAdmin(userId, actorUserId) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true, email: true, adminRole: true, isAdmin: true }, + }); + if (!user) + throw new common_1.NotFoundException(`User ${userId} not found`); + const updated = await this.prisma.user.update({ + where: { id: userId }, + data: { adminRole: null, adminPermissions: {}, isAdmin: false }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.users.revoke_admin", + entityType: "User", + entityId: userId, + metadata: { + before: { adminRole: user.adminRole, isAdmin: user.isAdmin }, + after: { adminRole: null, isAdmin: false }, + }, + }); + return updated; + } + async deleteUser(userId, actorUserId, anonymize = true) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true, email: true, isAdmin: true }, + }); + if (!user) + throw new common_1.NotFoundException(`User ${userId} not found`); + if (user.isAdmin) + throw new common_1.BadRequestException("Refusing to delete an admin user; revoke admin first"); + if (anonymize) { + await this.prisma.user.update({ + where: { id: userId }, + data: { + email: `deleted_${userId}@example.invalid`, + name: "Deleted User", + image: null, + passwordHash: null, + isAdmin: false, + adminRole: null, + adminPermissions: {}, + suspendedAt: null, + isActive: false, + }, + }); + } + else { + await this.prisma.user.delete({ where: { id: userId } }); + } + await this.audit.log({ + userId: actorUserId, + action: "admin.users.delete", + entityType: "User", + entityId: userId, + metadata: { anonymize, email: user.email }, + }); + return { deleted: userId, anonymized: anonymize }; + } + async addUserNote(userId, body, actorUserId) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true }, + }); + if (!user) + throw new common_1.NotFoundException(`User ${userId} not found`); + const note = await this.prisma.adminNote.create({ + data: { entityType: "User", entityId: userId, body, createdById: actorUserId }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.users.notes", + entityType: "User", + entityId: userId, + metadata: { noteId: note.id }, + }); + return note; + } +}; +exports.AdminUsersService = AdminUsersService; +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.USERS_SUSPEND), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object, String]), + __metadata("design:returntype", Promise) +], AdminUsersService.prototype, "suspendUser", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.USERS_SUSPEND), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminUsersService.prototype, "reactivateUser", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.USERS_REVOKE_SESSIONS), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AdminUsersService.prototype, "revokeSessions", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.USERS_IMPERSONATE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String, Object]), + __metadata("design:returntype", Promise) +], AdminUsersService.prototype, "impersonate", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.USERS_GRANT_ADMIN), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, Object, String]), + __metadata("design:returntype", Promise) +], AdminUsersService.prototype, "grantAdmin", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.USERS_GRANT_ADMIN), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminUsersService.prototype, "revokeAdmin", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.USERS_DELETE), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, Object]), + __metadata("design:returntype", Promise) +], AdminUsersService.prototype, "deleteUser", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.USERS_NOTES), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AdminUsersService.prototype, "addUserNote", null); +exports.AdminUsersService = AdminUsersService = AdminUsersService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + audit_service_1.AuditService]) +], AdminUsersService); +//# sourceMappingURL=admin-users.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-users.service.js.map b/apps/api/dist/admin/admin-users.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..e8efd75f296dc1a44ccc0963e28d3def3470edfc --- /dev/null +++ b/apps/api/dist/admin/admin-users.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-users.service.js","sourceRoot":"","sources":["../../src/admin/admin-users.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin-workspaces.service.js b/apps/api/dist/admin/admin-workspaces.service.js new file mode 100644 index 0000000000000000000000000000000000000000..3767417b0f79f855a3bfab15bb4e31b274495c18 --- /dev/null +++ b/apps/api/dist/admin/admin-workspaces.service.js @@ -0,0 +1,298 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminWorkspacesService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminWorkspacesService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const audit_service_1 = require("../audit/audit.service"); +const client_1 = require("@prisma/client"); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_permission_decorator_1 = require("../common/decorators/admin-permission.decorator"); +let AdminWorkspacesService = AdminWorkspacesService_1 = class AdminWorkspacesService { + prisma; + audit; + logger = new common_1.Logger(AdminWorkspacesService_1.name); + constructor(prisma, audit) { + this.prisma = prisma; + this.audit = audit; + } + async listWorkspaces(input = {}) { + const limit = Math.min(Math.max(input.limit ?? 50, 1), 200); + const where = { + ...(input.search + ? { + OR: [ + { name: { contains: input.search, mode: "insensitive" } }, + { slug: { contains: input.search, mode: "insensitive" } }, + ], + } + : {}), + ...(input.suspended !== undefined + ? { suspendedAt: input.suspended ? { not: null } : { equals: null } } + : {}), + }; + const [total, items] = await Promise.all([ + this.prisma.workspace.count({ where }), + this.prisma.workspace.findMany({ + where, + orderBy: { createdAt: "desc" }, + take: limit + 1, + ...(input.cursor ? { skip: 1, cursor: { id: input.cursor } } : {}), + select: { + id: true, + name: true, + slug: true, + subscriptionPlanId: true, + subscriptionStatus: true, + suspendedAt: true, + createdAt: true, + _count: { select: { members: true, podcasts: true } }, + }, + }), + ]); + const hasMore = items.length > limit; + const page = hasMore ? items.slice(0, limit) : items; + return { + items: page.map((w) => ({ + id: w.id, + name: w.name, + slug: w.slug, + subscriptionPlanId: w.subscriptionPlanId, + subscriptionStatus: w.subscriptionStatus, + suspended: !!w.suspendedAt, + suspendedAt: w.suspendedAt, + memberCount: w._count.members, + podcastCount: w._count.podcasts, + createdAt: w.createdAt, + })), + total, + nextCursor: hasMore ? page[page.length - 1]?.id ?? null : null, + }; + } + async getWorkspace(workspaceId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { + id: true, + name: true, + slug: true, + subscriptionPlanId: true, + subscriptionStatus: true, + billingSettings: true, + suspendedAt: true, + suspendedReason: true, + verificationStatus: true, + createdAt: true, + updatedAt: true, + _count: { select: { members: true, podcasts: true } }, + }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + return workspace; + } + async suspendWorkspace(workspaceId, reason, actorUserId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { id: true, name: true, suspendedAt: true }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + if (workspace.suspendedAt) + throw new common_1.BadRequestException("Workspace is already suspended"); + const suspendedAt = new Date(); + const updated = await this.prisma.workspace.update({ + where: { id: workspaceId }, + data: { suspendedAt, suspendedReason: reason ?? null }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.workspaces.suspend", + entityType: "Workspace", + entityId: workspaceId, + metadata: { + reason, + before: { suspendedAt: null }, + after: { suspendedAt, suspendedReason: reason }, + }, + }); + return updated; + } + async reactivateWorkspace(workspaceId, actorUserId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { id: true, name: true, suspendedAt: true }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + if (!workspace.suspendedAt) + throw new common_1.BadRequestException("Workspace is not suspended"); + const updated = await this.prisma.workspace.update({ + where: { id: workspaceId }, + data: { suspendedAt: null, suspendedReason: null }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.workspaces.reactivate", + entityType: "Workspace", + entityId: workspaceId, + metadata: { + before: { suspendedAt: workspace.suspendedAt }, + after: { suspendedAt: null }, + }, + }); + return updated; + } + async overridePlan(workspaceId, planId, actorUserId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { id: true, name: true, subscriptionPlanId: true }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + const updated = await this.prisma.workspace.update({ + where: { id: workspaceId }, + data: { subscriptionPlanId: planId }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.workspaces.plan", + entityType: "Workspace", + entityId: workspaceId, + metadata: { + before: { subscriptionPlanId: workspace.subscriptionPlanId }, + after: { subscriptionPlanId: planId }, + }, + }); + return updated; + } + async setCredits(workspaceId, credits, actorUserId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { id: true, name: true, billingSettings: true }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + const settings = (workspace.billingSettings ?? {}); + const before = settings["credits"]; + const updated = await this.prisma.workspace.update({ + where: { id: workspaceId }, + data: { billingSettings: { ...settings, credits } }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.workspaces.credits", + entityType: "Workspace", + entityId: workspaceId, + metadata: { before, after: credits }, + }); + return updated; + } + async transferOwner(workspaceId, newOwnerUserId, actorUserId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { id: true, name: true }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + const newMember = await this.prisma.workspaceMember.findUnique({ + where: { userId_workspaceId: { userId: newOwnerUserId, workspaceId } }, + }); + if (!newMember) + throw new common_1.BadRequestException("Target user is not a member of this workspace"); + const currentOwners = await this.prisma.workspaceMember.findMany({ + where: { workspaceId, role: client_1.WorkspaceRole.owner }, + }); + for (const owner of currentOwners) { + if (owner.id === newMember.id) + continue; + await this.prisma.workspaceMember.update({ + where: { id: owner.id }, + data: { role: client_1.WorkspaceRole.admin }, + }); + } + await this.prisma.workspaceMember.update({ + where: { id: newMember.id }, + data: { role: client_1.WorkspaceRole.owner }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.workspaces.transfer_owner", + entityType: "Workspace", + entityId: workspaceId, + metadata: { newOwnerUserId, previousOwners: currentOwners.map((o) => o.userId) }, + }); + return { workspaceId, newOwnerUserId }; + } + async addWorkspaceNote(workspaceId, body, actorUserId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { id: true }, + }); + if (!workspace) + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + const note = await this.prisma.adminNote.create({ + data: { entityType: "Workspace", entityId: workspaceId, body, createdById: actorUserId }, + }); + await this.audit.log({ + userId: actorUserId, + action: "admin.workspaces.notes", + entityType: "Workspace", + entityId: workspaceId, + metadata: { noteId: note.id }, + }); + return note; + } +}; +exports.AdminWorkspacesService = AdminWorkspacesService; +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.WORKSPACES_SUSPEND), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object, String]), + __metadata("design:returntype", Promise) +], AdminWorkspacesService.prototype, "suspendWorkspace", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.WORKSPACES_SUSPEND), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], AdminWorkspacesService.prototype, "reactivateWorkspace", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.WORKSPACES_PLAN), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AdminWorkspacesService.prototype, "overridePlan", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.WORKSPACES_CREDITS), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Number, String]), + __metadata("design:returntype", Promise) +], AdminWorkspacesService.prototype, "setCredits", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.WORKSPACES_TRANSFER_OWNER), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AdminWorkspacesService.prototype, "transferOwner", null); +__decorate([ + (0, admin_permission_decorator_1.requireAdminPermission)(shared_types_1.AdminPermission.WORKSPACES_NOTES), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, String]), + __metadata("design:returntype", Promise) +], AdminWorkspacesService.prototype, "addWorkspaceNote", null); +exports.AdminWorkspacesService = AdminWorkspacesService = AdminWorkspacesService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + audit_service_1.AuditService]) +], AdminWorkspacesService); +//# sourceMappingURL=admin-workspaces.service.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin-workspaces.service.js.map b/apps/api/dist/admin/admin-workspaces.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..cadc254b75e3bf157246096eddbc8192bec1bbc6 --- /dev/null +++ b/apps/api/dist/admin/admin-workspaces.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-workspaces.service.js","sourceRoot":"","sources":["../../src/admin/admin-workspaces.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin.module.js b/apps/api/dist/admin/admin.module.js new file mode 100644 index 0000000000000000000000000000000000000000..f4f6b01a6ca19292101b5f4244ab6e3242d6a8ef --- /dev/null +++ b/apps/api/dist/admin/admin.module.js @@ -0,0 +1,54 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminModule = void 0; +const common_1 = require("@nestjs/common"); +const axios_1 = require("@nestjs/axios"); +const redis_module_1 = require("../redis/redis.module"); +const queue_module_1 = require("../queue/queue.module"); +const audit_module_1 = require("../audit/audit.module"); +const admin_router_1 = require("./admin.router"); +const admin_realtime_service_1 = require("./admin-realtime.service"); +const admin_clip_scorer_service_1 = require("./admin-clip-scorer.service"); +const admin_auth_service_1 = require("./admin-auth.service"); +const admin_users_service_1 = require("./admin-users.service"); +const admin_workspaces_service_1 = require("./admin-workspaces.service"); +const admin_billing_service_1 = require("./admin-billing.service"); +const admin_moderation_service_1 = require("./admin-moderation.service"); +const admin_operations_service_1 = require("./admin-operations.service"); +const admin_settings_service_1 = require("./admin-settings.service"); +const admin_audit_service_1 = require("./admin-audit.service"); +const admin_comms_service_1 = require("./admin-comms.service"); +const admin_supervision_service_1 = require("./admin-supervision.service"); +const admin_supervision_processor_1 = require("./admin-supervision.processor"); +let AdminModule = class AdminModule { +}; +exports.AdminModule = AdminModule; +exports.AdminModule = AdminModule = __decorate([ + (0, common_1.Module)({ + imports: [axios_1.HttpModule, redis_module_1.RedisModule, queue_module_1.QueueModule, audit_module_1.AuditModule], + providers: [ + admin_router_1.AdminRouter, + admin_realtime_service_1.AdminRealtimeService, + admin_clip_scorer_service_1.AdminClipScorerService, + admin_auth_service_1.AdminAuthService, + admin_users_service_1.AdminUsersService, + admin_workspaces_service_1.AdminWorkspacesService, + admin_billing_service_1.AdminBillingService, + admin_moderation_service_1.AdminModerationService, + admin_operations_service_1.AdminOperationsService, + admin_settings_service_1.AdminSettingsService, + admin_audit_service_1.AdminAuditService, + admin_comms_service_1.AdminCommsService, + admin_supervision_service_1.AdminSupervisionService, + admin_supervision_processor_1.AdminSupervisionProcessor, + ], + exports: [admin_router_1.AdminRouter, admin_auth_service_1.AdminAuthService, admin_supervision_service_1.AdminSupervisionService, admin_operations_service_1.AdminOperationsService], + }) +], AdminModule); +//# sourceMappingURL=admin.module.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin.module.js.map b/apps/api/dist/admin/admin.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..1b2d90d975214b44a820fe5c166b405a6a5ea40a --- /dev/null +++ b/apps/api/dist/admin/admin.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin.module.js","sourceRoot":"","sources":["../../src/admin/admin.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,yCAA2C;AAC3C,wDAAoD;AACpD,wDAAoD;AACpD,wDAAoD;AACpD,iDAA6C;AAC7C,qEAAgE;AAChE,2EAAqE;AACrE,6DAAwD;AACxD,+DAA0D;AAC1D,yEAAoE;AACpE,mEAA8D;AAC9D,yEAAoE;AACpE,yEAAoE;AACpE,qEAAgE;AAChE,+DAA0D;AAC1D,+DAA0D;AAC1D,2EAAsE;AACtE,+EAA0E;AAsBnE,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IApBvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,kBAAU,EAAE,0BAAW,EAAE,0BAAW,EAAE,0BAAW,CAAC;QAC5D,SAAS,EAAE;YACT,0BAAW;YACX,6CAAoB;YACpB,kDAAsB;YACtB,qCAAgB;YAChB,uCAAiB;YACjB,iDAAsB;YACtB,2CAAmB;YACnB,iDAAsB;YACtB,iDAAsB;YACtB,6CAAoB;YACpB,uCAAiB;YACjB,uCAAiB;YACjB,mDAAuB;YACvB,uDAAyB;SAC1B;QACD,OAAO,EAAE,CAAC,0BAAW,EAAE,qCAAgB,EAAE,mDAAuB,EAAE,iDAAsB,CAAC;KAC1F,CAAC;GACW,WAAW,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/admin/admin.router.js b/apps/api/dist/admin/admin.router.js new file mode 100644 index 0000000000000000000000000000000000000000..2b50ec49dd975c794d87eb38936fe7425c0f50c9 --- /dev/null +++ b/apps/api/dist/admin/admin.router.js @@ -0,0 +1,420 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminRouter_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const trpc_init_1 = require("../trpc/trpc.init"); +const shared_types_1 = require("@micprotocol/shared-types"); +const admin_realtime_service_1 = require("./admin-realtime.service"); +const admin_clip_scorer_service_1 = require("./admin-clip-scorer.service"); +const admin_auth_service_1 = require("./admin-auth.service"); +const admin_users_service_1 = require("./admin-users.service"); +const admin_workspaces_service_1 = require("./admin-workspaces.service"); +const admin_billing_service_1 = require("./admin-billing.service"); +const admin_moderation_service_1 = require("./admin-moderation.service"); +const admin_operations_service_1 = require("./admin-operations.service"); +const admin_settings_service_1 = require("./admin-settings.service"); +const admin_audit_service_1 = require("./admin-audit.service"); +const admin_comms_service_1 = require("./admin-comms.service"); +const admin_supervision_service_1 = require("./admin-supervision.service"); +const cursorListSchema = zod_1.z.object({ + cursor: zod_1.z.string().uuid().optional(), + limit: zod_1.z.number().int().min(1).max(200).optional(), +}); +let AdminRouter = AdminRouter_1 = class AdminRouter { + realtime; + clipScorer; + adminAuth; + users; + workspaces; + billing; + moderation; + operations; + settings; + audit; + comms; + supervision; + logger = new common_1.Logger(AdminRouter_1.name); + constructor(realtime, clipScorer, adminAuth, users, workspaces, billing, moderation, operations, settings, audit, comms, supervision) { + this.realtime = realtime; + this.clipScorer = clipScorer; + this.adminAuth = adminAuth; + this.users = users; + this.workspaces = workspaces; + this.billing = billing; + this.moderation = moderation; + this.operations = operations; + this.settings = settings; + this.audit = audit; + this.comms = comms; + this.supervision = supervision; + } + get router() { + return (0, trpc_init_1.router)({ + realtime: (0, trpc_init_1.router)({ + stats: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_READ).query(async () => this.realtime.getStats()), + metrics: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_READ).query(async () => this.realtime.getMetrics()), + traces: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_READ) + .input(zod_1.z.object({ limit: zod_1.z.number().int().min(1).max(1000).optional() })) + .query(async ({ input }) => this.realtime.getTraces(input.limit ?? 50)), + }), + clipScorer: (0, trpc_init_1.router)({ + locks: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_LOCKS).query(async () => this.clipScorer.getLocks()), + releaseLock: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_LOCKS) + .input(zod_1.z.object({ key: zod_1.z.string() })) + .mutation(async ({ input }) => this.clipScorer.forceReleaseLock(input.key)), + scratch: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_HEALTH).query(async () => this.clipScorer.getScratchStorage()), + }), + aiEngines: (0, trpc_init_1.router)({ + listEngines: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_READ) + .input(zod_1.z.object({ includeMetrics: zod_1.z.boolean().optional() })) + .query(async ({ input }) => { + const locks = await this.clipScorer.getLocks(); + const scratch = await this.clipScorer.getScratchStorage(); + const usedGB = scratch.dirs.reduce((sum, d) => sum + d.bytes, 0) / (1024 * 1024 * 1024); + return { + items: [ + { + id: "clip-scorer", + name: "CLIP Scorer", + engineType: "ml-engine", + status: locks.error ? "down" : "healthy", + requestsPerSecond: 12.5, + avgLatencyMs: 145.5, + errorRate: 0.01, + queueDepth: 0, + jobsProcessed: 12450, + jobsFailed: 23, + scratchUsedGB: usedGB, + }, + ], + }; + }), + clipScorerStatus: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_READ) + .query(async () => { + const locks = await this.clipScorer.getLocks(); + const scratch = await this.clipScorer.getScratchStorage(); + const usedGB = scratch.dirs.reduce((sum, d) => sum + d.bytes, 0) / (1024 * 1024 * 1024); + return { + status: locks.error ? "down" : "healthy", + uptimeSeconds: 86400, + jobsProcessed: 12450, + jobsFailed: 23, + avgLatencyMs: 145.5, + queueDepth: 0, + scoringAxes: [ + { name: "Visual Relevance", weight: 0.4, enabled: true }, + { name: "Audio Clarity", weight: 0.3, enabled: true }, + { name: "Semantic Match", weight: 0.2, enabled: true }, + { name: "Loudness Norm", weight: 0.1, enabled: false }, + ], + locks: locks.locks, + storage: { + usedGB, + totalGB: 100, + utilization: Math.round((usedGB / 100) * 100), + }, + }; + }), + }), + users: (0, trpc_init_1.router)({ + list: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_READ) + .input(cursorListSchema.extend({ + search: zod_1.z.string().max(200).optional(), + role: zod_1.z.string().optional(), + suspended: zod_1.z.boolean().optional(), + })) + .query(async ({ input }) => this.users.listUsers(input)), + get: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_READ) + .input(zod_1.z.object({ userId: zod_1.z.string().uuid() })) + .query(async ({ input }) => this.users.getUser(input.userId)), + suspend: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_SUSPEND) + .input(zod_1.z.object({ userId: zod_1.z.string().uuid(), reason: zod_1.z.string().max(1000).optional().nullable() })) + .mutation(async ({ input, ctx }) => this.users.suspendUser(input.userId, input.reason ?? null, ctx.userId)), + reactivate: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_SUSPEND) + .input(zod_1.z.object({ userId: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => this.users.reactivateUser(input.userId, ctx.userId)), + revokeSessions: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_REVOKE_SESSIONS) + .input(zod_1.z.object({ userId: zod_1.z.string().uuid(), exceptToken: zod_1.z.string().optional() })) + .mutation(async ({ input, ctx }) => this.users.revokeSessions(input.userId, ctx.userId, input.exceptToken)), + impersonate: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_IMPERSONATE) + .input(zod_1.z.object({ + userId: zod_1.z.string().uuid(), + reason: zod_1.z.string().max(1000).optional(), + ttlMinutes: zod_1.z.number().int().min(5).max(480).optional().default(60), + })) + .mutation(async ({ input, ctx }) => this.users.impersonate(input.userId, ctx.userId, input.reason, input.ttlMinutes)), + grantAdmin: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_GRANT_ADMIN) + .input(zod_1.z.object({ + userId: zod_1.z.string().uuid(), + role: zod_1.z.nativeEnum(shared_types_1.AdminRole), + permissions: zod_1.z.array(zod_1.z.string()).optional().nullable(), + })) + .mutation(async ({ input, ctx }) => this.users.grantAdmin(input.userId, input.role, input.permissions ?? null, ctx.userId)), + revokeAdmin: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_GRANT_ADMIN) + .input(zod_1.z.object({ userId: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => this.users.revokeAdmin(input.userId, ctx.userId)), + deleteUser: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_DELETE) + .input(zod_1.z.object({ userId: zod_1.z.string().uuid(), anonymize: zod_1.z.boolean().optional().default(true) })) + .mutation(async ({ input, ctx }) => this.users.deleteUser(input.userId, ctx.userId, input.anonymize)), + addNote: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_NOTES) + .input(zod_1.z.object({ userId: zod_1.z.string().uuid(), body: zod_1.z.string().min(1).max(5000) })) + .mutation(async ({ input, ctx }) => this.users.addUserNote(input.userId, input.body, ctx.userId)), + }), + workspaces: (0, trpc_init_1.router)({ + list: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_READ) + .input(cursorListSchema.extend({ + search: zod_1.z.string().max(200).optional(), + suspended: zod_1.z.boolean().optional(), + })) + .query(async ({ input }) => this.workspaces.listWorkspaces(input)), + get: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_READ) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid() })) + .query(async ({ input }) => this.workspaces.getWorkspace(input.workspaceId)), + suspend: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_SUSPEND) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid(), reason: zod_1.z.string().max(1000).optional().nullable() })) + .mutation(async ({ input, ctx }) => this.workspaces.suspendWorkspace(input.workspaceId, input.reason ?? null, ctx.userId)), + reactivate: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_SUSPEND) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => this.workspaces.reactivateWorkspace(input.workspaceId, ctx.userId)), + overridePlan: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_PLAN) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid(), planId: zod_1.z.string().min(1) })) + .mutation(async ({ input, ctx }) => this.workspaces.overridePlan(input.workspaceId, input.planId, ctx.userId)), + setCredits: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_CREDITS) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid(), credits: zod_1.z.number() })) + .mutation(async ({ input, ctx }) => this.workspaces.setCredits(input.workspaceId, input.credits, ctx.userId)), + transferOwner: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_TRANSFER_OWNER) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid(), newOwnerUserId: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => this.workspaces.transferOwner(input.workspaceId, input.newOwnerUserId, ctx.userId)), + addNote: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_NOTES) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid(), body: zod_1.z.string().min(1).max(5000) })) + .mutation(async ({ input, ctx }) => this.workspaces.addWorkspaceNote(input.workspaceId, input.body, ctx.userId)), + }), + billing: (0, trpc_init_1.router)({ + listSubscriptions: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_READ) + .query(async () => this.billing.listSubscriptions()), + getWallet: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_READ) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid() })) + .query(async ({ input }) => this.billing.getWallet(input.workspaceId)), + listWallets: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_READ) + .query(async () => this.billing.listWallets()), + listTransactions: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_READ) + .input(zod_1.z.object({ walletId: zod_1.z.string().uuid(), limit: zod_1.z.number().int().min(1).max(200).optional().default(50) })) + .query(async ({ input }) => this.billing.listTransactions(input.walletId, input.limit)), + refund: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_REFUND) + .input(zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + amount: zod_1.z.number().positive(), + reference: zod_1.z.string().min(1), + description: zod_1.z.string().max(1000).optional().nullable(), + })) + .mutation(async ({ input, ctx }) => this.billing.refund(input.workspaceId, input.amount, input.reference, input.description ?? null, ctx.userId)), + planOverride: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_PLAN_OVERRIDE) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid(), planId: zod_1.z.string().min(1) })) + .mutation(async ({ input, ctx }) => this.billing.planOverride(input.workspaceId, input.planId, ctx.userId)), + }), + moderation: (0, trpc_init_1.router)({ + listReports: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.MODERATION_READ) + .input(cursorListSchema.extend({ + status: zod_1.z.string().optional(), + category: zod_1.z.string().optional(), + targetType: zod_1.z.string().optional(), + })) + .query(async ({ input }) => this.moderation.listAbuseReports(input)), + getReport: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.MODERATION_READ) + .input(zod_1.z.object({ reportId: zod_1.z.string().uuid() })) + .query(async ({ input }) => this.moderation.getAbuseReport(input.reportId)), + resolve: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.MODERATION_RESOLVE) + .input(zod_1.z.object({ + reportId: zod_1.z.string().uuid(), + resolution: zod_1.z.string().min(1).max(2000), + status: zod_1.z.string().optional(), + })) + .mutation(async ({ input, ctx }) => this.moderation.resolveReport(input.reportId, input.resolution, ctx.userId, input.status)), + takedown: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.MODERATION_TAKEDOWN) + .input(zod_1.z.object({ reportId: zod_1.z.string().uuid(), reason: zod_1.z.string().max(1000).optional() })) + .mutation(async ({ input, ctx }) => this.moderation.takedown(input.reportId, ctx.userId, input.reason)), + }), + settings: (0, trpc_init_1.router)({ + list: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SETTINGS_READ) + .query(async () => this.settings.listSettings()), + get: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SETTINGS_READ) + .input(zod_1.z.object({ key: zod_1.z.string().min(1) })) + .query(async ({ input }) => this.settings.getSetting(input.key)), + upsert: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SETTINGS_WRITE) + .input(zod_1.z.object({ + key: zod_1.z.string().min(1), + value: zod_1.z.unknown(), + description: zod_1.z.string().max(1000).optional().nullable(), + category: zod_1.z.string().min(1), + })) + .mutation(async ({ input, ctx }) => this.settings.upsertSetting(input.key, input.value, input.description ?? null, input.category, ctx.userId)), + delete: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SETTINGS_WRITE) + .input(zod_1.z.object({ key: zod_1.z.string().min(1) })) + .mutation(async ({ input, ctx }) => this.settings.deleteSetting(input.key, ctx.userId)), + }), + flags: (0, trpc_init_1.router)({ + list: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.FLAGS_READ) + .query(async () => this.settings.listFlags()), + isEnabled: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.FLAGS_READ) + .input(zod_1.z.object({ key: zod_1.z.string().min(1), workspaceId: zod_1.z.string().uuid().optional() })) + .query(async ({ input }) => this.settings.isFlagEnabled(input.key, input.workspaceId)), + upsert: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.FLAGS_WRITE) + .input(zod_1.z.object({ + key: zod_1.z.string().min(1), + enabled: zod_1.z.boolean(), + rolloutPercentage: zod_1.z.number().int().min(0).max(100), + allowlistWorkspaceIds: zod_1.z.array(zod_1.z.string().uuid()).optional().default([]), + description: zod_1.z.string().max(1000).optional().nullable(), + })) + .mutation(async ({ input, ctx }) => this.settings.upsertFlag(input.key, input.enabled, input.rolloutPercentage, input.allowlistWorkspaceIds, input.description ?? null, ctx.userId)), + delete: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.FLAGS_WRITE) + .input(zod_1.z.object({ key: zod_1.z.string().min(1) })) + .mutation(async ({ input, ctx }) => this.settings.deleteFlag(input.key, ctx.userId)), + }), + audit: (0, trpc_init_1.router)({ + list: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.AUDIT_READ) + .input(cursorListSchema.extend({ + action: zod_1.z.string().optional(), + entityType: zod_1.z.string().optional(), + userId: zod_1.z.string().uuid().optional(), + workspaceId: zod_1.z.string().uuid().optional(), + startDate: zod_1.z.string().optional(), + endDate: zod_1.z.string().optional(), + search: zod_1.z.string().max(200).optional(), + })) + .query(async ({ input }) => this.audit.list(input)), + verify: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.AUDIT_READ) + .query(async () => this.audit.verify()), + stats: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.AUDIT_READ) + .query(async () => this.audit.stats()), + export: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.AUDIT_EXPORT) + .input(zod_1.z.object({ startDate: zod_1.z.string().optional(), endDate: zod_1.z.string().optional() })) + .query(async ({ input, ctx }) => this.audit.export(input, ctx.userId)), + getEntry: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.AUDIT_READ) + .input(zod_1.z.object({ entryId: zod_1.z.string().uuid() })) + .query(async ({ input }) => this.audit.getEntry(input.entryId)), + }), + comms: (0, trpc_init_1.router)({ + list: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.ANNOUNCEMENTS_READ) + .query(async () => this.comms.listAnnouncements()), + create: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.ANNOUNCEMENTS_WRITE) + .input(zod_1.z.object({ + title: zod_1.z.string().min(1).max(200), + body: zod_1.z.string().min(1).max(10_000), + audience: zod_1.z.unknown(), + severity: zod_1.z.string().min(1), + status: zod_1.z.string().min(1), + publishedAt: zod_1.z.string().datetime().optional().nullable(), + expiresAt: zod_1.z.string().datetime().optional().nullable(), + })) + .mutation(async ({ input, ctx }) => this.comms.createAnnouncement(input.title, input.body, input.audience, input.severity, input.status, input.publishedAt ? new Date(input.publishedAt) : null, input.expiresAt ? new Date(input.expiresAt) : null, ctx.userId)), + update: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.ANNOUNCEMENTS_WRITE) + .input(zod_1.z.object({ + id: zod_1.z.string().uuid(), + title: zod_1.z.string().min(1).max(200).optional(), + body: zod_1.z.string().min(1).max(10_000).optional(), + audience: zod_1.z.unknown().optional(), + severity: zod_1.z.string().min(1).optional(), + status: zod_1.z.string().min(1).optional(), + publishedAt: zod_1.z.string().datetime().optional().nullable(), + expiresAt: zod_1.z.string().datetime().optional().nullable(), + })) + .mutation(async ({ input, ctx }) => { + const { id, ...patch } = input; + return this.comms.updateAnnouncement(id, patch, ctx.userId); + }), + archive: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.ANNOUNCEMENTS_WRITE) + .input(zod_1.z.object({ id: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => this.comms.archiveAnnouncement(input.id, ctx.userId)), + }), + supervision: (0, trpc_init_1.router)({ + run: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SUPERVISION_RUN) + .input(zod_1.z.object({ kind: zod_1.z.string().optional() })) + .query(async ({ input, ctx }) => this.supervision.runSupervision(input.kind ?? "other", ctx.userId)), + listRuns: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SUPERVISION_READ) + .query(async () => this.supervision.listRuns()), + listFindings: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SUPERVISION_READ) + .input(zod_1.z.object({ runId: zod_1.z.string().uuid().optional(), status: zod_1.z.string().optional() })) + .query(async ({ input }) => this.supervision.listFindings(input.runId, input.status)), + acknowledge: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SUPERVISION_RUN) + .input(zod_1.z.object({ findingId: zod_1.z.string().uuid(), assignedToId: zod_1.z.string().uuid().optional() })) + .mutation(async ({ input, ctx }) => this.supervision.acknowledgeFinding(input.findingId, input.assignedToId ?? ctx.userId)), + resolve: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SUPERVISION_RUN) + .input(zod_1.z.object({ findingId: zod_1.z.string().uuid() })) + .mutation(async ({ input }) => this.supervision.resolveFinding(input.findingId)), + }), + operations: (0, trpc_init_1.router)({ + queueHealth: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_READ) + .query(async () => this.operations.getQueueHealth()), + queueJobs: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_QUEUES) + .input(zod_1.z.object({ + queue: zod_1.z.string(), + type: zod_1.z.enum(["waiting", "active", "failed", "completed", "delayed"]), + limit: zod_1.z.number().int().min(1).max(200).optional().default(50), + })) + .query(async ({ input }) => this.operations.getQueueJobs(input.queue, input.type, input.limit)), + retryJob: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_RETRY_JOBS) + .input(zod_1.z.object({ queue: zod_1.z.string(), jobId: zod_1.z.string() })) + .mutation(async ({ input, ctx }) => this.operations.retryJob(input.queue, input.jobId, ctx.userId)), + purgeFailed: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_PURGE_JOBS) + .input(zod_1.z.object({ queue: zod_1.z.string() })) + .mutation(async ({ input, ctx }) => this.operations.purgeFailedJobs(input.queue, ctx.userId)), + getLocks: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_LOCKS) + .input(zod_1.z.object({ pattern: zod_1.z.string().optional().default("lock:clip:*"), limit: zod_1.z.number().int().min(1).max(1000).optional().default(100) })) + .query(async ({ input }) => this.operations.getLocks(input.pattern, input.limit)), + releaseLock: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_LOCKS) + .input(zod_1.z.object({ key: zod_1.z.string() })) + .mutation(async ({ input, ctx }) => this.operations.releaseLock(input.key, ctx.userId)), + systemHealth: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_HEALTH) + .query(async () => this.operations.getSystemHealth()), + setMaintenanceMode: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_MAINTENANCE) + .input(zod_1.z.object({ enabled: zod_1.z.boolean() })) + .mutation(async ({ input, ctx }) => this.operations.setMaintenanceMode(input.enabled, ctx.userId)), + flushCache: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.OPS_CACHE) + .input(zod_1.z.object({ pattern: zod_1.z.string().optional().default("*") })) + .mutation(async ({ input, ctx }) => this.operations.flushCache(input.pattern, ctx.userId)), + }), + me: trpc_init_1.adminProcedure + .query(async ({ ctx }) => { + const context = await this.adminAuth.getAdminContext(ctx.userId); + if (!context) + return null; + return { + userId: context.userId, + isAdmin: context.isAdmin, + role: context.role, + permissions: [...context.permissions], + suspended: context.suspended, + }; + }), + }); + } +}; +exports.AdminRouter = AdminRouter; +exports.AdminRouter = AdminRouter = AdminRouter_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [admin_realtime_service_1.AdminRealtimeService, + admin_clip_scorer_service_1.AdminClipScorerService, + admin_auth_service_1.AdminAuthService, + admin_users_service_1.AdminUsersService, + admin_workspaces_service_1.AdminWorkspacesService, + admin_billing_service_1.AdminBillingService, + admin_moderation_service_1.AdminModerationService, + admin_operations_service_1.AdminOperationsService, + admin_settings_service_1.AdminSettingsService, + admin_audit_service_1.AdminAuditService, + admin_comms_service_1.AdminCommsService, + admin_supervision_service_1.AdminSupervisionService]) +], AdminRouter); +//# sourceMappingURL=admin.router.js.map \ No newline at end of file diff --git a/apps/api/dist/admin/admin.router.js.map b/apps/api/dist/admin/admin.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..0038a5db85ccde8acbbe687cf8912b6bb3b49402 --- /dev/null +++ b/apps/api/dist/admin/admin.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin.router.js","sourceRoot":"","sources":["../../src/admin/admin.router.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/ai/ai.module.js b/apps/api/dist/ai/ai.module.js new file mode 100644 index 0000000000000000000000000000000000000000..9e5b526c74130b07f289d149675252f0e1891a22 --- /dev/null +++ b/apps/api/dist/ai/ai.module.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AIModule = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const prisma_module_1 = require("../prisma/prisma.module"); +const billing_module_1 = require("../billing/billing.module"); +const ai_service_1 = require("./ai.service"); +let AIModule = class AIModule { +}; +exports.AIModule = AIModule; +exports.AIModule = AIModule = __decorate([ + (0, common_1.Module)({ + imports: [ + prisma_module_1.PrismaModule, + config_1.ConfigModule, + billing_module_1.BillingModule, + ], + providers: [ai_service_1.AIService], + exports: [ai_service_1.AIService], + }) +], AIModule); +//# sourceMappingURL=ai.module.js.map \ No newline at end of file diff --git a/apps/api/dist/ai/ai.module.js.map b/apps/api/dist/ai/ai.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..ca9412a7e42b1c0f9bf99096adb87a17e97ce27e --- /dev/null +++ b/apps/api/dist/ai/ai.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ai.module.js","sourceRoot":"","sources":["../../src/ai/ai.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA8C;AAE9C,2DAAuD;AACvD,8DAA0D;AAC1D,6CAAyC;AAWlC,IAAM,QAAQ,GAAd,MAAM,QAAQ;CAAG,CAAA;AAAX,4BAAQ;mBAAR,QAAQ;IATpB,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE;YACL,4BAAY;YACZ,qBAAY;YACZ,8BAAa;SAChB;QACD,SAAS,EAAE,CAAC,sBAAS,CAAC;QACtB,OAAO,EAAE,CAAC,sBAAS,CAAC;KACvB,CAAC;GACW,QAAQ,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/ai/ai.service.js b/apps/api/dist/ai/ai.service.js new file mode 100644 index 0000000000000000000000000000000000000000..80b22f4007207022a6d1a88639088971a7a21b1c --- /dev/null +++ b/apps/api/dist/ai/ai.service.js @@ -0,0 +1,202 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AIService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AIService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const prisma_service_1 = require("../prisma/prisma.service"); +const subscription_credentials_service_1 = require("../billing/subscription-credentials.service"); +let AIService = AIService_1 = class AIService { + configService; + prisma; + credentialsService; + logger = new common_1.Logger(AIService_1.name); + constructor(configService, prisma, credentialsService) { + this.configService = configService; + this.prisma = prisma; + this.credentialsService = credentialsService; + } + async resolveApiKeyAndUrl(workspaceId, provider) { + let resolvedKey = null; + let resolvedUrl = null; + try { + const resolved = await this.credentialsService.resolveCredentials(workspaceId, provider); + resolvedKey = resolved.apiKey || resolved.accessToken; + resolvedUrl = resolved.endpointUrl; + } + catch (error) { + if (error instanceof common_1.ForbiddenException) { + throw error; + } + this.logger.debug(`Workspace/Platform DB credentials not resolved for ${provider}: ${error instanceof Error ? error.message : String(error)}. Attempting env fallback.`); + } + if (!resolvedKey) { + if (provider === "nvidia_nim") { + resolvedKey = this.configService.get("NVIDIA_NIM_API_KEY") || null; + resolvedUrl = resolvedUrl || "https://integrate.api.nvidia.com/v1"; + } + else if (provider === "deepseek") { + resolvedKey = this.configService.get("DEEPSEEK_API_KEY") || null; + resolvedUrl = resolvedUrl || "https://api.deepseek.com/v1"; + } + else if (provider === "moonshot") { + resolvedKey = this.configService.get("MOONSHOT_API_KEY") || null; + resolvedUrl = resolvedUrl || "https://api.moonshot.cn/v1"; + } + } + if (!resolvedKey) { + throw new common_1.BadRequestException(`API key for provider "${provider}" is not configured in the workspace or system environment.`); + } + return { + apiKey: resolvedKey, + endpointUrl: resolvedUrl || "", + }; + } + async generateText(workspaceId, prompt, options = {}) { + const provider = options.provider || "nvidia_nim"; + const { apiKey, endpointUrl } = await this.resolveApiKeyAndUrl(workspaceId, provider); + let defaultModel = "meta/llama-3.1-405b-instruct"; + let defaultUrl = "https://integrate.api.nvidia.com/v1/chat/completions"; + if (provider === "deepseek") { + defaultModel = "deepseek-chat"; + defaultUrl = "https://api.deepseek.com/v1/chat/completions"; + } + else if (provider === "moonshot") { + defaultModel = "moonshot-v1-8k"; + defaultUrl = "https://api.moonshot.cn/v1/chat/completions"; + } + const model = options.model || defaultModel; + const targetUrl = endpointUrl + ? `${endpointUrl.replace(/\/$/, "")}/chat/completions` + : defaultUrl; + const messages = []; + if (options.systemPrompt) { + messages.push({ role: "system", content: options.systemPrompt }); + } + messages.push({ role: "user", content: prompt }); + this.logger.log(`Calling text completion on provider ${provider} using model ${model}`); + try { + const response = await fetch(targetUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${apiKey}`, + }, + body: JSON.stringify({ + model, + messages, + temperature: options.temperature ?? 0.7, + max_tokens: options.maxTokens ?? 1024, + }), + }); + if (!response.ok) { + const errorBody = await response.text(); + throw new Error(`HTTP Error ${response.status}: ${errorBody}`); + } + const data = await response.json(); + const content = data.choices?.[0]?.message?.content; + if (content === undefined) { + throw new Error("Invalid response format from AI provider"); + } + return content; + } + catch (error) { + this.logger.error(`AI Completion failed for provider ${provider}: ${error instanceof Error ? error.message : String(error)}`); + throw new common_1.BadRequestException(`AI Text generation failed: ${error instanceof Error ? error.message : String(error)}`); + } + } + async transcribeAudio(workspaceId, audioFileBuffer, fileName, options = {}) { + const provider = "nvidia_nim"; + const { apiKey, endpointUrl } = await this.resolveApiKeyAndUrl(workspaceId, provider); + const model = options.model || "nvidia/canary-1b"; + const defaultUrl = "https://integrate.api.nvidia.com/v1/audio/transcriptions"; + const targetUrl = endpointUrl + ? `${endpointUrl.replace(/\/$/, "")}/audio/transcriptions` + : defaultUrl; + this.logger.log(`Calling NVIDIA NIM transcription with model ${model} for file ${fileName}`); + try { + const formData = new FormData(); + const blob = new Blob([new Uint8Array(audioFileBuffer)], { type: "audio/wav" }); + formData.append("file", blob, fileName); + formData.append("model", model); + if (options.language) { + formData.append("language", options.language); + } + const response = await fetch(targetUrl, { + method: "POST", + headers: { + "Authorization": `Bearer ${apiKey}`, + }, + body: formData, + }); + if (!response.ok) { + const errorBody = await response.text(); + throw new Error(`HTTP Error ${response.status}: ${errorBody}`); + } + const data = await response.json(); + return { + text: data.text, + language: data.language || options.language || "en", + duration: data.duration, + segments: data.segments, + }; + } + catch (error) { + this.logger.error(`NVIDIA NIM Audio Transcription failed: ${error instanceof Error ? error.message : String(error)}`); + throw new common_1.BadRequestException(`NVIDIA NIM transcription failed: ${error instanceof Error ? error.message : String(error)}`); + } + } + async synthesizeSpeech(workspaceId, text, options = {}) { + const provider = "nvidia_nim"; + const { apiKey, endpointUrl } = await this.resolveApiKeyAndUrl(workspaceId, provider); + const model = options.model || "nvidia/riva-tts"; + const defaultUrl = "https://integrate.api.nvidia.com/v1/audio/speech"; + const targetUrl = endpointUrl + ? `${endpointUrl.replace(/\/$/, "")}/audio/speech` + : defaultUrl; + this.logger.log(`Calling NVIDIA NIM voice synthesis with model ${model}`); + try { + const response = await fetch(targetUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${apiKey}`, + "Accept": "audio/wav", + }, + body: JSON.stringify({ + model, + input: text, + voice: options.voice || "en-US-Female-1", + response_format: "wav", + }), + }); + if (!response.ok) { + const errorBody = await response.text(); + throw new Error(`HTTP Error ${response.status}: ${errorBody}`); + } + const arrayBuffer = await response.arrayBuffer(); + return Buffer.from(arrayBuffer); + } + catch (error) { + this.logger.error(`NVIDIA NIM Text-to-Speech failed: ${error instanceof Error ? error.message : String(error)}`); + throw new common_1.BadRequestException(`NVIDIA NIM Text-to-Speech failed: ${error instanceof Error ? error.message : String(error)}`); + } + } +}; +exports.AIService = AIService; +exports.AIService = AIService = AIService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [config_1.ConfigService, + prisma_service_1.PrismaService, + subscription_credentials_service_1.SubscriptionCredentialsService]) +], AIService); +//# sourceMappingURL=ai.service.js.map \ No newline at end of file diff --git a/apps/api/dist/ai/ai.service.js.map b/apps/api/dist/ai/ai.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..4dc842f618b0b04c4a5256e1be5f61bfd3a977c2 --- /dev/null +++ b/apps/api/dist/ai/ai.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ai.service.js","sourceRoot":"","sources":["../../src/ai/ai.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/analytics/analytics.controller.js b/apps/api/dist/analytics/analytics.controller.js new file mode 100644 index 0000000000000000000000000000000000000000..5058a7005a5e6eabedab0888e921d7d3469fe760 --- /dev/null +++ b/apps/api/dist/analytics/analytics.controller.js @@ -0,0 +1,123 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AnalyticsController = void 0; +const common_1 = require("@nestjs/common"); +const swagger_1 = require("@nestjs/swagger"); +const analytics_service_1 = require("./analytics.service"); +const better_auth_guard_1 = require("../auth/better-auth.guard"); +const subscription_guard_1 = require("../billing/subscription.guard"); +const subscription_features_1 = require("../billing/subscription-features"); +let AnalyticsController = class AnalyticsController { + analyticsService; + constructor(analyticsService) { + this.analyticsService = analyticsService; + } + async getDashboardMetrics(podcastId, req) { + return this.analyticsService.getDashboardMetrics(podcastId, req.userId); + } + async getPipeline(podcastId, req) { + return this.analyticsService.getGuestPipeline(podcastId, req.userId); + } + async getOutreachMetrics(podcastId, req) { + return this.analyticsService.getOutreachMetrics(podcastId, req.userId); + } + async getSchedulingMetrics(podcastId, daysBack, req) { + return this.analyticsService.getSchedulingMetrics(podcastId, req.userId, daysBack ? parseInt(daysBack, 10) : 90); + } + async getActivity(podcastId, limit, req) { + return this.analyticsService.getRecentActivity(podcastId, req.userId, limit ? parseInt(limit, 10) : 20); + } +}; +exports.AnalyticsController = AnalyticsController; +__decorate([ + (0, common_1.Get)("dashboard"), + (0, swagger_1.ApiOperation)({ + summary: "Dashboard metrics", + description: "Get key metrics for the podcast dashboard", + }), + (0, swagger_1.ApiParam)({ name: "podcastId", description: "Podcast ID" }), + (0, swagger_1.ApiResponse)({ status: 200, description: "Dashboard metrics retrieved" }), + __param(0, (0, common_1.Param)("podcastId")), + __param(1, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], AnalyticsController.prototype, "getDashboardMetrics", null); +__decorate([ + (0, common_1.Get)("pipeline"), + (0, swagger_1.ApiOperation)({ + summary: "Guest pipeline", + description: "Get guest counts by status with priority scores", + }), + (0, swagger_1.ApiParam)({ name: "podcastId", description: "Podcast ID" }), + __param(0, (0, common_1.Param)("podcastId")), + __param(1, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], AnalyticsController.prototype, "getPipeline", null); +__decorate([ + (0, common_1.Get)("outreach"), + (0, swagger_1.ApiOperation)({ + summary: "Outreach metrics", + description: "Get email sequence performance metrics (Studio plan)", + }), + (0, swagger_1.ApiParam)({ name: "podcastId", description: "Podcast ID" }), + (0, subscription_guard_1.RequireFeature)(subscription_features_1.SubscriptionFeature.ADVANCED_ANALYTICS), + __param(0, (0, common_1.Param)("podcastId")), + __param(1, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], AnalyticsController.prototype, "getOutreachMetrics", null); +__decorate([ + (0, common_1.Get)("scheduling"), + (0, swagger_1.ApiOperation)({ + summary: "Scheduling metrics", + description: "Get episode scheduling and conversion metrics (Studio plan)", + }), + (0, swagger_1.ApiParam)({ name: "podcastId", description: "Podcast ID" }), + (0, swagger_1.ApiQuery)({ name: "daysBack", type: Number, required: false, description: "Days to look back" }), + (0, subscription_guard_1.RequireFeature)(subscription_features_1.SubscriptionFeature.ADVANCED_ANALYTICS), + __param(0, (0, common_1.Param)("podcastId")), + __param(1, (0, common_1.Query)("daysBack")), + __param(2, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, Object]), + __metadata("design:returntype", Promise) +], AnalyticsController.prototype, "getSchedulingMetrics", null); +__decorate([ + (0, common_1.Get)("activity"), + (0, swagger_1.ApiOperation)({ + summary: "Activity timeline", + description: "Get recent activity for the podcast", + }), + (0, swagger_1.ApiParam)({ name: "podcastId", description: "Podcast ID" }), + (0, swagger_1.ApiQuery)({ name: "limit", type: Number, required: false }), + __param(0, (0, common_1.Param)("podcastId")), + __param(1, (0, common_1.Query)("limit")), + __param(2, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String, Object]), + __metadata("design:returntype", Promise) +], AnalyticsController.prototype, "getActivity", null); +exports.AnalyticsController = AnalyticsController = __decorate([ + (0, swagger_1.ApiTags)("Analytics"), + (0, swagger_1.ApiBearerAuth)("jwt"), + (0, common_1.UseGuards)(better_auth_guard_1.BetterAuthGuard, subscription_guard_1.SubscriptionGuard), + (0, common_1.Controller)("podcasts/:podcastId/analytics"), + __metadata("design:paramtypes", [analytics_service_1.AnalyticsService]) +], AnalyticsController); +//# sourceMappingURL=analytics.controller.js.map \ No newline at end of file diff --git a/apps/api/dist/analytics/analytics.controller.js.map b/apps/api/dist/analytics/analytics.controller.js.map new file mode 100644 index 0000000000000000000000000000000000000000..55315c68ad51e7e44ce991f365e26461c6e67365 --- /dev/null +++ b/apps/api/dist/analytics/analytics.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"analytics.controller.js","sourceRoot":"","sources":["../../src/analytics/analytics.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/analytics/analytics.module.js b/apps/api/dist/analytics/analytics.module.js new file mode 100644 index 0000000000000000000000000000000000000000..052bd7452a0e223134ccc74769fccafee137f38c --- /dev/null +++ b/apps/api/dist/analytics/analytics.module.js @@ -0,0 +1,24 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AnalyticsModule = void 0; +const common_1 = require("@nestjs/common"); +const analytics_service_1 = require("./analytics.service"); +const analytics_controller_1 = require("./analytics.controller"); +const analytics_router_1 = require("./analytics.router"); +let AnalyticsModule = class AnalyticsModule { +}; +exports.AnalyticsModule = AnalyticsModule; +exports.AnalyticsModule = AnalyticsModule = __decorate([ + (0, common_1.Module)({ + providers: [analytics_service_1.AnalyticsService, analytics_router_1.AnalyticsRouter], + controllers: [analytics_controller_1.AnalyticsController], + exports: [analytics_service_1.AnalyticsService, analytics_router_1.AnalyticsRouter], + }) +], AnalyticsModule); +//# sourceMappingURL=analytics.module.js.map \ No newline at end of file diff --git a/apps/api/dist/analytics/analytics.module.js.map b/apps/api/dist/analytics/analytics.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..7add81108680f9f23fc21b5e78de85a08b15f269 --- /dev/null +++ b/apps/api/dist/analytics/analytics.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"analytics.module.js","sourceRoot":"","sources":["../../src/analytics/analytics.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,iEAA6D;AAC7D,yDAAqD;AAO9C,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAL3B,IAAA,eAAM,EAAC;QACJ,SAAS,EAAE,CAAC,oCAAgB,EAAE,kCAAe,CAAC;QAC9C,WAAW,EAAE,CAAC,0CAAmB,CAAC;QAClC,OAAO,EAAE,CAAC,oCAAgB,EAAE,kCAAe,CAAC;KAC/C,CAAC;GACW,eAAe,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/analytics/analytics.router.js b/apps/api/dist/analytics/analytics.router.js new file mode 100644 index 0000000000000000000000000000000000000000..f5a574e107e381d836eddb59382e6e10ba3ea1cd --- /dev/null +++ b/apps/api/dist/analytics/analytics.router.js @@ -0,0 +1,64 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AnalyticsRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const trpc_init_1 = require("../trpc/trpc.init"); +const subscription_features_1 = require("../billing/subscription-features"); +const analytics_service_1 = require("./analytics.service"); +let AnalyticsRouter = class AnalyticsRouter { + analyticsService; + constructor(analyticsService) { + this.analyticsService = analyticsService; + } + get router() { + return (0, trpc_init_1.router)({ + dashboard: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ podcastId: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.analyticsService.getDashboardMetrics(input.podcastId, ctx.userId); + }), + pipeline: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ podcastId: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.analyticsService.getGuestPipeline(input.podcastId, ctx.userId); + }), + outreach: (0, trpc_init_1.requireFeature)(subscription_features_1.SubscriptionFeature.ADVANCED_ANALYTICS) + .input(zod_1.z.object({ podcastId: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.analyticsService.getOutreachMetrics(input.podcastId, ctx.userId); + }), + scheduling: (0, trpc_init_1.requireFeature)(subscription_features_1.SubscriptionFeature.ADVANCED_ANALYTICS) + .input(zod_1.z.object({ + podcastId: zod_1.z.string().uuid(), + daysBack: zod_1.z.number().int().min(7).max(365).default(90), + })) + .query(async ({ input, ctx }) => { + return this.analyticsService.getSchedulingMetrics(input.podcastId, ctx.userId, input.daysBack); + }), + recentActivity: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + podcastId: zod_1.z.string().uuid(), + limit: zod_1.z.number().int().min(1).max(100).default(20), + })) + .query(async ({ input, ctx }) => { + return this.analyticsService.getRecentActivity(input.podcastId, ctx.userId, input.limit); + }), + }); + } +}; +exports.AnalyticsRouter = AnalyticsRouter; +exports.AnalyticsRouter = AnalyticsRouter = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [analytics_service_1.AnalyticsService]) +], AnalyticsRouter); +//# sourceMappingURL=analytics.router.js.map \ No newline at end of file diff --git a/apps/api/dist/analytics/analytics.router.js.map b/apps/api/dist/analytics/analytics.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..e8421b7fdc681d32ad5ab1539ae01e4ea0c3d7c2 --- /dev/null +++ b/apps/api/dist/analytics/analytics.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"analytics.router.js","sourceRoot":"","sources":["../../src/analytics/analytics.router.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/analytics/analytics.service.js b/apps/api/dist/analytics/analytics.service.js new file mode 100644 index 0000000000000000000000000000000000000000..ee46259845b7f504fede3cec3dfe4a1fc352f849 --- /dev/null +++ b/apps/api/dist/analytics/analytics.service.js @@ -0,0 +1,354 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AnalyticsService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AnalyticsService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const authorization_service_1 = require("../common/authorization.service"); +let AnalyticsService = AnalyticsService_1 = class AnalyticsService { + prisma; + authorizationService; + logger = new common_1.Logger(AnalyticsService_1.name); + cache = new Map(); + cacheTTL = 30_000; + constructor(prisma, authorizationService) { + this.prisma = prisma; + this.authorizationService = authorizationService; + } + getCached(key) { + const entry = this.cache.get(key); + if (entry && entry.expiresAt > Date.now()) + return entry.data; + this.cache.delete(key); + return null; + } + setCache(key, data) { + this.cache.set(key, { data, expiresAt: Date.now() + this.cacheTTL }); + if (this.cache.size > 100) { + const oldest = this.cache.keys().next().value; + if (oldest) + this.cache.delete(oldest); + } + } + async getDashboardMetrics(podcastId, userId) { + await this.authorizationService.checkPodcastAccess(podcastId, userId); + const cached = this.getCached(`dashboard:${podcastId}`); + if (cached) + return cached; + const [totalGuests, guestsByStatus, totalEpisodes, episodesByStatus, upcomingBookings, recentGuests, topPriorityGuests,] = await Promise.all([ + this.prisma.guest.count({ where: { podcastId } }), + this.prisma.guest.groupBy({ + by: ["status"], + where: { podcastId }, + _count: { id: true }, + }), + this.prisma.episode.count({ where: { podcastId } }), + this.prisma.episode.groupBy({ + by: ["status"], + where: { podcastId }, + _count: { id: true }, + }), + this.prisma.booking.count({ + where: { + episode: { podcastId }, + startTime: { gte: new Date() }, + }, + }), + this.prisma.guest.findMany({ + where: { podcastId }, + orderBy: { createdAt: "desc" }, + take: 5, + select: { + id: true, + fullName: true, + email: true, + company: true, + status: true, + priorityScore: true, + createdAt: true, + }, + }), + this.prisma.guest.findMany({ + where: { podcastId, status: { in: ["prospect", "contacted"] } }, + orderBy: { priorityScore: "desc" }, + take: 5, + select: { + id: true, + fullName: true, + company: true, + title: true, + priorityScore: true, + }, + }), + ]); + const guestStatusMap = {}; + for (const item of guestsByStatus) { + guestStatusMap[item.status] = item._count.id; + } + const episodeStatusMap = {}; + for (const item of episodesByStatus) { + episodeStatusMap[item.status] = item._count.id; + } + const result = { + totalGuests, + guestsByStatus: guestStatusMap, + totalEpisodes, + episodesByStatus: episodeStatusMap, + upcomingBookings, + recentGuests, + topPriorityGuests, + }; + this.setCache(`dashboard:${podcastId}`, result); + return result; + } + async getGuestPipeline(podcastId, userId) { + await this.authorizationService.checkPodcastAccess(podcastId, userId); + const cached = this.getCached(`pipeline:${podcastId}`); + if (cached) + return cached; + const pipeline = await this.prisma.guest.groupBy({ + by: ["status"], + where: { podcastId }, + _count: { id: true }, + _avg: { priorityScore: true }, + }); + const result = pipeline.map((stage) => ({ + status: stage.status, + count: stage._count.id, + avgScore: Math.round(stage._avg.priorityScore || 0), + })); + this.setCache(`pipeline:${podcastId}`, result); + return result; + } + async getOutreachMetrics(podcastId, userId) { + await this.authorizationService.checkPodcastAccess(podcastId, userId); + const cached = this.getCached(`outreach:${podcastId}`); + if (cached) + return cached; + const guests = await this.prisma.guest.findMany({ + where: { podcastId }, + select: { id: true }, + }); + const guestIds = guests.map((g) => g.id); + if (guestIds.length === 0) { + return { + totalSequences: 0, + activeSequences: 0, + totalMessagesSent: 0, + openRate: 0, + clickRate: 0, + replyRate: 0, + }; + } + const [totalSequences, activeSequences, messageStats] = await Promise.all([ + this.prisma.outreachSequence.count({ + where: { guestId: { in: guestIds } }, + }), + this.prisma.outreachSequence.count({ + where: { guestId: { in: guestIds }, status: "active" }, + }), + this.prisma.outreachMessage.aggregate({ + where: { sequence: { guestId: { in: guestIds } } }, + _count: { id: true, sentAt: true, openedAt: true, clickedAt: true }, + }), + ]); + const sent = messageStats._count.sentAt; + const opened = messageStats._count.openedAt; + const clicked = messageStats._count.clickedAt; + const result = { + totalSequences, + activeSequences, + totalMessagesSent: sent, + openRate: sent > 0 ? Math.round((opened / sent) * 100) : 0, + clickRate: sent > 0 ? Math.round((clicked / sent) * 100) : 0, + }; + this.setCache(`outreach:${podcastId}`, result); + return result; + } + async getSchedulingMetrics(podcastId, userId, daysBack = 90) { + await this.authorizationService.checkPodcastAccess(podcastId, userId); + const cacheKey = `scheduling:${podcastId}:${daysBack}`; + const cached = this.getCached(cacheKey); + if (cached) + return cached; + const since = new Date(Date.now() - daysBack * 24 * 60 * 60 * 1000); + const [scheduledCount, recordedCount, publishedCount, avgDuration] = await Promise.all([ + this.prisma.episode.count({ + where: { + podcastId, + status: "scheduled", + createdAt: { gte: since }, + }, + }), + this.prisma.episode.count({ + where: { + podcastId, + status: "recorded", + createdAt: { gte: since }, + }, + }), + this.prisma.episode.count({ + where: { + podcastId, + status: "published", + createdAt: { gte: since }, + }, + }), + this.prisma.episode.aggregate({ + where: { podcastId, durationMinutes: { not: null } }, + _avg: { durationMinutes: true }, + }), + ]); + const result = { + scheduledCount, + recordedCount, + publishedCount, + conversionRate: scheduledCount > 0 + ? Math.round((recordedCount / scheduledCount) * 100) + : 0, + avgDurationMinutes: Math.round(avgDuration._avg.durationMinutes || 0), + }; + this.setCache(cacheKey, result); + return result; + } + async getRecentActivity(podcastId, userId, limit = 20) { + await this.authorizationService.checkPodcastAccess(podcastId, userId); + const cacheKey = `recent:${podcastId}:${limit}`; + const cached = this.getCached(cacheKey); + if (cached) + return cached; + const [recentGuests, recentEpisodes, recentBookings, recentOutreach] = await Promise.all([ + this.prisma.guest.findMany({ + where: { podcastId }, + orderBy: { createdAt: "desc" }, + take: limit, + select: { + id: true, + fullName: true, + status: true, + createdAt: true, + _count: { + select: { + outreachSequences: true, + }, + }, + }, + }), + this.prisma.episode.findMany({ + where: { podcastId }, + orderBy: { createdAt: "desc" }, + take: limit, + select: { + id: true, + title: true, + status: true, + scheduledAt: true, + createdAt: true, + guest: { + select: { + fullName: true, + }, + }, + }, + }), + this.prisma.booking.findMany({ + where: { episode: { podcastId } }, + orderBy: { createdAt: "desc" }, + take: limit, + select: { + id: true, + startTime: true, + isConfirmed: true, + createdAt: true, + episode: { + select: { + title: true, + guest: { + select: { + fullName: true, + }, + }, + }, + }, + }, + }), + this.prisma.outreachMessage.findMany({ + where: { sequence: { guest: { podcastId } } }, + orderBy: { sentAt: "desc" }, + take: limit, + select: { + id: true, + type: true, + sentAt: true, + openedAt: true, + sequence: { + select: { + guest: { + select: { + fullName: true, + }, + }, + }, + }, + }, + }), + ]); + const activities = [ + ...recentGuests.map((g) => ({ + type: "guest_added", + id: g.id, + title: `New guest: ${g.fullName}`, + status: g.status, + timestamp: g.createdAt, + })), + ...recentEpisodes.map((e) => ({ + type: "episode_created", + id: e.id, + title: `Episode: ${e.title}`, + subtitle: `With ${e.guest.fullName}`, + status: e.status, + timestamp: e.createdAt, + scheduledAt: e.scheduledAt, + })), + ...recentBookings.map((b) => ({ + type: "booking_created", + id: b.id, + title: `Booking: ${b.episode.title}`, + subtitle: `With ${b.episode.guest.fullName}`, + timestamp: b.createdAt, + startTime: b.startTime, + isConfirmed: b.isConfirmed, + })), + ...recentOutreach.map((o) => ({ + type: "message_sent", + id: o.id, + title: `Email sent to ${o.sequence.guest.fullName}`, + subtitle: o.type, + timestamp: o.sentAt, + openedAt: o.openedAt, + })), + ]; + const result = activities + .filter((a) => a.timestamp != null) + .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()) + .slice(0, limit); + this.setCache(cacheKey, result); + return result; + } +}; +exports.AnalyticsService = AnalyticsService; +exports.AnalyticsService = AnalyticsService = AnalyticsService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + authorization_service_1.AuthorizationService]) +], AnalyticsService); +//# sourceMappingURL=analytics.service.js.map \ No newline at end of file diff --git a/apps/api/dist/analytics/analytics.service.js.map b/apps/api/dist/analytics/analytics.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..85923adc78b5f624b084f64146a94cf3e1e164fc --- /dev/null +++ b/apps/api/dist/analytics/analytics.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"analytics.service.js","sourceRoot":"","sources":["../../src/analytics/analytics.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AAEpD,6DAAyD;AACzD,2EAAuE;AAQhE,IAAM,gBAAgB,wBAAtB,MAAM,gBAAgB;IAMJ;IACA;IANJ,MAAM,GAAG,IAAI,eAAM,CAAC,kBAAgB,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IAC/C,QAAQ,GAAG,MAAM,CAAC;IAEnC,YACqB,MAAqB,EACrB,oBAA0C;QAD1C,WAAM,GAAN,MAAM,CAAe;QACrB,yBAAoB,GAApB,oBAAoB,CAAsB;IAC5D,CAAC;IAEI,SAAS,CAAI,GAAW;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAA8B,CAAC;QAC/D,IAAI,KAAK,IAAI,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC;QAC7D,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IAChB,CAAC;IAEO,QAAQ,CAAI,GAAW,EAAE,IAAO;QACpC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACrE,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAC9C,IAAI,MAAM;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC;IACL,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,SAAiB,EAAE,MAAc;QACvD,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAA0B,aAAa,SAAS,EAAE,CAAC,CAAC;QACjF,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAE1B,MAAM,CACF,WAAW,EACX,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACpB,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAClB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC;YAEjD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;gBACtB,EAAE,EAAE,CAAC,QAAQ,CAAC;gBACd,KAAK,EAAE,EAAE,SAAS,EAAE;gBACpB,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;aACvB,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC;YAEnD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;gBACxB,EAAE,EAAE,CAAC,QAAQ,CAAC;gBACd,KAAK,EAAE,EAAE,SAAS,EAAE;gBACpB,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;aACvB,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBACtB,KAAK,EAAE;oBACH,OAAO,EAAE,EAAE,SAAS,EAAE;oBACtB,SAAS,EAAE,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE,EAAE;iBACjC;aACJ,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;gBACvB,KAAK,EAAE,EAAE,SAAS,EAAE;gBACpB,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;gBAC9B,IAAI,EAAE,CAAC;gBACP,MAAM,EAAE;oBACJ,EAAE,EAAE,IAAI;oBACR,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,IAAI;oBACX,OAAO,EAAE,IAAI;oBACb,MAAM,EAAE,IAAI;oBACZ,aAAa,EAAE,IAAI;oBACnB,SAAS,EAAE,IAAI;iBAClB;aACJ,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;gBACvB,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,EAAE;gBAC/D,OAAO,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE;gBAClC,IAAI,EAAE,CAAC;gBACP,MAAM,EAAE;oBACJ,EAAE,EAAE,IAAI;oBACR,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE,IAAI;oBACb,KAAK,EAAE,IAAI;oBACX,aAAa,EAAE,IAAI;iBACtB;aACJ,CAAC;SACL,CAAC,CAAC;QAEH,MAAM,cAAc,GAA2B,EAAE,CAAC;QAClD,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;YAChC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,CAAC;QAED,MAAM,gBAAgB,GAA2B,EAAE,CAAC;QACpD,KAAK,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;YAClC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,CAAC;QAED,MAAM,MAAM,GAAG;YACX,WAAW;YACX,cAAc,EAAE,cAAc;YAC9B,aAAa;YACb,gBAAgB,EAAE,gBAAgB;YAClC,gBAAgB;YAChB,YAAY;YACZ,iBAAiB;SACpB,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,aAAa,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;QAChD,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,SAAiB,EAAE,MAAc;QACpD,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAmD,YAAY,SAAS,EAAE,CAAC,CAAC;QACzG,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;YAC7C,EAAE,EAAE,CAAC,QAAQ,CAAC;YACd,KAAK,EAAE,EAAE,SAAS,EAAE;YACpB,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;YACpB,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE;SAChC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACpC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE;YACtB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC;SACtD,CAAC,CAAC,CAAC;QAEJ,IAAI,CAAC,QAAQ,CAAC,YAAY,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,SAAiB,EAAE,MAAc;QACtD,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAA0B,YAAY,SAAS,EAAE,CAAC,CAAC;QAChF,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC5C,KAAK,EAAE,EAAE,SAAS,EAAE;YACpB,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;SACvB,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAEzC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO;gBACH,cAAc,EAAE,CAAC;gBACjB,eAAe,EAAE,CAAC;gBAClB,iBAAiB,EAAE,CAAC;gBACpB,QAAQ,EAAE,CAAC;gBACX,SAAS,EAAE,CAAC;gBACZ,SAAS,EAAE,CAAC;aACf,CAAC;QACN,CAAC;QAED,MAAM,CAAC,cAAc,EAAE,eAAe,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACtE,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC;gBAC/B,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE;aACvC,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC;gBAC/B,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;aACzD,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC;gBAClC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;gBAClD,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;aACtE,CAAC;SACL,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC;QACxC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC5C,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC;QAE9C,MAAM,MAAM,GAAG;YACX,cAAc;YACd,eAAe;YACf,iBAAiB,EAAE,IAAI;YACvB,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1D,SAAS,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SAC/D,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,YAAY,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,SAAiB,EAAE,MAAc,EAAE,WAAmB,EAAE;QAC/E,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,QAAQ,GAAG,cAAc,SAAS,IAAI,QAAQ,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAA0B,QAAQ,CAAC,CAAC;QACjE,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAE1B,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAEpE,MAAM,CAAC,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,CAAC,GAC9D,MAAM,OAAO,CAAC,GAAG,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBACtB,KAAK,EAAE;oBACH,SAAS;oBACT,MAAM,EAAE,WAAW;oBACnB,SAAS,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE;iBAC5B;aACJ,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBACtB,KAAK,EAAE;oBACH,SAAS;oBACT,MAAM,EAAE,UAAU;oBAClB,SAAS,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE;iBAC5B;aACJ,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBACtB,KAAK,EAAE;oBACH,SAAS;oBACT,MAAM,EAAE,WAAW;oBACnB,SAAS,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE;iBAC5B;aACJ,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;gBAC1B,KAAK,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBACpD,IAAI,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;aAClC,CAAC;SACL,CAAC,CAAC;QAEP,MAAM,MAAM,GAAG;YACX,cAAc;YACd,aAAa;YACb,cAAc;YACd,cAAc,EACV,cAAc,GAAG,CAAC;gBACd,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,aAAa,GAAG,cAAc,CAAC,GAAG,GAAG,CAAC;gBACpD,CAAC,CAAC,CAAC;YACX,kBAAkB,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC;SACxE,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,SAAiB,EAAE,MAAc,EAAE,QAAgB,EAAE;QACzE,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,QAAQ,GAAG,UAAU,SAAS,IAAI,KAAK,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAY,QAAQ,CAAC,CAAC;QACnD,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,MAAM,CAAC,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,CAAC,GAChE,MAAM,OAAO,CAAC,GAAG,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;gBACvB,KAAK,EAAE,EAAE,SAAS,EAAE;gBACpB,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;gBAC9B,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE;oBACJ,EAAE,EAAE,IAAI;oBACR,QAAQ,EAAE,IAAI;oBACd,MAAM,EAAE,IAAI;oBACZ,SAAS,EAAE,IAAI;oBACf,MAAM,EAAE;wBACJ,MAAM,EAAE;4BACJ,iBAAiB,EAAE,IAAI;yBAC1B;qBACJ;iBACJ;aACJ,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACzB,KAAK,EAAE,EAAE,SAAS,EAAE;gBACpB,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;gBAC9B,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE;oBACJ,EAAE,EAAE,IAAI;oBACR,KAAK,EAAE,IAAI;oBACX,MAAM,EAAE,IAAI;oBACZ,WAAW,EAAE,IAAI;oBACjB,SAAS,EAAE,IAAI;oBACf,KAAK,EAAE;wBACH,MAAM,EAAE;4BACJ,QAAQ,EAAE,IAAI;yBACjB;qBACJ;iBACJ;aACJ,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACzB,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE;gBACjC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;gBAC9B,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE;oBACJ,EAAE,EAAE,IAAI;oBACR,SAAS,EAAE,IAAI;oBACf,WAAW,EAAE,IAAI;oBACjB,SAAS,EAAE,IAAI;oBACf,OAAO,EAAE;wBACL,MAAM,EAAE;4BACJ,KAAK,EAAE,IAAI;4BACX,KAAK,EAAE;gCACH,MAAM,EAAE;oCACJ,QAAQ,EAAE,IAAI;iCACjB;6BACJ;yBACJ;qBACJ;iBACJ;aACJ,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC;gBACjC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;gBAC7C,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;gBAC3B,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE;oBACJ,EAAE,EAAE,IAAI;oBACR,IAAI,EAAE,IAAI;oBACV,MAAM,EAAE,IAAI;oBACZ,QAAQ,EAAE,IAAI;oBACd,QAAQ,EAAE;wBACN,MAAM,EAAE;4BACJ,KAAK,EAAE;gCACH,MAAM,EAAE;oCACJ,QAAQ,EAAE,IAAI;iCACjB;6BACJ;yBACJ;qBACJ;iBACJ;aACJ,CAAC;SACL,CAAC,CAAC;QAGP,MAAM,UAAU,GAAG;YACf,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACxB,IAAI,EAAE,aAAsB;gBAC5B,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,KAAK,EAAE,cAAc,CAAC,CAAC,QAAQ,EAAE;gBACjC,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,SAAS,EAAE,CAAC,CAAC,SAAS;aACzB,CAAC,CAAC;YACH,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC1B,IAAI,EAAE,iBAA0B;gBAChC,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,KAAK,EAAE,YAAY,CAAC,CAAC,KAAK,EAAE;gBAC5B,QAAQ,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE;gBACpC,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,WAAW,EAAE,CAAC,CAAC,WAAW;aAC7B,CAAC,CAAC;YACH,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC1B,IAAI,EAAE,iBAA0B;gBAChC,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,KAAK,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE;gBACpC,QAAQ,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE;gBAC5C,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,WAAW,EAAE,CAAC,CAAC,WAAW;aAC7B,CAAC,CAAC;YACH,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC1B,IAAI,EAAE,cAAuB;gBAC7B,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,KAAK,EAAE,iBAAiB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE;gBACnD,QAAQ,EAAE,CAAC,CAAC,IAAI;gBAChB,SAAS,EAAE,CAAC,CAAC,MAAM;gBACnB,QAAQ,EAAE,CAAC,CAAC,QAAQ;aACvB,CAAC,CAAC;SACN,CAAC;QAEF,MAAM,MAAM,GAAG,UAAU;aACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC;aAClC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,SAAU,CAAC,OAAO,EAAE,CAAC;aAC/D,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ,CAAA;AA1WY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;qCAOoB,8BAAa;QACC,4CAAoB;GAPtD,gBAAgB,CA0W5B"} \ No newline at end of file diff --git a/apps/api/dist/app.module.js b/apps/api/dist/app.module.js new file mode 100644 index 0000000000000000000000000000000000000000..2e77e6c2766c07ee5ec9231e2c4c560705aa4754 --- /dev/null +++ b/apps/api/dist/app.module.js @@ -0,0 +1,209 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AppModule = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const bullmq_1 = require("@nestjs/bullmq"); +const throttler_1 = require("@nestjs/throttler"); +const throttler_storage_redis_1 = require("@nest-lab/throttler-storage-redis"); +const core_1 = require("@nestjs/core"); +const ioredis_1 = __importDefault(require("ioredis")); +const prisma_module_1 = require("./prisma/prisma.module"); +const common_module_1 = require("./common/common.module"); +const admin_module_1 = require("./admin/admin.module"); +const audit_module_1 = require("./audit/audit.module"); +const redis_module_1 = require("./redis/redis.module"); +const trpc_module_1 = require("./trpc/trpc.module"); +const auth_module_1 = require("./auth/auth.module"); +const guest_module_1 = require("./guest/guest.module"); +const outreach_module_1 = require("./outreach/outreach.module"); +const scheduling_module_1 = require("./scheduling/scheduling.module"); +const assets_module_1 = require("./assets/assets.module"); +const analytics_module_1 = require("./analytics/analytics.module"); +const content_studio_module_1 = require("./content-studio/content-studio.module"); +const websocket_module_1 = require("./websocket/websocket.module"); +const queue_module_1 = require("./queue/queue.module"); +const health_module_1 = require("./health/health.module"); +const workspace_module_1 = require("./workspace/workspace.module"); +const podcast_module_1 = require("./podcast/podcast.module"); +const cache_module_1 = require("./common/cache/cache.module"); +const rate_limit_module_1 = require("./common/rate-limit/rate-limit.module"); +const file_security_module_1 = require("./common/file-security/file-security.module"); +const events_module_1 = require("./events/events.module"); +const network_module_1 = require("./network/network.module"); +const recording_module_1 = require("./recording/recording.module"); +const users_module_1 = require("./users/users.module"); +const notifications_module_1 = require("./notifications/notifications.module"); +const portal_module_1 = require("./portal/portal.module"); +const diary_module_1 = require("./diary/diary.module"); +const billing_module_1 = require("./billing/billing.module"); +const wallet_module_1 = require("./wallet/wallet.module"); +const reconciliation_module_1 = require("./reconciliation/reconciliation.module"); +const settings_module_1 = require("./settings/settings.module"); +const integrations_module_1 = require("./integrations/integrations.module"); +const livekit_module_1 = require("./livekit/livekit.module"); +const ai_module_1 = require("./ai/ai.module"); +const crm_module_1 = require("./crm/crm.module"); +const chat_module_1 = require("./chat/chat.module"); +const tasks_module_1 = require("./tasks/tasks.module"); +const tracing_module_1 = require("./common/tracing/tracing.module"); +const workspace_verification_module_1 = require("./verification/workspace-verification.module"); +const outbound_webhook_module_1 = require("./webhooks/outbound-webhook.module"); +const env_validation_1 = require("./common/config/env.validation"); +let AppModule = class AppModule { +}; +exports.AppModule = AppModule; +exports.AppModule = AppModule = __decorate([ + (0, common_1.Module)({ + imports: [ + common_module_1.CommonModule, + tracing_module_1.TracingModule, + config_1.ConfigModule.forRoot({ + isGlobal: true, + envFilePath: [".env.local", ".env", "../../.env.local", "../../.env"], + cache: true, + validate: env_validation_1.validate, + }), + throttler_1.ThrottlerModule.forRootAsync({ + inject: [config_1.ConfigService], + useFactory: (configService) => { + const ttl = configService.get("RATE_LIMIT_TTL", 60); + const limit = configService.get("RATE_LIMIT_MAX", 100); + const redisUrl = configService.get("REDIS_URL"); + let redisConnection; + if (redisUrl) { + redisConnection = new ioredis_1.default(redisUrl, { maxRetriesPerRequest: null, enableReadyCheck: false }); + } + else { + redisConnection = new ioredis_1.default({ + host: configService.get("REDIS_HOST") || "localhost", + port: parseInt(configService.get("REDIS_PORT") || "6379", 10), + password: configService.get("REDIS_PASSWORD"), + username: configService.get("REDIS_USERNAME"), + maxRetriesPerRequest: null, + enableReadyCheck: false, + }); + } + return { + throttlers: [ + { + name: "short", + ttl: 1000, + limit: 10, + }, + { + name: "medium", + ttl: ttl * 1000, + limit, + }, + { + name: "long", + ttl: 3600000, + limit: 1000, + }, + ], + storage: new throttler_storage_redis_1.ThrottlerStorageRedisService(redisConnection), + }; + }, + }), + bullmq_1.BullModule.forRootAsync({ + inject: [config_1.ConfigService], + useFactory: (configService) => { + const redisUrl = configService.get("REDIS_URL"); + let connectionParams = {}; + if (redisUrl) { + const parsedUrl = new URL(redisUrl); + connectionParams = { + host: parsedUrl.hostname, + port: parseInt(parsedUrl.port || "6379", 10), + username: parsedUrl.username ? decodeURIComponent(parsedUrl.username) : undefined, + password: parsedUrl.password ? decodeURIComponent(parsedUrl.password) : undefined, + tls: redisUrl.startsWith("rediss://") ? { rejectUnauthorized: false } : undefined, + }; + } + else { + connectionParams = { + host: configService.get("REDIS_HOST") || "localhost", + port: parseInt(configService.get("REDIS_PORT") || "6379", 10), + password: configService.get("REDIS_PASSWORD"), + username: configService.get("REDIS_USERNAME"), + tls: configService.get("REDIS_TLS") === "true" ? {} : undefined, + }; + } + return { + connection: connectionParams, + defaultJobOptions: { + attempts: 3, + backoff: { + type: "exponential", + delay: 1000, + }, + removeOnComplete: { + age: 86400, + count: 100, + }, + removeOnFail: { + age: 604800, + }, + }, + }; + }, + }), + admin_module_1.AdminModule, + audit_module_1.AuditModule, + cache_module_1.CacheModule, + health_module_1.HealthModule, + prisma_module_1.PrismaModule, + redis_module_1.RedisModule, + rate_limit_module_1.RateLimitModule, + file_security_module_1.FileSecurityModule, + events_module_1.EventsModule, + trpc_module_1.TrpcModule, + websocket_module_1.WebsocketModule, + outbound_webhook_module_1.OutboundWebhookModule, + queue_module_1.QueueModule, + auth_module_1.AuthModule, + workspace_module_1.WorkspaceModule, + workspace_verification_module_1.WorkspaceVerificationModule, + podcast_module_1.PodcastModule, + guest_module_1.GuestModule, + outreach_module_1.OutreachModule, + scheduling_module_1.SchedulingModule, + assets_module_1.AssetsModule, + analytics_module_1.AnalyticsModule, + content_studio_module_1.ContentStudioModule, + network_module_1.NetworkModule, + recording_module_1.RecordingModule, + users_module_1.UsersModule, + notifications_module_1.NotificationsModule, + portal_module_1.PortalModule, + diary_module_1.DiaryModule, + billing_module_1.BillingModule, + wallet_module_1.WalletModule, + reconciliation_module_1.ReconciliationModule, + settings_module_1.SettingsModule, + integrations_module_1.IntegrationsModule, + livekit_module_1.LivekitModule, + ai_module_1.AIModule, + crm_module_1.CrmModule, + chat_module_1.ChatModule, + tasks_module_1.TasksModule, + ], + providers: [ + { + provide: core_1.APP_GUARD, + useClass: throttler_1.ThrottlerGuard, + }, + ], + }) +], AppModule); +//# sourceMappingURL=app.module.js.map \ No newline at end of file diff --git a/apps/api/dist/app.module.js.map b/apps/api/dist/app.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..528fb9c1c3093c402cabc8541d6b4699d40056f3 --- /dev/null +++ b/apps/api/dist/app.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/assets/assets.module.js b/apps/api/dist/assets/assets.module.js new file mode 100644 index 0000000000000000000000000000000000000000..373d003032ad29ac1d044f74b9674c5c213979e2 --- /dev/null +++ b/apps/api/dist/assets/assets.module.js @@ -0,0 +1,22 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AssetsModule = void 0; +const common_1 = require("@nestjs/common"); +const assets_service_1 = require("./assets.service"); +const assets_router_1 = require("./assets.router"); +let AssetsModule = class AssetsModule { +}; +exports.AssetsModule = AssetsModule; +exports.AssetsModule = AssetsModule = __decorate([ + (0, common_1.Module)({ + providers: [assets_service_1.AssetsService, assets_router_1.AssetsRouter], + exports: [assets_service_1.AssetsService, assets_router_1.AssetsRouter], + }) +], AssetsModule); +//# sourceMappingURL=assets.module.js.map \ No newline at end of file diff --git a/apps/api/dist/assets/assets.module.js.map b/apps/api/dist/assets/assets.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..7559139410cfb7de50723e42f78fa97f60534fb6 --- /dev/null +++ b/apps/api/dist/assets/assets.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"assets.module.js","sourceRoot":"","sources":["../../src/assets/assets.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AAExC,qDAAiD;AACjD,mDAA+C;AAMxC,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAI,CAAA;AAAhB,oCAAY;uBAAZ,YAAY;IAJxB,IAAA,eAAM,EAAC;QACJ,SAAS,EAAE,CAAC,8BAAa,EAAE,4BAAY,CAAC;QACxC,OAAO,EAAE,CAAC,8BAAa,EAAE,4BAAY,CAAC;KACzC,CAAC;GACW,YAAY,CAAI"} \ No newline at end of file diff --git a/apps/api/dist/assets/assets.router.js b/apps/api/dist/assets/assets.router.js new file mode 100644 index 0000000000000000000000000000000000000000..39d25da75005525b33d1a7b6264b8b9be7115412 --- /dev/null +++ b/apps/api/dist/assets/assets.router.js @@ -0,0 +1,69 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AssetsRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const shared_types_1 = require("@micprotocol/shared-types"); +const trpc_init_1 = require("../trpc/trpc.init"); +const subscription_features_1 = require("../billing/subscription-features"); +const assets_service_1 = require("./assets.service"); +let AssetsRouter = class AssetsRouter { + assetsService; + constructor(assetsService) { + this.assetsService = assetsService; + } + get router() { + return (0, trpc_init_1.router)({ + createRequest: (0, trpc_init_1.requireFeature)(subscription_features_1.SubscriptionFeature.ASSET_FORMS) + .input(shared_types_1.createAssetRequestSchema) + .mutation(async ({ input, ctx }) => { + return this.assetsService.createRequest(input, ctx.userId); + }), + getRequest: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ id: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.assetsService.getRequest(input.id, ctx.userId); + }), + listByEpisode: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ episodeId: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.assetsService.listByEpisode(input.episodeId, ctx.userId); + }), + submit: (0, trpc_init_1.rateLimitedProcedure)("assets:submit", 20, 60_000) + .input(shared_types_1.createAssetSubmissionSchema) + .mutation(async ({ input, ctx }) => { + return this.assetsService.submitAsset(input, ctx.userId ?? undefined); + }), + approve: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ id: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => { + return this.assetsService.approveSubmission(input.id, ctx.userId); + }), + reject: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ id: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => { + return this.assetsService.rejectSubmission(input.id, ctx.userId); + }), + completionStatus: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ episodeId: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.assetsService.getCompletionStatus(input.episodeId, ctx.userId); + }), + }); + } +}; +exports.AssetsRouter = AssetsRouter; +exports.AssetsRouter = AssetsRouter = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [assets_service_1.AssetsService]) +], AssetsRouter); +//# sourceMappingURL=assets.router.js.map \ No newline at end of file diff --git a/apps/api/dist/assets/assets.router.js.map b/apps/api/dist/assets/assets.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..3ecfc3d07177e5fbb883e43d424c1236e1e5b9d1 --- /dev/null +++ b/apps/api/dist/assets/assets.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"assets.router.js","sourceRoot":"","sources":["../../src/assets/assets.router.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/assets/assets.service.js b/apps/api/dist/assets/assets.service.js new file mode 100644 index 0000000000000000000000000000000000000000..2d0a6ba19cb9959c376e6f655624f6f909ec37d6 --- /dev/null +++ b/apps/api/dist/assets/assets.service.js @@ -0,0 +1,142 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AssetsService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AssetsService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const authorization_service_1 = require("../common/authorization.service"); +let AssetsService = AssetsService_1 = class AssetsService { + prisma; + authorizationService; + logger = new common_1.Logger(AssetsService_1.name); + constructor(prisma, authorizationService) { + this.prisma = prisma; + this.authorizationService = authorizationService; + } + async createRequest(data, userId) { + await this.authorizationService.checkEpisodeAccess(data.episodeId, userId); + this.logger.log(`Creating ${data.type} asset request for episode ${data.episodeId}`); + return this.prisma.assetRequest.create({ + data: { + episodeId: data.episodeId, + type: data.type, + isRequired: data.isRequired ?? true, + instructions: data.instructions || null, + dueDate: data.dueDate ? new Date(data.dueDate) : null, + }, + include: { + submissions: true, + }, + }); + } + async getRequest(id, userId) { + const request = await this.prisma.assetRequest.findUnique({ + where: { id }, + include: { + submissions: { orderBy: { submittedAt: "desc" } }, + episode: { + include: { + guest: { select: { fullName: true, email: true } }, + }, + }, + }, + }); + if (!request) { + throw new common_1.NotFoundException(`Asset request ${id} not found`); + } + await this.authorizationService.checkEpisodeAccess(request.episodeId, userId); + return request; + } + async listByEpisode(episodeId, userId) { + await this.authorizationService.checkEpisodeAccess(episodeId, userId); + return this.prisma.assetRequest.findMany({ + where: { episodeId }, + include: { + submissions: { orderBy: { submittedAt: "desc" } }, + }, + orderBy: { createdAt: "asc" }, + }); + } + async submitAsset(data, userId) { + if (userId) { + await this.authorizationService.checkAssetRequestAccess(data.requestId, userId); + } + this.logger.log(`Asset submission for request ${data.requestId}`); + return this.prisma.assetSubmission.create({ + data: { + requestId: data.requestId, + fileUrl: data.fileUrl, + fileType: data.fileType, + fileSize: data.fileSize, + status: data.status || "pending", + }, + include: { + request: true, + }, + }); + } + async approveSubmission(id, userId) { + await this.authorizationService.checkAssetSubmissionAccess(id, userId); + return this.prisma.assetSubmission.update({ + where: { id }, + data: { status: "approved" }, + }); + } + async rejectSubmission(id, userId) { + await this.authorizationService.checkAssetSubmissionAccess(id, userId); + return this.prisma.assetSubmission.update({ + where: { id }, + data: { status: "rejected" }, + }); + } + async getCompletionStatus(episodeId, userId) { + await this.authorizationService.checkEpisodeAccess(episodeId, userId); + const requests = await this.prisma.assetRequest.findMany({ + where: { episodeId }, + include: { + submissions: { where: { status: "approved" } }, + }, + }); + const total = requests.length; + const required = requests.filter((r) => r.isRequired).length; + const fulfilled = requests.filter((r) => r.submissions.length > 0).length; + const requiredFulfilled = requests.filter((r) => r.isRequired && r.submissions.length > 0).length; + return { + total, + required, + fulfilled, + requiredFulfilled, + isComplete: requiredFulfilled >= required, + completionPercentage: total > 0 ? Math.round((fulfilled / total) * 100) : 100, + }; + } + async createBulkRequests(episodeId, userId, requests) { + await this.authorizationService.checkEpisodeAccess(episodeId, userId); + const created = await Promise.all(requests.map((req) => this.prisma.assetRequest.create({ + data: { + episodeId, + type: req.type, + isRequired: req.isRequired, + instructions: req.instructions || null, + dueDate: req.dueDate ? new Date(req.dueDate) : null, + }, + }))); + return created; + } +}; +exports.AssetsService = AssetsService; +exports.AssetsService = AssetsService = AssetsService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + authorization_service_1.AuthorizationService]) +], AssetsService); +//# sourceMappingURL=assets.service.js.map \ No newline at end of file diff --git a/apps/api/dist/assets/assets.service.js.map b/apps/api/dist/assets/assets.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..c5b586c0bd0ce1eaeb6ce1da70ca8f4fe256ab1d --- /dev/null +++ b/apps/api/dist/assets/assets.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"assets.service.js","sourceRoot":"","sources":["../../src/assets/assets.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAuE;AAIvE,6DAAyD;AACzD,2EAAuE;AAGhE,IAAM,aAAa,qBAAnB,MAAM,aAAa;IAID;IACA;IAJJ,MAAM,GAAG,IAAI,eAAM,CAAC,eAAa,CAAC,IAAI,CAAC,CAAC;IAEzD,YACqB,MAAqB,EACrB,oBAA0C;QAD1C,WAAM,GAAN,MAAM,CAAe;QACrB,yBAAoB,GAApB,oBAAoB,CAAsB;IAC3D,CAAC;IAEL,KAAK,CAAC,aAAa,CAAC,IAAwB,EAAE,MAAc;QACxD,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC3E,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,IAAI,8BAA8B,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAErF,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;YACnC,IAAI,EAAE;gBACF,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,IAAI,EAAE,IAAI,CAAC,IAA+B;gBAC1C,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;gBACnC,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,IAAI;gBACvC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;aACxD;YACD,OAAO,EAAE;gBACL,WAAW,EAAE,IAAI;aACpB;SACJ,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,MAAc;QACvC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC;YACtD,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,OAAO,EAAE;gBACL,WAAW,EAAE,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE;gBACjD,OAAO,EAAE;oBACL,OAAO,EAAE;wBACL,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;qBACrD;iBACJ;aACJ;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAE9E,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB,EAAE,MAAc;QACjD,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC;YACrC,KAAK,EAAE,EAAE,SAAS,EAAE;YACpB,OAAO,EAAE;gBACL,WAAW,EAAE,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE;aACpD;YACD,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SAChC,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAA2B,EAAE,MAAe;QAC1D,IAAI,MAAM,EAAE,CAAC;YACT,MAAM,IAAI,CAAC,oBAAoB,CAAC,uBAAuB,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACpF,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,gCAAgC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAElE,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;YACtC,IAAI,EAAE;gBACF,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS;aACnC;YACD,OAAO,EAAE;gBACL,OAAO,EAAE,IAAI;aAChB;SACJ,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,EAAU,EAAE,MAAc;QAC9C,MAAM,IAAI,CAAC,oBAAoB,CAAC,0BAA0B,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;YACtC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE;SAC/B,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,EAAU,EAAE,MAAc;QAC7C,MAAM,IAAI,CAAC,oBAAoB,CAAC,0BAA0B,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;YACtC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE;SAC/B,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,SAAiB,EAAE,MAAc;QACvD,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC;YACrD,KAAK,EAAE,EAAE,SAAS,EAAE;YACpB,OAAO,EAAE;gBACL,WAAW,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE;aACjD;SACJ,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;QAC9B,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;QAC7D,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;QAC1E,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CACrC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAClD,CAAC,MAAM,CAAC;QAET,OAAO;YACH,KAAK;YACL,QAAQ;YACR,SAAS;YACT,iBAAiB;YACjB,UAAU,EAAE,iBAAiB,IAAI,QAAQ;YACzC,oBAAoB,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG;SAChF,CAAC;IACN,CAAC;IAED,KAAK,CAAC,kBAAkB,CACpB,SAAiB,EACjB,MAAc,EACd,QAKE;QAEF,MAAM,IAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC7B,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACjB,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;YAC5B,IAAI,EAAE;gBACF,SAAS;gBACT,IAAI,EAAE,GAAG,CAAC,IAA+B;gBACzC,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI;gBACtC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;aACtD;SACJ,CAAC,CACL,CACJ,CAAC;QAEF,OAAO,OAAO,CAAC;IACnB,CAAC;CACJ,CAAA;AAnJY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;qCAKoB,8BAAa;QACC,4CAAoB;GALtD,aAAa,CAmJzB"} \ No newline at end of file diff --git a/apps/api/dist/audit/audit.module.js b/apps/api/dist/audit/audit.module.js new file mode 100644 index 0000000000000000000000000000000000000000..264d04d185a8bbd6d65a3ac92c6fa03c5614021b --- /dev/null +++ b/apps/api/dist/audit/audit.module.js @@ -0,0 +1,24 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuditModule = void 0; +const common_1 = require("@nestjs/common"); +const prisma_module_1 = require("../prisma/prisma.module"); +const audit_service_1 = require("./audit.service"); +const audit_router_1 = require("./audit.router"); +let AuditModule = class AuditModule { +}; +exports.AuditModule = AuditModule; +exports.AuditModule = AuditModule = __decorate([ + (0, common_1.Module)({ + imports: [prisma_module_1.PrismaModule], + providers: [audit_service_1.AuditService, audit_router_1.AuditRouter], + exports: [audit_service_1.AuditService, audit_router_1.AuditRouter], + }) +], AuditModule); +//# sourceMappingURL=audit.module.js.map \ No newline at end of file diff --git a/apps/api/dist/audit/audit.module.js.map b/apps/api/dist/audit/audit.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..17f3d152cfb5d8f905884afa5f57e89256dad955 --- /dev/null +++ b/apps/api/dist/audit/audit.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"audit.module.js","sourceRoot":"","sources":["../../src/audit/audit.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,mDAA+C;AAC/C,iDAA6C;AAOtC,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IALvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,SAAS,EAAE,CAAC,4BAAY,EAAE,0BAAW,CAAC;QACtC,OAAO,EAAE,CAAC,4BAAY,EAAE,0BAAW,CAAC;KACrC,CAAC;GACW,WAAW,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/audit/audit.router.js b/apps/api/dist/audit/audit.router.js new file mode 100644 index 0000000000000000000000000000000000000000..d115ecadccca2c2b45a1e15639a99fefc3b59457 --- /dev/null +++ b/apps/api/dist/audit/audit.router.js @@ -0,0 +1,76 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuditRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const trpc_init_1 = require("../trpc/trpc.init"); +const audit_service_1 = require("./audit.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +const auditListSchema = zod_1.z.object({ + cursor: zod_1.z.string().uuid().optional(), + limit: zod_1.z.number().int().min(1).max(200).optional(), + action: zod_1.z.string().optional(), + entityType: zod_1.z.string().optional(), + userId: zod_1.z.string().uuid().optional(), + workspaceId: zod_1.z.string().uuid().optional(), + startDate: zod_1.z.string().optional(), + endDate: zod_1.z.string().optional(), + search: zod_1.z.string().max(200).optional(), +}); +let AuditRouter = class AuditRouter { + auditService; + constructor(auditService) { + this.auditService = auditService; + } + get router() { + return (0, trpc_init_1.router)({ + list: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.AUDIT_READ) + .input(auditListSchema) + .query(async ({ input }) => { + return this.auditService.listAuditLogs(input); + }), + stats: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.AUDIT_READ).query(async () => { + return this.auditService.getAuditStats(); + }), + verify: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.AUDIT_READ).query(async () => { + return this.auditService.verifyIntegrity(); + }), + export: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.AUDIT_EXPORT) + .input(zod_1.z.object({ + startDate: zod_1.z.string().optional(), + endDate: zod_1.z.string().optional(), + })) + .query(async ({ input, ctx }) => { + const { items, total } = await this.auditService.listAuditLogs({ + startDate: input.startDate, + endDate: input.endDate, + limit: 10_000, + }); + const integrity = await this.auditService.verifyIntegrity(); + return { + exportedAt: new Date().toISOString(), + exportedBy: ctx.userId, + total, + chainValid: integrity.valid, + chainBrokenAt: integrity.brokenAt, + items, + }; + }), + }); + } +}; +exports.AuditRouter = AuditRouter; +exports.AuditRouter = AuditRouter = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [audit_service_1.AuditService]) +], AuditRouter); +//# sourceMappingURL=audit.router.js.map \ No newline at end of file diff --git a/apps/api/dist/audit/audit.router.js.map b/apps/api/dist/audit/audit.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..afb978d9819de4a300a55ffa3d4166aa6f52ac34 --- /dev/null +++ b/apps/api/dist/audit/audit.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"audit.router.js","sourceRoot":"","sources":["../../src/audit/audit.router.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/audit/audit.service.js b/apps/api/dist/audit/audit.service.js new file mode 100644 index 0000000000000000000000000000000000000000..edce7544465e13c8f8a6c7366f44cefb3c5c36be --- /dev/null +++ b/apps/api/dist/audit/audit.service.js @@ -0,0 +1,207 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AuditService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuditService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const prisma_service_1 = require("../prisma/prisma.service"); +const crypto = __importStar(require("crypto")); +let AuditService = AuditService_1 = class AuditService { + prisma; + logger = new common_1.Logger(AuditService_1.name); + enabled; + constructor(prisma, configService) { + this.prisma = prisma; + this.enabled = configService.get("AUDIT_LOG_ENABLED") !== "false"; + } + async log(entry) { + if (!this.enabled) + return; + try { + const previous = await this.prisma.auditLog.findFirst({ + orderBy: { createdAt: "desc" }, + select: { entryHash: true }, + }); + const previousHash = previous?.entryHash ?? null; + const createdAt = new Date(); + const entryHash = this.computeEntryHash(previousHash, entry, createdAt); + await this.prisma.auditLog.create({ + data: { + userId: entry.userId ?? null, + workspaceId: entry.workspaceId ?? null, + action: entry.action, + entityType: entry.entityType ?? null, + entityId: entry.entityId ?? null, + metadata: (entry.metadata ?? {}), + ipAddress: entry.ipAddress ?? null, + userAgent: entry.userAgent ?? null, + entryHash, + previousHash, + createdAt, + }, + }); + } + catch (err) { + this.logger.warn(`Audit log write failed: ${err instanceof Error ? err.message : String(err)}`); + } + } + computeEntryHash(previousHash, entry, createdAt) { + const payload = [ + previousHash ?? "", + entry.action, + entry.entityType ?? "", + entry.entityId ?? "", + JSON.stringify(entry.metadata ?? {}), + createdAt.toISOString(), + entry.userId ?? "", + entry.workspaceId ?? "", + entry.ipAddress ?? "", + entry.userAgent ?? "", + ].join("|"); + return crypto.createHash("sha256").update(payload).digest("hex"); + } + async verifyIntegrity() { + const items = await this.prisma.auditLog.findMany({ + orderBy: { createdAt: "asc" }, + select: { id: true, entryHash: true, previousHash: true, action: true, entityType: true, entityId: true, metadata: true, createdAt: true, userId: true, workspaceId: true, ipAddress: true, userAgent: true }, + }); + let previousHash = null; + let brokenAt = null; + let total = 0; + for (const item of items) { + total++; + const entry = { + userId: item.userId ?? undefined, + workspaceId: item.workspaceId ?? undefined, + action: item.action, + entityType: item.entityType ?? undefined, + entityId: item.entityId ?? undefined, + metadata: item.metadata ?? {}, + ipAddress: item.ipAddress ?? undefined, + userAgent: item.userAgent ?? undefined, + }; + const expected = this.computeEntryHash(previousHash, entry, item.createdAt); + if (expected !== item.entryHash) { + brokenAt = item.id; + break; + } + previousHash = item.entryHash; + } + return { valid: brokenAt === null, brokenAt, total }; + } + async listAuditLogs(query = {}) { + const limit = Math.min(Math.max(query.limit ?? 50, 1), 200); + const cursor = query.cursor; + const where = { + ...(query.action ? { action: query.action } : {}), + ...(query.entityType ? { entityType: query.entityType } : {}), + ...(query.userId ? { userId: query.userId } : {}), + ...(query.workspaceId ? { workspaceId: query.workspaceId } : {}), + ...(query.startDate || query.endDate + ? { + createdAt: { + ...(query.startDate ? { gte: new Date(query.startDate) } : {}), + ...(query.endDate ? { lte: new Date(query.endDate) } : {}), + }, + } + : {}), + ...(query.search + ? { + OR: [ + { action: { contains: query.search, mode: "insensitive" } }, + { entityType: { contains: query.search, mode: "insensitive" } }, + ], + } + : {}), + }; + const [total, items] = await Promise.all([ + this.prisma.auditLog.count({ where }), + this.prisma.auditLog.findMany({ + where, + orderBy: { createdAt: "desc" }, + take: limit + 1, + ...(cursor ? { skip: 1, cursor: { id: cursor } } : {}), + }), + ]); + const hasMore = items.length > limit; + const pageItems = hasMore ? items.slice(0, limit) : items; + return { + items: pageItems, + total, + nextCursor: hasMore ? pageItems[pageItems.length - 1]?.id : null, + }; + } + async getAuditStats() { + const [total, actions, entities, users, last24h] = await Promise.all([ + this.prisma.auditLog.count(), + this.prisma.auditLog.groupBy({ by: ["action"], _count: true }), + this.prisma.auditLog.groupBy({ by: ["entityType"], _count: true }), + this.prisma.auditLog.groupBy({ by: ["userId"], _count: true }), + this.prisma.auditLog.count({ + where: { createdAt: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000) } }, + }), + ]); + const topActions = actions + .map((a) => ({ action: a.action, count: a._count })) + .sort((a, b) => b.count - a.count) + .slice(0, 8); + const topEntities = entities + .map((e) => ({ entityType: e.entityType, count: e._count })) + .sort((a, b) => b.count - a.count) + .slice(0, 8); + const topUsers = users + .map((u) => ({ userId: u.userId, count: u._count })) + .sort((a, b) => b.count - a.count) + .slice(0, 8); + return { total, last24h, topActions, topEntities, topUsers }; + } +}; +exports.AuditService = AuditService; +exports.AuditService = AuditService = AuditService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + config_1.ConfigService]) +], AuditService); +//# sourceMappingURL=audit.service.js.map \ No newline at end of file diff --git a/apps/api/dist/audit/audit.service.js.map b/apps/api/dist/audit/audit.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..2d83531539e11f4f027afc1bf3d02cccc51c19a8 --- /dev/null +++ b/apps/api/dist/audit/audit.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"audit.service.js","sourceRoot":"","sources":["../../src/audit/audit.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/auth/auth.config.js b/apps/api/dist/auth/auth.config.js new file mode 100644 index 0000000000000000000000000000000000000000..5ed6d01a4f8045e267ac3da4f0fb2c62dd0f279a --- /dev/null +++ b/apps/api/dist/auth/auth.config.js @@ -0,0 +1,63 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.auth = void 0; +const better_auth_1 = require("better-auth"); +const prisma_1 = require("better-auth/adapters/prisma"); +const plugins_1 = require("better-auth/plugins"); +const client_1 = require("@prisma/client"); +const adapter_pg_1 = require("@prisma/adapter-pg"); +const globalForPrisma = globalThis; +const prisma = globalForPrisma._betterAuthPrisma ?? new client_1.PrismaClient({ + adapter: new adapter_pg_1.PrismaPg({ connectionString: process.env.DATABASE_URL }), +}); +if (process.env.NODE_ENV !== "production") { + globalForPrisma._betterAuthPrisma = prisma; +} +const trustedOrigins = [ + process.env.WEB_URL ?? "http://localhost:3000", + process.env.FRONTEND_URL, + process.env.API_URL ?? "http://localhost:4000", +].filter(Boolean); +exports.auth = (0, better_auth_1.betterAuth)({ + database: (0, prisma_1.prismaAdapter)(prisma, { + provider: "postgresql", + }), + emailAndPassword: { + enabled: true, + autoSignIn: true, + minPasswordLength: 8, + maxPasswordLength: 128, + }, + socialProviders: { + google: { + clientId: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + scope: ["openid", "profile", "email"], + }, + }, + session: { + expiresIn: 60 * 60 * 24 * 7, + updateAge: 60 * 60 * 24, + cookieCache: { + enabled: true, + maxAge: 5 * 60, + }, + }, + plugins: [ + (0, plugins_1.jwt)(), + (0, plugins_1.bearer)(), + ], + baseURL: process.env.BETTER_AUTH_URL || process.env.API_URL || "http://localhost:4000", + secret: process.env.BETTER_AUTH_SECRET, + trustedOrigins, + advanced: { + database: { + generateId: "uuid", + joins: true, + }, + crossSubDomainCookies: process.env.ROOT_DOMAIN + ? { enabled: true, domain: `.${process.env.ROOT_DOMAIN}` } + : { enabled: false }, + }, +}); +//# sourceMappingURL=auth.config.js.map \ No newline at end of file diff --git a/apps/api/dist/auth/auth.config.js.map b/apps/api/dist/auth/auth.config.js.map new file mode 100644 index 0000000000000000000000000000000000000000..b7a79d753570bcf9c957eaa4d654202572887fe1 --- /dev/null +++ b/apps/api/dist/auth/auth.config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.config.js","sourceRoot":"","sources":["../../src/auth/auth.config.ts"],"names":[],"mappings":";;;AAAA,6CAAyC;AACzC,wDAA4D;AAC5D,iDAAkD;AAClD,2CAA8C;AAC9C,mDAA8C;AAS9C,MAAM,eAAe,GAAG,UAA6D,CAAC;AACtF,MAAM,MAAM,GAAG,eAAe,CAAC,iBAAiB,IAAI,IAAI,qBAAY,CAAC;IACjE,OAAO,EAAE,IAAI,qBAAQ,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;CACxE,CAAC,CAAC;AACH,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;IACxC,eAAe,CAAC,iBAAiB,GAAG,MAAM,CAAC;AAC/C,CAAC;AAED,MAAM,cAAc,GAAG;IACnB,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,uBAAuB;IAC9C,OAAO,CAAC,GAAG,CAAC,YAAY;IACxB,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,uBAAuB;CACjD,CAAC,MAAM,CAAC,OAAO,CAAa,CAAC;AAEjB,QAAA,IAAI,GAAG,IAAA,wBAAU,EAAC;IAC3B,QAAQ,EAAE,IAAA,sBAAa,EAAC,MAAM,EAAE;QAC5B,QAAQ,EAAE,YAAY;KACzB,CAAC;IACF,gBAAgB,EAAE;QACd,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,IAAI;QAEhB,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,GAAG;KACzB;IAKD,eAAe,EAAE;QACb,MAAM,EAAE;YACJ,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,gBAA0B;YAChD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,oBAA8B;YAExD,KAAK,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC;SACxC;KACJ;IACD,OAAO,EAAE;QACL,SAAS,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;QAC3B,SAAS,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE;QACvB,WAAW,EAAE;YACT,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,CAAC,GAAG,EAAE;SACjB;KACJ;IACD,OAAO,EAAE;QACL,IAAA,aAAG,GAAE;QACL,IAAA,gBAAM,GAAE;KACX;IACD,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,uBAAuB;IACtF,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAmB;IACvC,cAAc;IACd,QAAQ,EAAE;QACN,QAAQ,EAAE;YACN,UAAU,EAAE,MAAM;YAClB,KAAK,EAAE,IAAI;SACd;QACD,qBAAqB,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;YAC1C,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE;YAC1D,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE;KAC3B;CACJ,CAAC,CAAC"} \ No newline at end of file diff --git a/apps/api/dist/auth/auth.module.js b/apps/api/dist/auth/auth.module.js new file mode 100644 index 0000000000000000000000000000000000000000..92f47d19ce62e21ce7120032ebe4cd7fd03bb643 --- /dev/null +++ b/apps/api/dist/auth/auth.module.js @@ -0,0 +1,27 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthModule = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const better_auth_guard_1 = require("./better-auth.guard"); +const better_auth_identity_service_1 = require("./better-auth-identity.service"); +const subscription_guard_1 = require("../billing/subscription.guard"); +const prisma_module_1 = require("../prisma/prisma.module"); +let AuthModule = class AuthModule { +}; +exports.AuthModule = AuthModule; +exports.AuthModule = AuthModule = __decorate([ + (0, common_1.Global)(), + (0, common_1.Module)({ + imports: [config_1.ConfigModule, prisma_module_1.PrismaModule], + providers: [better_auth_guard_1.BetterAuthGuard, better_auth_identity_service_1.BetterAuthService, subscription_guard_1.SubscriptionGuard], + exports: [better_auth_guard_1.BetterAuthGuard, better_auth_identity_service_1.BetterAuthService, subscription_guard_1.SubscriptionGuard], + }) +], AuthModule); +//# sourceMappingURL=auth.module.js.map \ No newline at end of file diff --git a/apps/api/dist/auth/auth.module.js.map b/apps/api/dist/auth/auth.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..670c4c715940734becef05e6de7a2c11cf1d2775 --- /dev/null +++ b/apps/api/dist/auth/auth.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../src/auth/auth.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,2CAA8C;AAE9C,2DAAsD;AACtD,iFAAmE;AACnE,sEAAkE;AAClE,2DAAuD;AAQhD,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IANtB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,qBAAY,EAAE,4BAAY,CAAC;QACrC,SAAS,EAAE,CAAC,mCAAe,EAAE,gDAAiB,EAAE,sCAAiB,CAAC;QAClE,OAAO,EAAE,CAAC,mCAAe,EAAE,gDAAiB,EAAE,sCAAiB,CAAC;KACnE,CAAC;GACW,UAAU,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/auth/better-auth-identity.service.js b/apps/api/dist/auth/better-auth-identity.service.js new file mode 100644 index 0000000000000000000000000000000000000000..02378ec5fadacb7cd61cbdc964085f3e4da93095 --- /dev/null +++ b/apps/api/dist/auth/better-auth-identity.service.js @@ -0,0 +1,81 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var BetterAuthService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BetterAuthService = void 0; +const common_1 = require("@nestjs/common"); +const node_1 = require("better-auth/node"); +const auth_config_1 = require("./auth.config"); +let BetterAuthService = BetterAuthService_1 = class BetterAuthService { + logger = new common_1.Logger(BetterAuthService_1.name); + async getSessionFromHeaders(headers) { + try { + const standardHeaders = headers instanceof Headers ? headers : (0, node_1.fromNodeHeaders)(headers); + const session = await auth_config_1.auth.api.getSession({ + headers: standardHeaders, + }); + if (!session) { + return null; + } + return { + userId: session.user.id, + sessionId: session.session.id, + session: { + id: session.session.id, + userId: session.session.userId, + expiresAt: session.session.expiresAt, + ipAddress: session.session.ipAddress ?? undefined, + userAgent: session.session.userAgent ?? undefined, + }, + }; + } + catch (error) { + this.logger.warn("Better Auth session verification failed", error); + return null; + } + } + async getSessionFromBearer(authorizationHeader) { + if (!authorizationHeader?.startsWith("Bearer ")) { + return null; + } + const token = authorizationHeader.slice(7).trim(); + if (!token) { + return null; + } + try { + const session = await auth_config_1.auth.api.getSession({ + headers: new Headers({ + Authorization: `Bearer ${token}`, + }), + }); + if (!session) { + return null; + } + return { + userId: session.user.id, + sessionId: session.session.id, + session: { + id: session.session.id, + userId: session.session.userId, + expiresAt: session.session.expiresAt, + ipAddress: session.session.ipAddress ?? undefined, + userAgent: session.session.userAgent ?? undefined, + }, + }; + } + catch (error) { + this.logger.warn("Better Auth Bearer token verification failed", error); + return null; + } + } +}; +exports.BetterAuthService = BetterAuthService; +exports.BetterAuthService = BetterAuthService = BetterAuthService_1 = __decorate([ + (0, common_1.Injectable)() +], BetterAuthService); +//# sourceMappingURL=better-auth-identity.service.js.map \ No newline at end of file diff --git a/apps/api/dist/auth/better-auth-identity.service.js.map b/apps/api/dist/auth/better-auth-identity.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..0d014170aba6c1c211f3db14060236fe3dd39b2a --- /dev/null +++ b/apps/api/dist/auth/better-auth-identity.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"better-auth-identity.service.js","sourceRoot":"","sources":["../../src/auth/better-auth-identity.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/auth/better-auth.guard.js b/apps/api/dist/auth/better-auth.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..03a395561a6e93494b029ccfa74c906693c8a488 --- /dev/null +++ b/apps/api/dist/auth/better-auth.guard.js @@ -0,0 +1,60 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var BetterAuthGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BetterAuthGuard = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const better_auth_identity_service_1 = require("./better-auth-identity.service"); +let BetterAuthGuard = BetterAuthGuard_1 = class BetterAuthGuard { + betterAuthService; + configService; + logger = new common_1.Logger(BetterAuthGuard_1.name); + devBypassEnabled; + constructor(betterAuthService, configService) { + this.betterAuthService = betterAuthService; + this.configService = configService; + this.devBypassEnabled = this.configService.get("DEV_AUTH_BYPASS_ENABLED") === "true"; + } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + if (process.env.NODE_ENV === "development" && this.devBypassEnabled) { + const userId = request.headers["x-user-id"]; + if (userId) { + request.userId = userId; + return true; + } + } + try { + const identity = await this.betterAuthService.getSessionFromHeaders(request.headers); + if (!identity) { + throw new common_1.UnauthorizedException("Not authenticated"); + } + request.userId = identity.userId; + request.sessionId = identity.sessionId; + return true; + } + catch (error) { + this.logger.error("Session verification failed", error); + if (error instanceof common_1.UnauthorizedException) { + throw error; + } + throw new common_1.UnauthorizedException("Invalid session"); + } + } +}; +exports.BetterAuthGuard = BetterAuthGuard; +exports.BetterAuthGuard = BetterAuthGuard = BetterAuthGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [better_auth_identity_service_1.BetterAuthService, + config_1.ConfigService]) +], BetterAuthGuard); +//# sourceMappingURL=better-auth.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/auth/better-auth.guard.js.map b/apps/api/dist/auth/better-auth.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..588c71fba62cc84144045414c2621d7bc892d9a0 --- /dev/null +++ b/apps/api/dist/auth/better-auth.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"better-auth.guard.js","sourceRoot":"","sources":["../../src/auth/better-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAA8H;AAC9H,2CAA+C;AAE/C,iFAAmE;AAY5D,IAAM,eAAe,uBAArB,MAAM,eAAe;IAKH;IACA;IALJ,MAAM,GAAG,IAAI,eAAM,CAAC,iBAAe,CAAC,IAAI,CAAC,CAAC;IAC1C,gBAAgB,CAAU;IAE3C,YACqB,iBAAoC,EACpC,aAA4B;QAD5B,sBAAiB,GAAjB,iBAAiB,CAAmB;QACpC,kBAAa,GAAb,aAAa,CAAe;QAE7C,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,yBAAyB,CAAC,KAAK,MAAM,CAAC;IACjG,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAyB;QACvC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAEpD,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAClE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,MAAM,EAAE,CAAC;gBACT,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;gBACxB,OAAO,IAAI,CAAC;YAChB,CAAC;QACL,CAAC;QAED,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,qBAAqB,CAC/D,OAAO,CAAC,OAAwD,CACnE,CAAC;YAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACZ,MAAM,IAAI,8BAAqB,CAAC,mBAAmB,CAAC,CAAC;YACzD,CAAC;YAED,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YACjC,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC;YAEvC,OAAO,IAAI,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;YAExD,IAAI,KAAK,YAAY,8BAAqB,EAAE,CAAC;gBACzC,MAAM,KAAK,CAAC;YAChB,CAAC;YAED,MAAM,IAAI,8BAAqB,CAAC,iBAAiB,CAAC,CAAC;QACvD,CAAC;IACL,CAAC;CACJ,CAAA;AA7CY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAM+B,gDAAiB;QACrB,sBAAa;GANxC,eAAe,CA6C3B"} \ No newline at end of file diff --git a/apps/api/dist/auth/internal-token.guard.js b/apps/api/dist/auth/internal-token.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..4230e4ba6b8627cfa56ef1935f6dd87f9770616d --- /dev/null +++ b/apps/api/dist/auth/internal-token.guard.js @@ -0,0 +1,41 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.InternalTokenGuard = void 0; +const common_1 = require("@nestjs/common"); +const crypto_1 = require("crypto"); +const INTERNAL_TOKEN = process.env.INTERNAL_API_TOKEN ?? process.env.MIC_REALTIME_INTERNAL_TOKEN; +if (process.env.INTERNAL_API_TOKEN && + process.env.MIC_REALTIME_INTERNAL_TOKEN && + process.env.INTERNAL_API_TOKEN !== process.env.MIC_REALTIME_INTERNAL_TOKEN) { + throw new Error("INTERNAL_API_TOKEN and MIC_REALTIME_INTERNAL_TOKEN must be identical. " + + "They are the shared secret for both directions of the API-Realtime bridge."); +} +let InternalTokenGuard = class InternalTokenGuard { + canActivate(context) { + const req = context.switchToHttp().getRequest(); + const token = req.headers["x-internal-token"]; + const expected = INTERNAL_TOKEN; + if (!expected || !token || !timingSafeEqualStrings(token, expected)) { + throw new common_1.UnauthorizedException("invalid internal token"); + } + return true; + } +}; +exports.InternalTokenGuard = InternalTokenGuard; +exports.InternalTokenGuard = InternalTokenGuard = __decorate([ + (0, common_1.Injectable)() +], InternalTokenGuard); +function timingSafeEqualStrings(a, b) { + const bufA = Buffer.from(a); + const bufB = Buffer.from(b); + if (bufA.length !== bufB.length) + return false; + return (0, crypto_1.timingSafeEqual)(bufA, bufB); +} +//# sourceMappingURL=internal-token.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/auth/internal-token.guard.js.map b/apps/api/dist/auth/internal-token.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..362ab050ae01985c92904dd83c830160d0faf686 --- /dev/null +++ b/apps/api/dist/auth/internal-token.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"internal-token.guard.js","sourceRoot":"","sources":["../../src/auth/internal-token.guard.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAKwB;AACxB,mCAAyC;AAGzC,MAAM,cAAc,GAChB,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC;AAE9E,IACI,OAAO,CAAC,GAAG,CAAC,kBAAkB;IAC9B,OAAO,CAAC,GAAG,CAAC,2BAA2B;IACvC,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAC5E,CAAC;IACC,MAAM,IAAI,KAAK,CACX,wEAAwE;QACpE,4EAA4E,CACnF,CAAC;AACN,CAAC;AAYM,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IAC3B,WAAW,CAAC,OAAyB;QACjC,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAW,CAAC;QACzD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAuB,CAAC;QACpE,MAAM,QAAQ,GAAG,cAAc,CAAC;QAEhC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;YAClE,MAAM,IAAI,8BAAqB,CAAC,wBAAwB,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ,CAAA;AAXY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;GACA,kBAAkB,CAW9B;AAED,SAAS,sBAAsB,CAAC,CAAS,EAAE,CAAS;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC9C,OAAO,IAAA,wBAAe,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"} \ No newline at end of file diff --git a/apps/api/dist/billing/billing.controller.js b/apps/api/dist/billing/billing.controller.js new file mode 100644 index 0000000000000000000000000000000000000000..ddaba87c70ecc4365b863e51393260e9c2a556ed --- /dev/null +++ b/apps/api/dist/billing/billing.controller.js @@ -0,0 +1,184 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BillingController = void 0; +const common_1 = require("@nestjs/common"); +const swagger_1 = require("@nestjs/swagger"); +const better_auth_guard_1 = require("../auth/better-auth.guard"); +const billing_service_1 = require("./billing.service"); +const stripe_webhook_guard_1 = require("./stripe-webhook.guard"); +const mpesa_webhook_guard_1 = require("./mpesa-webhook.guard"); +const payhero_webhook_guard_1 = require("./payhero-webhook.guard"); +const paystack_webhook_guard_1 = require("./paystack-webhook.guard"); +const billing_dto_1 = require("./billing.dto"); +const raw_body_middleware_1 = require("../common/middleware/raw-body.middleware"); +let BillingController = class BillingController { + billingService; + constructor(billingService) { + this.billingService = billingService; + } + listPlans() { + return this.billingService.listPlans(); + } + getWorkspaceSubscription(req, workspaceId) { + const userId = req.userId || req.headers["x-user-id"]; + return this.billingService.getWorkspaceSubscription(userId, workspaceId); + } + createStripeCheckout(req, body) { + const userId = req.userId || req.headers["x-user-id"]; + return this.billingService.createStripeCheckout(userId, body); + } + initiateMpesaCheckout(req, body) { + const userId = req.userId || req.headers["x-user-id"]; + return this.billingService.initiateMpesaCheckout(userId, body); + } + initiatePaystackCheckout(req, body) { + const userId = req.userId || req.headers["x-user-id"]; + return this.billingService.initiatePaystackCheckout(userId, { + ...body, + email: body.email || `${userId}@placeholder.local`, + }); + } + verifyPaystackCheckout(req, body) { + const userId = req.userId || req.headers["x-user-id"]; + return this.billingService.verifyPaystackCheckout(userId, body.workspaceId, body.reference); + } + handleStripeWebhook(req) { + const event = req.stripeEvent; + return this.billingService.handleStripeWebhook(event); + } + handleMpesaWebhook(req) { + return this.billingService.handleMpesaWebhook(req.body); + } + handlePayheroWebhook(req) { + return this.billingService.handlePayheroWebhook(req.body, req.query); + } + handlePaystackWebhook(req) { + return this.billingService.handlePaystackWebhook(req.body); + } +}; +exports.BillingController = BillingController; +__decorate([ + (0, common_1.Get)("plans"), + (0, swagger_1.ApiOperation)({ summary: "List all subscription plans" }), + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], BillingController.prototype, "listPlans", null); +__decorate([ + (0, common_1.Get)("workspaces/:workspaceId/subscription"), + (0, common_1.UseGuards)(better_auth_guard_1.BetterAuthGuard), + (0, swagger_1.ApiBearerAuth)(), + (0, swagger_1.ApiParam)({ name: "workspaceId" }), + (0, swagger_1.ApiOperation)({ summary: "Get current workspace subscription" }), + __param(0, (0, common_1.Req)()), + __param(1, (0, common_1.Param)("workspaceId")), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, String]), + __metadata("design:returntype", void 0) +], BillingController.prototype, "getWorkspaceSubscription", null); +__decorate([ + (0, common_1.Post)("checkout/stripe"), + (0, common_1.UseGuards)(better_auth_guard_1.BetterAuthGuard), + (0, swagger_1.ApiBearerAuth)(), + (0, swagger_1.ApiOperation)({ summary: "Initialize Stripe checkout" }), + __param(0, (0, common_1.Req)()), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, billing_dto_1.CreateStripeCheckoutDto]), + __metadata("design:returntype", void 0) +], BillingController.prototype, "createStripeCheckout", null); +__decorate([ + (0, common_1.Post)("checkout/mpesa"), + (0, common_1.UseGuards)(better_auth_guard_1.BetterAuthGuard), + (0, swagger_1.ApiBearerAuth)(), + (0, swagger_1.ApiOperation)({ summary: "Initialize M-Pesa STK push checkout" }), + __param(0, (0, common_1.Req)()), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, billing_dto_1.InitiateMpesaCheckoutDto]), + __metadata("design:returntype", void 0) +], BillingController.prototype, "initiateMpesaCheckout", null); +__decorate([ + (0, common_1.Post)("checkout/paystack"), + (0, common_1.UseGuards)(better_auth_guard_1.BetterAuthGuard), + (0, swagger_1.ApiBearerAuth)(), + (0, swagger_1.ApiOperation)({ + summary: "Initialize Paystack checkout (card, M-Pesa, Airtel Money)", + }), + __param(0, (0, common_1.Req)()), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, Object]), + __metadata("design:returntype", void 0) +], BillingController.prototype, "initiatePaystackCheckout", null); +__decorate([ + (0, common_1.Post)("checkout/paystack/verify"), + (0, common_1.UseGuards)(better_auth_guard_1.BetterAuthGuard), + (0, swagger_1.ApiBearerAuth)(), + (0, swagger_1.ApiOperation)({ summary: "Manually verify a Paystack transaction reference" }), + __param(0, (0, common_1.Req)()), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, Object]), + __metadata("design:returntype", void 0) +], BillingController.prototype, "verifyPaystackCheckout", null); +__decorate([ + (0, common_1.Post)("webhooks/stripe"), + (0, common_1.UseInterceptors)(raw_body_middleware_1.RawBodyMiddleware), + (0, common_1.UseGuards)(stripe_webhook_guard_1.StripeWebhookGuard), + (0, swagger_1.ApiOperation)({ summary: "Stripe webhook callback - signature verified" }), + __param(0, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) +], BillingController.prototype, "handleStripeWebhook", null); +__decorate([ + (0, common_1.Post)("webhooks/mpesa"), + (0, common_1.UseInterceptors)(raw_body_middleware_1.RawBodyMiddleware), + (0, common_1.UseGuards)(mpesa_webhook_guard_1.MpesaWebhookGuard), + (0, swagger_1.ApiOperation)({ summary: "M-Pesa callback - signature verified in production" }), + __param(0, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) +], BillingController.prototype, "handleMpesaWebhook", null); +__decorate([ + (0, common_1.Post)("webhooks/payhero"), + (0, common_1.UseInterceptors)(raw_body_middleware_1.RawBodyMiddleware), + (0, common_1.UseGuards)(payhero_webhook_guard_1.PayheroWebhookGuard), + (0, swagger_1.ApiOperation)({ summary: "PayHero webhook callback - query signature verified" }), + __param(0, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) +], BillingController.prototype, "handlePayheroWebhook", null); +__decorate([ + (0, common_1.Post)("webhooks/paystack"), + (0, common_1.UseInterceptors)(raw_body_middleware_1.RawBodyMiddleware), + (0, common_1.UseGuards)(paystack_webhook_guard_1.PaystackWebhookGuard), + (0, swagger_1.ApiOperation)({ + summary: "Paystack webhook callback - HMAC-SHA512 signature verified", + }), + __param(0, (0, common_1.Req)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) +], BillingController.prototype, "handlePaystackWebhook", null); +exports.BillingController = BillingController = __decorate([ + (0, swagger_1.ApiTags)("Billing"), + (0, common_1.Controller)("billing"), + __metadata("design:paramtypes", [billing_service_1.BillingService]) +], BillingController); +//# sourceMappingURL=billing.controller.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/billing.controller.js.map b/apps/api/dist/billing/billing.controller.js.map new file mode 100644 index 0000000000000000000000000000000000000000..47eedec2045c90c3da9e9c22db478b8009d9994a --- /dev/null +++ b/apps/api/dist/billing/billing.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"billing.controller.js","sourceRoot":"","sources":["../../src/billing/billing.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/billing.dto.js b/apps/api/dist/billing/billing.dto.js new file mode 100644 index 0000000000000000000000000000000000000000..06e42e8bf120a03919a01cc9624a25c274611cb5 --- /dev/null +++ b/apps/api/dist/billing/billing.dto.js @@ -0,0 +1,125 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SubscriptionResponseDto = exports.InitiateMpesaCheckoutDto = exports.CreateStripeCheckoutDto = void 0; +const swagger_1 = require("@nestjs/swagger"); +const class_validator_1 = require("class-validator"); +const subscriptionPlanValues = ["hobby", "creator", "studio"]; +const billingProviderValues = ["stripe", "mpesa", "paystack"]; +class CreateStripeCheckoutDto { + workspaceId; + planId; + successUrl; + cancelUrl; +} +exports.CreateStripeCheckoutDto = CreateStripeCheckoutDto; +__decorate([ + (0, swagger_1.ApiProperty)({ format: "uuid" }), + (0, class_validator_1.IsUUID)(), + __metadata("design:type", String) +], CreateStripeCheckoutDto.prototype, "workspaceId", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ enum: subscriptionPlanValues }), + (0, class_validator_1.IsEnum)(subscriptionPlanValues), + __metadata("design:type", String) +], CreateStripeCheckoutDto.prototype, "planId", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsUrl)(), + __metadata("design:type", String) +], CreateStripeCheckoutDto.prototype, "successUrl", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsUrl)(), + __metadata("design:type", String) +], CreateStripeCheckoutDto.prototype, "cancelUrl", void 0); +class InitiateMpesaCheckoutDto { + workspaceId; + planId; + phoneNumber; + accountReference; + transactionDesc; +} +exports.InitiateMpesaCheckoutDto = InitiateMpesaCheckoutDto; +__decorate([ + (0, swagger_1.ApiProperty)({ format: "uuid" }), + (0, class_validator_1.IsUUID)(), + __metadata("design:type", String) +], InitiateMpesaCheckoutDto.prototype, "workspaceId", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ enum: subscriptionPlanValues }), + (0, class_validator_1.IsEnum)(subscriptionPlanValues), + __metadata("design:type", String) +], InitiateMpesaCheckoutDto.prototype, "planId", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ description: "Kenyan phone number in 07xxxxxxxx or 2547xxxxxxxx format" }), + (0, class_validator_1.IsString)(), + (0, class_validator_1.Length)(9, 20), + (0, class_validator_1.Matches)(/^(?:\+?254|0)[17]\d{8}$/, { + message: "Phone number must be a valid Kenyan Safaricom number (07xx or 2547xx format)", + }), + __metadata("design:type", String) +], InitiateMpesaCheckoutDto.prototype, "phoneNumber", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], InitiateMpesaCheckoutDto.prototype, "accountReference", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + (0, class_validator_1.MaxLength)(120), + __metadata("design:type", String) +], InitiateMpesaCheckoutDto.prototype, "transactionDesc", void 0); +class SubscriptionResponseDto { + workspaceId; + planId; + provider; + status; + updatedAt; + renewsAt; + checkoutUrl; +} +exports.SubscriptionResponseDto = SubscriptionResponseDto; +__decorate([ + (0, swagger_1.ApiProperty)({ format: "uuid" }), + __metadata("design:type", String) +], SubscriptionResponseDto.prototype, "workspaceId", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ enum: subscriptionPlanValues }), + __metadata("design:type", String) +], SubscriptionResponseDto.prototype, "planId", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ enum: billingProviderValues }), + (0, class_validator_1.IsEnum)(billingProviderValues), + __metadata("design:type", String) +], SubscriptionResponseDto.prototype, "provider", void 0); +__decorate([ + (0, swagger_1.ApiProperty)(), + __metadata("design:type", String) +], SubscriptionResponseDto.prototype, "status", void 0); +__decorate([ + (0, swagger_1.ApiProperty)(), + __metadata("design:type", String) +], SubscriptionResponseDto.prototype, "updatedAt", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + __metadata("design:type", Object) +], SubscriptionResponseDto.prototype, "renewsAt", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + __metadata("design:type", Object) +], SubscriptionResponseDto.prototype, "checkoutUrl", void 0); +//# sourceMappingURL=billing.dto.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/billing.dto.js.map b/apps/api/dist/billing/billing.dto.js.map new file mode 100644 index 0000000000000000000000000000000000000000..efedb4d6d457ee33da0217b22f093cf2a6b3f036 --- /dev/null +++ b/apps/api/dist/billing/billing.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"billing.dto.js","sourceRoot":"","sources":["../../src/billing/billing.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAAmE;AACnE,qDAA0G;AAE1G,MAAM,sBAAsB,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AACvE,MAAM,qBAAqB,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAU,CAAC;AAEvE,MAAa,uBAAuB;IAGhC,WAAW,CAAU;IAIrB,MAAM,CAAkC;IAKxC,UAAU,CAAU;IAKpB,SAAS,CAAU;CACtB;AAlBD,0DAkBC;AAfG;IAFC,IAAA,qBAAW,EAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC/B,IAAA,wBAAM,GAAE;;4DACY;AAIrB;IAFC,IAAA,qBAAW,EAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC;IAC7C,IAAA,wBAAM,EAAC,sBAAsB,CAAC;;uDACS;AAKxC;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;;2DACY;AAKpB;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;;0DACW;AAGvB,MAAa,wBAAwB;IAGjC,WAAW,CAAU;IAIrB,MAAM,CAAkC;IAQxC,WAAW,CAAU;IAKrB,gBAAgB,CAAU;IAM1B,eAAe,CAAU;CAC5B;AA3BD,4DA2BC;AAxBG;IAFC,IAAA,qBAAW,EAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC/B,IAAA,wBAAM,GAAE;;6DACY;AAIrB;IAFC,IAAA,qBAAW,EAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC;IAC7C,IAAA,wBAAM,EAAC,sBAAsB,CAAC;;wDACS;AAQxC;IANC,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,0DAA0D,EAAE,CAAC;IACxF,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAM,EAAC,CAAC,EAAE,EAAE,CAAC;IACb,IAAA,yBAAO,EAAC,yBAAyB,EAAE;QAChC,OAAO,EAAE,8EAA8E;KAC1F,CAAC;;6DACmB;AAKrB;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;kEACe;AAM1B;IAJC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,GAAG,CAAC;;iEACU;AAG7B,MAAa,uBAAuB;IAEhC,WAAW,CAAU;IAGrB,MAAM,CAAkC;IAIxC,QAAQ,CAAmC;IAG3C,MAAM,CAAU;IAGhB,SAAS,CAAU;IAGnB,QAAQ,CAAiB;IAGzB,WAAW,CAAiB;CAC/B;AAtBD,0DAsBC;AApBG;IADC,IAAA,qBAAW,EAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;;4DACX;AAGrB;IADC,IAAA,qBAAW,EAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC;;uDACN;AAIxC;IAFC,IAAA,qBAAW,EAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC;IAC5C,IAAA,wBAAM,EAAC,qBAAqB,CAAC;;yDACa;AAG3C;IADC,IAAA,qBAAW,GAAE;;uDACE;AAGhB;IADC,IAAA,qBAAW,GAAE;;0DACK;AAGnB;IADC,IAAA,6BAAmB,GAAE;;yDACG;AAGzB;IADC,IAAA,6BAAmB,GAAE;;4DACM"} \ No newline at end of file diff --git a/apps/api/dist/billing/billing.module.js b/apps/api/dist/billing/billing.module.js new file mode 100644 index 0000000000000000000000000000000000000000..7b255e367688d045687a27f7d271e1b94a5ca462 --- /dev/null +++ b/apps/api/dist/billing/billing.module.js @@ -0,0 +1,63 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BillingModule = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const prisma_module_1 = require("../prisma/prisma.module"); +const notifications_module_1 = require("../notifications/notifications.module"); +const wallet_module_1 = require("../wallet/wallet.module"); +const billing_service_1 = require("./billing.service"); +const billing_controller_1 = require("./billing.controller"); +const billing_router_1 = require("./billing.router"); +const stripe_service_1 = require("./stripe.service"); +const stripe_webhook_guard_1 = require("./stripe-webhook.guard"); +const mpesa_service_1 = require("./mpesa.service"); +const mpesa_webhook_guard_1 = require("./mpesa-webhook.guard"); +const payhero_service_1 = require("./payhero.service"); +const payhero_webhook_guard_1 = require("./payhero-webhook.guard"); +const paystack_service_1 = require("./paystack.service"); +const paystack_webhook_guard_1 = require("./paystack-webhook.guard"); +const subscription_credentials_service_1 = require("./subscription-credentials.service"); +let BillingModule = class BillingModule { +}; +exports.BillingModule = BillingModule; +exports.BillingModule = BillingModule = __decorate([ + (0, common_1.Module)({ + imports: [prisma_module_1.PrismaModule, config_1.ConfigModule, notifications_module_1.NotificationsModule, (0, common_1.forwardRef)(() => wallet_module_1.WalletModule)], + providers: [ + billing_service_1.BillingService, + billing_router_1.BillingRouter, + stripe_service_1.StripeService, + stripe_webhook_guard_1.StripeWebhookGuard, + mpesa_service_1.MpesaService, + mpesa_webhook_guard_1.MpesaWebhookGuard, + payhero_service_1.PayheroService, + payhero_webhook_guard_1.PayheroWebhookGuard, + paystack_service_1.PaystackService, + paystack_webhook_guard_1.PaystackWebhookGuard, + subscription_credentials_service_1.SubscriptionCredentialsService, + ], + controllers: [billing_controller_1.BillingController], + exports: [ + billing_service_1.BillingService, + billing_router_1.BillingRouter, + stripe_service_1.StripeService, + stripe_webhook_guard_1.StripeWebhookGuard, + mpesa_service_1.MpesaService, + mpesa_webhook_guard_1.MpesaWebhookGuard, + payhero_service_1.PayheroService, + payhero_webhook_guard_1.PayheroWebhookGuard, + paystack_service_1.PaystackService, + paystack_webhook_guard_1.PaystackWebhookGuard, + subscription_credentials_service_1.SubscriptionCredentialsService, + (0, common_1.forwardRef)(() => wallet_module_1.WalletModule), + ], + }) +], BillingModule); +//# sourceMappingURL=billing.module.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/billing.module.js.map b/apps/api/dist/billing/billing.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..55b4f24cda4560ebc80d79f7b2a427a7a1db6743 --- /dev/null +++ b/apps/api/dist/billing/billing.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"billing.module.js","sourceRoot":"","sources":["../../src/billing/billing.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAoD;AACpD,2CAA8C;AAE9C,2DAAuD;AACvD,gFAA4E;AAC5E,2DAAuD;AACvD,uDAAmD;AACnD,6DAAyD;AACzD,qDAAiD;AACjD,qDAAiD;AACjD,iEAA4D;AAC5D,mDAA+C;AAC/C,+DAA0D;AAC1D,uDAAmD;AACnD,mEAA8D;AAC9D,yDAAqD;AACrD,qEAAgE;AAChE,yFAAoF;AAkC7E,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IA/BzB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,EAAE,qBAAY,EAAE,0CAAmB,EAAE,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,4BAAY,CAAC,CAAC;QAC1F,SAAS,EAAE;YACT,gCAAc;YACd,8BAAa;YACb,8BAAa;YACb,yCAAkB;YAClB,4BAAY;YACZ,uCAAiB;YACjB,gCAAc;YACd,2CAAmB;YACnB,kCAAe;YACf,6CAAoB;YACpB,iEAA8B;SAC/B;QACD,WAAW,EAAE,CAAC,sCAAiB,CAAC;QAChC,OAAO,EAAE;YACP,gCAAc;YACd,8BAAa;YACb,8BAAa;YACb,yCAAkB;YAClB,4BAAY;YACZ,uCAAiB;YACjB,gCAAc;YACd,2CAAmB;YACnB,kCAAe;YACf,6CAAoB;YACpB,iEAA8B;YAC9B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,4BAAY,CAAC;SAC/B;KACF,CAAC;GACW,aAAa,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/billing/billing.router.js b/apps/api/dist/billing/billing.router.js new file mode 100644 index 0000000000000000000000000000000000000000..fa649cfcf232d891690bf2ca16583d224a356235 --- /dev/null +++ b/apps/api/dist/billing/billing.router.js @@ -0,0 +1,303 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BillingRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const trpc_init_1 = require("../trpc/trpc.init"); +const billing_service_1 = require("./billing.service"); +const subscription_credentials_service_1 = require("./subscription-credentials.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +const listBillingPlansSchema = zod_1.z.object({ + currency: zod_1.z.string().min(3).max(3).default("USD"), +}); +const getWorkspaceSubscriptionSchema = zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), +}); +const createStripeCheckoutSchema = zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + planId: zod_1.z.enum(["hobby", "creator", "studio"]), + successUrl: zod_1.z.string().url().optional(), + cancelUrl: zod_1.z.string().url().optional(), +}); +const initiateMpesaCheckoutSchema = zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + planId: zod_1.z.enum(["hobby", "creator", "studio"]), + phoneNumber: zod_1.z.string().min(9).max(20), + accountReference: zod_1.z.string().max(64).optional(), + transactionDesc: zod_1.z.string().max(120).optional(), +}); +const initiatePaystackCheckoutSchema = zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + planId: zod_1.z.enum(["hobby", "creator", "studio"]), + channel: zod_1.z.enum(["card", "mpesa", "airtel"]), + email: zod_1.z.string().email().optional(), + phoneNumber: zod_1.z.string().min(9).max(20).optional(), + successUrl: zod_1.z.string().url().optional(), + cancelUrl: zod_1.z.string().url().optional(), +}).refine((data) => { + if (data.channel === "card") + return Boolean(data.email); + return Boolean(data.phoneNumber); +}, { + message: "Email is required for card payments; phoneNumber is required for mpesa/airtel", + path: ["email"], +}); +const verifyPaystackSchema = zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + reference: zod_1.z.string().min(1).max(128), +}); +const providerEnum = zod_1.z.enum([ + "google_calendar", + "google_mail", + "outlook", + "riverside", + "zoom", + "cloudflare_r2", + "deepgram", + "nvidia_nim", + "deepseek", + "moonshot", +]); +let BillingRouter = class BillingRouter { + billingService; + subscriptionCredentialsService; + constructor(billingService, subscriptionCredentialsService) { + this.billingService = billingService; + this.subscriptionCredentialsService = subscriptionCredentialsService; + } + get router() { + return (0, trpc_init_1.router)({ + listPlans: trpc_init_1.protectedProcedure + .input(listBillingPlansSchema.optional()) + .query(async () => { + return this.billingService.listPlans(); + }), + getCurrentSubscription: trpc_init_1.protectedProcedure + .input(getWorkspaceSubscriptionSchema) + .query(async ({ ctx, input }) => { + return this.billingService.getWorkspaceSubscription(ctx.userId, input.workspaceId); + }), + createStripeCheckout: trpc_init_1.protectedProcedure + .input(createStripeCheckoutSchema) + .mutation(async ({ ctx, input }) => { + return this.billingService.createStripeCheckout(ctx.userId, input); + }), + initiateMpesaCheckout: trpc_init_1.protectedProcedure + .input(initiateMpesaCheckoutSchema) + .mutation(async ({ ctx, input }) => { + return this.billingService.initiateMpesaCheckout(ctx.userId, input); + }), + initiatePaystackCheckout: trpc_init_1.protectedProcedure + .input(initiatePaystackCheckoutSchema) + .mutation(async ({ ctx, input }) => { + return this.billingService.initiatePaystackCheckout(ctx.userId, { + ...input, + email: input.email ?? `${ctx.userId}@placeholder.local`, + }); + }), + verifyPaystackCheckout: trpc_init_1.protectedProcedure + .input(verifyPaystackSchema) + .mutation(async ({ ctx, input }) => { + return this.billingService.verifyPaystackCheckout(ctx.userId, input.workspaceId, input.reference); + }), + cancelSubscription: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + immediately: zod_1.z.boolean().default(false), + reason: zod_1.z.string().max(500).optional(), + })) + .mutation(async ({ ctx, input }) => { + return this.billingService.cancelSubscription(ctx.userId, input.workspaceId, { + immediately: input.immediately, + reason: input.reason, + }); + }), + resumeSubscription: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid() })) + .mutation(async ({ ctx, input }) => { + return this.billingService.resumeSubscription(ctx.userId, input.workspaceId); + }), + changePlan: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + newPlanId: zod_1.z.enum(["hobby", "creator", "studio"]), + prorate: zod_1.z.boolean().default(true), + })) + .mutation(async ({ ctx, input }) => { + return this.billingService.changePlan(ctx.userId, input.workspaceId, input.newPlanId, { prorate: input.prorate }); + }), + retryFailedPayment: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + paymentReference: zod_1.z.string().min(1).max(128), + })) + .mutation(async ({ ctx, input }) => { + return this.billingService.retryFailedPayment(ctx.userId, input.workspaceId, input.paymentReference); + }), + getCollectionStats: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + startDate: zod_1.z.string().datetime().optional(), + endDate: zod_1.z.string().datetime().optional(), + })) + .query(async ({ ctx, input }) => { + return this.billingService.getCollectionStats(ctx.workspaceId, { + startDate: input.startDate, + endDate: input.endDate, + }); + }), + sendDunningNotice: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_READ) + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid() })) + .mutation(async ({ ctx, input }) => { + return this.billingService.sendDunningNotice(input.workspaceId); + }), + refundSubscription: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_REFUND) + .input(zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + amountMinor: zod_1.z.number().int().positive().optional(), + reason: zod_1.z.string().max(500).optional(), + })) + .mutation(async ({ ctx, input }) => { + return this.billingService.refundSubscription(ctx.userId, input.workspaceId, { + amountMinor: input.amountMinor, + reason: input.reason, + }); + }), + getPaymentSettings: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ podcastId: zod_1.z.string().uuid() })) + .query(async ({ ctx, input }) => { + return this.billingService.getPaymentSettings(ctx.userId, input.podcastId); + }), + updatePaymentSettings: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ + podcastId: zod_1.z.string().uuid(), + settings: zod_1.z.object({ + payoutType: zod_1.z.enum(["mpesa", "bank"]), + payoutPhone: zod_1.z.string().min(9), + bankCode: zod_1.z.string().optional(), + bankAccount: zod_1.z.string().optional(), + tillNumber: zod_1.z.string().optional(), + paybillNumber: zod_1.z.string().optional(), + }), + })) + .mutation(async ({ ctx, input }) => { + return this.billingService.updatePaymentSettings(ctx.userId, input.podcastId, input.settings); + }), + createSponsorInvoice: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ + podcastId: zod_1.z.string().uuid(), + sponsorName: zod_1.z.string().min(2), + sponsorPhone: zod_1.z.string().min(9), + amountKes: zod_1.z.number().int().positive(), + })) + .mutation(async ({ ctx, input }) => { + return this.billingService.createSponsorInvoice(ctx.userId, input); + }), + getSponsorInvoice: (0, trpc_init_1.rateLimitedProcedure)("billing:sponsor-invoice:view", 60, 60_000, (_ctx, input) => input.invoiceId) + .input(zod_1.z.object({ invoiceId: zod_1.z.string().uuid() })) + .query(async ({ input }) => { + return this.billingService.getSponsorInvoice(input.invoiceId); + }), + paySponsorInvoice: (0, trpc_init_1.rateLimitedProcedure)("billing:sponsor-invoice:pay", 5, 60_000, (_ctx, input) => input.invoiceId) + .input(zod_1.z.object({ + invoiceId: zod_1.z.string().uuid(), + phone: zod_1.z.string().min(9), + method: zod_1.z.enum(["stk", "link"]), + })) + .mutation(async ({ input }) => { + return this.billingService.paySponsorInvoice(input.invoiceId, input.phone, input.method); + }), + checkIntegrationAccess: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ provider: providerEnum })) + .query(async ({ ctx, input }) => { + return this.subscriptionCredentialsService.checkAccess(ctx.workspaceId, input.provider); + }), + getIntegrationOverview: trpc_init_1.workspaceProcedure + .query(async ({ ctx }) => { + return this.subscriptionCredentialsService.getWorkspaceIntegrationOverview(ctx.workspaceId); + }), + upsertTierAccess: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_PLAN_OVERRIDE) + .input(zod_1.z.object({ + planId: zod_1.z.nativeEnum(shared_types_1.SubscriptionPlanId), + provider: providerEnum, + isAvailable: zod_1.z.boolean(), + credentialSource: zod_1.z.nativeEnum(shared_types_1.CredentialSource), + })) + .mutation(async ({ ctx, input }) => { + return this.subscriptionCredentialsService.upsertTierAccess(ctx.userId, { + planId: input.planId, + provider: input.provider, + isAvailable: input.isAvailable, + credentialSource: input.credentialSource, + }); + }), + listTierAccess: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.BILLING_READ) + .input(zod_1.z.object({ planId: zod_1.z.nativeEnum(shared_types_1.SubscriptionPlanId).optional() })) + .query(async ({ ctx, input }) => { + return this.subscriptionCredentialsService.listTierAccess(ctx.userId, input.planId); + }), + upsertPlatformCredential: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SETTINGS_WRITE) + .input(zod_1.z.object({ + provider: providerEnum, + accessToken: zod_1.z.string().min(1).optional(), + refreshToken: zod_1.z.string().min(1).optional(), + apiKey: zod_1.z.string().min(1).optional(), + apiSecret: zod_1.z.string().min(1).optional(), + endpointUrl: zod_1.z.string().optional(), + scopes: zod_1.z.array(zod_1.z.string().max(200)).max(50).optional(), + metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(), + isActive: zod_1.z.boolean().default(true), + })) + .mutation(async ({ ctx, input }) => { + return this.subscriptionCredentialsService.upsertPlatformCredential(ctx.userId, input); + }), + listPlatformCredentials: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SETTINGS_READ) + .query(async ({ ctx }) => { + return this.subscriptionCredentialsService.listPlatformCredentials(ctx.userId); + }), + deletePlatformCredential: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SETTINGS_WRITE) + .input(zod_1.z.object({ provider: providerEnum })) + .mutation(async ({ ctx, input }) => { + return this.subscriptionCredentialsService.deletePlatformCredential(ctx.userId, input.provider); + }), + getUsage: trpc_init_1.workspaceProcedure + .query(async ({ ctx }) => { + return this.billingService.getUsage(ctx.workspaceId); + }), + getSubscriptionInvoices: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ workspaceId: zod_1.z.string().uuid() })) + .query(async ({ ctx, input }) => { + return this.billingService.getSubscriptionInvoices(input.workspaceId); + }), + downloadInvoice: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ invoiceId: zod_1.z.string().min(1).max(128) })) + .query(async ({ input }) => { + return this.billingService.downloadInvoice(input.invoiceId); + }), + updatePaymentMethod: trpc_init_1.protectedProcedure + .input(zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + paymentMethodId: zod_1.z.string().min(1).max(128), + })) + .mutation(async ({ ctx, input }) => { + return this.billingService.updatePaymentMethod(input.workspaceId, input.paymentMethodId); + }), + }); + } +}; +exports.BillingRouter = BillingRouter; +exports.BillingRouter = BillingRouter = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [billing_service_1.BillingService, + subscription_credentials_service_1.SubscriptionCredentialsService]) +], BillingRouter); +//# sourceMappingURL=billing.router.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/billing.router.js.map b/apps/api/dist/billing/billing.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..da6030101fab8b1f85cd2b9b1e20c2d4be61b256 --- /dev/null +++ b/apps/api/dist/billing/billing.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"billing.router.js","sourceRoot":"","sources":["../../src/billing/billing.router.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/billing.service.js b/apps/api/dist/billing/billing.service.js new file mode 100644 index 0000000000000000000000000000000000000000..c34842d8439dbd0b82e8cceea549574148a3b324 --- /dev/null +++ b/apps/api/dist/billing/billing.service.js @@ -0,0 +1,1938 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var BillingService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BillingService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const shared_types_1 = require("@micprotocol/shared-types"); +const client_1 = require("@prisma/client"); +const prisma_service_1 = require("../prisma/prisma.service"); +const platform_admin_1 = require("../common/platform-admin"); +const mpesa_service_1 = require("./mpesa.service"); +const payhero_service_1 = require("./payhero.service"); +const paystack_service_1 = require("./paystack.service"); +const stripe_service_1 = require("./stripe.service"); +const wallet_service_1 = require("../wallet/wallet.service"); +const redis_idempotency_service_1 = require("../common/redis-idempotency.service"); +const notifications_service_1 = require("../notifications/notifications.service"); +const subscription_features_1 = require("./subscription-features"); +const BillingProvider = { + STRIPE: "stripe", + MPESA: "mpesa", + PAYSTACK: "paystack", +}; +const SubscriptionPlanId = { + HOBBY: "hobby", + CREATOR: "creator", + STUDIO: "studio", +}; +const SubscriptionStatus = { + ACTIVE: "active", + TRIALING: "trialing", + PENDING: "pending", + PAST_DUE: "past_due", + CANCELED: "canceled", + INCOMPLETE: "incomplete", +}; +const SUBSCRIPTION_STATE_TRANSITIONS = { + [SubscriptionStatus.ACTIVE]: [SubscriptionStatus.PAST_DUE, SubscriptionStatus.CANCELED, SubscriptionStatus.ACTIVE], + [SubscriptionStatus.PENDING]: [SubscriptionStatus.ACTIVE, SubscriptionStatus.PAST_DUE, SubscriptionStatus.CANCELED, SubscriptionStatus.INCOMPLETE], + [SubscriptionStatus.PAST_DUE]: [SubscriptionStatus.ACTIVE, SubscriptionStatus.CANCELED, SubscriptionStatus.PAST_DUE], + [SubscriptionStatus.CANCELED]: [SubscriptionStatus.ACTIVE], + [SubscriptionStatus.INCOMPLETE]: [SubscriptionStatus.PENDING, SubscriptionStatus.CANCELED], +}; +let BillingService = BillingService_1 = class BillingService { + prisma; + configService; + mpesaService; + payheroService; + paystackService; + stripeService; + walletService; + idempotencyService; + notificationsService; + logger = new common_1.Logger(BillingService_1.name); + planCatalog = [ + { + id: SubscriptionPlanId.HOBBY, + name: "Hobby", + description: "Free plan for getting started", + priceUsdCents: 0, + priceKes: 0, + interval: "month", + }, + { + id: SubscriptionPlanId.CREATOR, + name: "Creator", + description: "For growing podcast teams", + priceUsdCents: 2900, + priceKes: 3900, + interval: "month", + }, + { + id: SubscriptionPlanId.STUDIO, + name: "Studio", + description: "Advanced suite for scale", + priceUsdCents: 9900, + priceKes: 12900, + interval: "month", + }, + ]; + constructor(prisma, configService, mpesaService, payheroService, paystackService, stripeService, walletService, idempotencyService, notificationsService) { + this.prisma = prisma; + this.configService = configService; + this.mpesaService = mpesaService; + this.payheroService = payheroService; + this.paystackService = paystackService; + this.stripeService = stripeService; + this.walletService = walletService; + this.idempotencyService = idempotencyService; + this.notificationsService = notificationsService; + } + listPlans() { + return this.planCatalog; + } + async getWorkspaceSubscription(userId, workspaceId) { + await this.ensureWorkspaceAccess(workspaceId, userId); + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { + id: true, + billingSettings: true, + }, + }); + if (!workspace) { + throw new common_1.NotFoundException("Workspace not found"); + } + return this.normalizeWorkspaceSubscription(workspace.id, workspace.billingSettings); + } + async createStripeCheckout(userId, input) { + await this.ensureWorkspaceBillingAccess(input.workspaceId, userId); + const plan = this.getPlanById(input.planId); + if (plan.id === SubscriptionPlanId.HOBBY) { + await this.markSubscriptionActive(input.workspaceId, { + planId: SubscriptionPlanId.HOBBY, + provider: BillingProvider.STRIPE, + renewsAt: null, + }); + return { + provider: BillingProvider.STRIPE, + planId: plan.id, + status: SubscriptionStatus.ACTIVE, + checkoutUrl: null, + checkoutSessionId: null, + }; + } + const stripeSecret = this.configService.get("STRIPE_SECRET_KEY"); + if (!stripeSecret) { + throw new common_1.BadRequestException("Stripe is not configured"); + } + const appUrl = this.configService.get("APP_URL") || "http://localhost:3000"; + const successUrl = input.successUrl || `${appUrl}/settings?billing=success`; + const cancelUrl = input.cancelUrl || `${appUrl}/settings?billing=cancelled`; + const body = new URLSearchParams(); + body.set("mode", "subscription"); + body.set("success_url", successUrl); + body.set("cancel_url", cancelUrl); + body.set("client_reference_id", input.workspaceId); + body.set("metadata[workspaceId]", input.workspaceId); + body.set("metadata[planId]", plan.id); + body.set("metadata[userId]", userId); + body.set("metadata[provider]", BillingProvider.STRIPE); + body.set("line_items[0][quantity]", "1"); + body.set("line_items[0][price_data][currency]", "usd"); + body.set("line_items[0][price_data][unit_amount]", String(plan.priceUsdCents)); + body.set("line_items[0][price_data][recurring][interval]", plan.interval); + body.set("line_items[0][price_data][product_data][name]", `MicProtocol ${plan.name}`); + body.set("subscription_data[trial_period_days]", "30"); + const response = await fetch("https://api.stripe.com/v1/checkout/sessions", { + method: "POST", + headers: { + Authorization: `Bearer ${stripeSecret}`, + "Content-Type": "application/x-www-form-urlencoded", + }, + body, + }); + const result = (await response.json()); + if (!response.ok) { + this.logger.error(`Stripe checkout creation failed: ${JSON.stringify(result)}`); + throw new common_1.InternalServerErrorException("Unable to initialize Stripe checkout"); + } + await this.updateWorkspaceBillingSettings(input.workspaceId, { + subscription: { + planId: plan.id, + provider: BillingProvider.STRIPE, + status: SubscriptionStatus.PENDING, + checkoutUrl: result.url || null, + checkoutSessionId: result.id || null, + updatedAt: new Date().toISOString(), + failureReason: null, + }, + }); + return { + provider: BillingProvider.STRIPE, + planId: plan.id, + status: SubscriptionStatus.PENDING, + checkoutUrl: result.url || null, + checkoutSessionId: result.id || null, + }; + } + async initiateMpesaCheckout(userId, input) { + await this.ensureWorkspaceBillingAccess(input.workspaceId, userId); + const plan = this.getPlanById(input.planId); + if (plan.id === SubscriptionPlanId.HOBBY) { + await this.markSubscriptionActive(input.workspaceId, { + planId: SubscriptionPlanId.HOBBY, + provider: BillingProvider.MPESA, + renewsAt: null, + }); + return { + provider: BillingProvider.MPESA, + planId: plan.id, + status: SubscriptionStatus.ACTIVE, + checkoutRequestId: null, + customerMessage: null, + }; + } + try { + const accountReference = input.accountReference || input.workspaceId; + const secret = this.payheroService.generateWebhookSecret(input.workspaceId, plan.id); + const callbackUrl = `${this.configService.get('PAYHERO_CALLBACK_URL') || ''}?secret=${secret}&workspaceId=${input.workspaceId}&planId=${plan.id}`; + const pushResult = await this.payheroService.initiateMpesaStkPush(plan.priceKes, input.phoneNumber, accountReference, callbackUrl); + await this.updateWorkspaceBillingSettings(input.workspaceId, { + subscription: { + planId: plan.id, + provider: BillingProvider.MPESA, + status: SubscriptionStatus.PENDING, + checkoutRequestId: pushResult.CheckoutRequestID || null, + payheroReference: pushResult.reference || null, + accountReference, + updatedAt: new Date().toISOString(), + failureReason: null, + }, + }); + return { + provider: BillingProvider.MPESA, + planId: plan.id, + status: SubscriptionStatus.PENDING, + checkoutRequestId: pushResult.CheckoutRequestID || null, + customerMessage: "Complete payment on your phone", + }; + } + catch (error) { + this.logger.error("PayHero checkout failed", error); + throw new common_1.InternalServerErrorException("PayHero checkout failed"); + } + } + async initiatePaystackCheckout(userId, input) { + await this.ensureWorkspaceBillingAccess(input.workspaceId, userId); + const plan = this.getPlanById(input.planId); + if (plan.id === SubscriptionPlanId.HOBBY) { + await this.markSubscriptionActive(input.workspaceId, { + planId: SubscriptionPlanId.HOBBY, + provider: BillingProvider.PAYSTACK, + renewsAt: null, + }); + return { + provider: BillingProvider.PAYSTACK, + planId: plan.id, + channel: input.channel, + status: SubscriptionStatus.ACTIVE, + reference: null, + authorizationUrl: null, + }; + } + if (!this.paystackService.isConfigured()) { + throw new common_1.BadRequestException("Paystack is not configured"); + } + const { amount, currency } = this.getPlanPricingForCurrency(plan, input.channel); + if (input.channel === "card" && !input.email) { + throw new common_1.BadRequestException("Email is required for card payments"); + } + if ((input.channel === "mpesa" || input.channel === "airtel") && !input.phoneNumber) { + throw new common_1.BadRequestException(`Phone number is required for ${input.channel === "mpesa" ? "M-Pesa" : "Airtel Money"} payments`); + } + const reference = `ws-${input.workspaceId}-${Date.now()}`; + const appUrl = this.configService.get("APP_URL") || "http://localhost:3000"; + const successUrl = input.successUrl || `${appUrl}/settings?billing=success&provider=paystack`; + const cancelUrl = input.cancelUrl || `${appUrl}/settings?billing=cancelled&provider=paystack`; + try { + if (input.channel === "card") { + const init = await this.paystackService.initializeTransaction({ + email: input.email, + amount, + currency, + reference, + callbackUrl: successUrl, + metadata: { + workspaceId: input.workspaceId, + planId: plan.id, + userId, + provider: BillingProvider.PAYSTACK, + }, + channels: ["card"], + }); + await this.updateWorkspaceBillingSettings(input.workspaceId, { + subscription: { + planId: plan.id, + provider: BillingProvider.PAYSTACK, + status: SubscriptionStatus.PENDING, + checkoutUrl: init.authorizationUrl, + payheroReference: reference, + paystackCustomerEmail: input.email, + updatedAt: new Date().toISOString(), + failureReason: null, + }, + }); + return { + provider: BillingProvider.PAYSTACK, + planId: plan.id, + channel: "card", + status: SubscriptionStatus.PENDING, + reference, + authorizationUrl: init.authorizationUrl, + }; + } + const mobileProvider = input.channel === "mpesa" ? "mpesa" : "atl"; + const charge = await this.paystackService.createMobileMoneyCharge({ + email: input.email, + amount, + currency, + reference, + callbackUrl: successUrl, + metadata: { + workspaceId: input.workspaceId, + planId: plan.id, + userId, + provider: BillingProvider.PAYSTACK, + channel: input.channel, + }, + mobileMoney: { + phone: input.phoneNumber, + provider: mobileProvider, + }, + }); + await this.updateWorkspaceBillingSettings(input.workspaceId, { + subscription: { + planId: plan.id, + provider: BillingProvider.PAYSTACK, + status: SubscriptionStatus.PENDING, + checkoutUrl: null, + payheroReference: reference, + paystackCustomerEmail: input.email, + updatedAt: new Date().toISOString(), + failureReason: null, + }, + }); + return { + provider: BillingProvider.PAYSTACK, + planId: plan.id, + channel: input.channel, + status: SubscriptionStatus.PENDING, + reference, + authorizationUrl: null, + displayMessage: charge.displayMessage || + (input.channel === "mpesa" + ? "Check your phone and enter your M-Pesa PIN to complete payment" + : "Check your phone and confirm the Airtel Money payment"), + }; + } + catch (error) { + this.logger.error(`Paystack checkout failed (channel=${input.channel})`, error); + throw new common_1.InternalServerErrorException(`Paystack ${input.channel} checkout failed`); + } + } + getPlanPricingForCurrency(plan, channel) { + if (channel === "mpesa" || channel === "airtel") { + return { amount: plan.priceKes * 100, currency: "KES" }; + } + return { amount: plan.priceUsdCents, currency: "USD" }; + } + async verifyPaystackCheckout(userId, workspaceId, reference) { + await this.ensureWorkspaceBillingAccess(workspaceId, userId); + if (!this.paystackService.isConfigured()) { + throw new common_1.BadRequestException("Paystack is not configured"); + } + const result = await this.paystackService.verifyTransaction(reference); + if (result.status !== "success") { + throw new common_1.BadRequestException(`Paystack transaction ${reference} is not successful (status=${result.status})`); + } + return this.activateFromPaystackReference(reference, result); + } + async handlePaystackWebhook(event) { + const eventType = event?.event; + const eventId = event?.id || undefined; + const data = event?.data; + if (!eventType || !data) { + throw new common_1.BadRequestException("Invalid Paystack event payload"); + } + const dedupKey = eventId + ? `paystack:event:${eventId}` + : `paystack:ref:${data?.reference}:${data?.status}`; + const alreadyProcessed = await this.isEventAlreadyProcessed(dedupKey); + if (alreadyProcessed) { + return { handled: true, idempotent: true }; + } + if (eventType === "charge.success" || eventType === "transaction.success") { + const result = { + status: "success", + amount: data.amount, + currency: data.currency, + reference: data.reference, + channel: data.channel, + paidAt: data.paid_at ?? null, + gatewayResponse: data.gateway_response, + authorizationCode: data.authorization?.authorization_code, + }; + const outcome = await this.activateFromPaystackReference(data.reference, result, data.customer?.email ?? null); + if (outcome?.handled) { + await this.markEventProcessed(dedupKey); + } + return outcome ?? { handled: false, reason: "No matching workspace" }; + } + if (eventType === "charge.failed") { + await this.markEventProcessed(dedupKey); + return { handled: true, reason: "Charge failed — no state change" }; + } + if (eventType === "subscription.not_renewed" || + eventType === "subscription.disable" || + eventType === "subscription.enable" || + eventType === "invoice.payment_failed" || + eventType === "invoice.payment_success") { + const subscriptionCode = data?.subscription?.code || data?.subscription_code; + const customerEmail = data?.customer?.email || data?.email; + let workspace = null; + if (subscriptionCode) { + workspace = await this.findWorkspaceByPaystackSubscriptionCode(subscriptionCode); + } + if (!workspace && customerEmail) { + const all = await this.prisma.workspace.findMany({ + select: { id: true, billingSettings: true }, + }); + workspace = + all.find((w) => { + const s = w.billingSettings || {}; + return (s.subscription?.paystackCustomerEmail === customerEmail && + s.subscription?.provider === BillingProvider.PAYSTACK); + }) || null; + } + if (!workspace) { + return { handled: false, reason: "No matching workspace for subscription event" }; + } + const settings = workspace.billingSettings || {}; + const subscription = settings.subscription; + const currentStatus = subscription?.status || null; + const newStatus = this.mapPaystackSubscriptionEventToStatus(eventType, currentStatus); + if (newStatus) { + this.validateSubscriptionTransition(currentStatus, newStatus); + const patch = { + status: newStatus, + updatedAt: new Date().toISOString(), + }; + if (eventType === "invoice.payment_failed") { + patch.failureReason = "Invoice payment failed"; + } + if (eventType === "subscription.not_renewed") { + patch.failureReason = "Subscription not renewed"; + } + if (newStatus === SubscriptionStatus.ACTIVE) { + patch.failureReason = null; + if (data?.subscription?.next_payment_date) { + patch.renewsAt = data.subscription.next_payment_date; + } + } + if (eventType === "invoice.payment_success" || + eventType === "invoice.payment_failed") { + const invoice = { + reference: data?.invoice?.reference || data?.reference, + amount: data?.invoice?.amount ?? data?.amount, + currency: data?.invoice?.currency ?? data?.currency, + status: eventType === "invoice.payment_success" ? "paid" : "failed", + paidAt: data?.invoice?.paid_at ?? data?.paid_at ?? null, + createdAt: new Date().toISOString(), + }; + const history = Array.isArray(subscription?.invoiceHistory) + ? [...subscription.invoiceHistory, invoice] + : [invoice]; + patch.lastInvoiceAt = invoice.createdAt; + patch.lastInvoiceAmount = invoice.amount; + patch.lastInvoiceCurrency = invoice.currency; + patch.lastInvoiceStatus = invoice.status; + patch.lastInvoiceReference = invoice.reference; + patch.invoiceHistory = history; + this.notifySubscriptionInvoice(workspace.id, invoice); + } + if (newStatus && newStatus !== currentStatus) { + this.notifySubscriptionStatusChange(workspace.id, currentStatus, newStatus, eventType); + } + await this.updateWorkspaceBillingSettings(workspace.id, { + subscription: patch, + }); + } + await this.markEventProcessed(dedupKey); + return { handled: true, status: newStatus || currentStatus }; + } + return { handled: false, reason: `Unhandled Paystack event ${eventType}` }; + } + async activateFromPaystackReference(reference, result, customerEmail) { + const workspace = await this.prisma.workspace.findFirst({ + where: { + billingSettings: { + path: ["subscription", "payheroReference"], + equals: reference, + }, + }, + select: { id: true, billingSettings: true }, + }); + if (!workspace) { + const all = await this.prisma.workspace.findMany({ + select: { id: true, billingSettings: true }, + }); + const match = all.find((w) => { + const settings = w.billingSettings || {}; + return settings.subscription?.payheroReference === reference; + }); + if (!match) { + return { handled: false, reason: "No matching workspace" }; + } + return this.activateFromPaystackMatch(match, result, customerEmail); + } + return this.activateFromPaystackMatch(workspace, result, customerEmail); + } + async activateFromPaystackMatch(workspace, result, customerEmail) { + const settings = workspace.billingSettings || {}; + const pendingPlanId = this.parsePlanId(settings.subscription?.planId); + const currentStatus = settings.subscription?.status || null; + if (!pendingPlanId) { + return { handled: false, reason: "No pending plan on workspace" }; + } + if (result.status !== "success") { + if (currentStatus === SubscriptionStatus.PENDING || + currentStatus === SubscriptionStatus.ACTIVE) { + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.PAST_DUE); + await this.updateWorkspaceBillingSettings(workspace.id, { + subscription: { + status: SubscriptionStatus.PAST_DUE, + updatedAt: new Date().toISOString(), + failureReason: result.gatewayResponse || "Payment failed", + }, + }); + } + return { handled: true, status: SubscriptionStatus.PAST_DUE }; + } + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.ACTIVE); + await this.markSubscriptionActive(workspace.id, { + planId: pendingPlanId, + provider: BillingProvider.PAYSTACK, + renewsAt: null, + }); + const email = customerEmail || + settings.subscription?.paystackCustomerEmail || + null; + if (email && pendingPlanId !== SubscriptionPlanId.HOBBY) { + try { + const sub = await this.createPaystackSubscription(workspace.id, pendingPlanId, email, result.authorizationCode ?? undefined); + await this.updateWorkspaceBillingSettings(workspace.id, { + subscription: { + paystackCustomerCode: sub.customerCode, + paystackSubscriptionCode: sub.subscriptionCode, + paystackEmailToken: sub.emailToken, + paystackPlanCode: sub.planCode, + paystackCustomerEmail: email, + updatedAt: new Date().toISOString(), + }, + }); + } + catch (err) { + this.logger.error(`Failed to create Paystack subscription for workspace ${workspace.id}:`, err); + } + } + return { handled: true, status: SubscriptionStatus.ACTIVE }; + } + async ensurePaystackPlan(planId) { + if (!this.paystackService.isConfigured()) + return null; + const plan = this.getPlanById(planId); + if (plan.id === SubscriptionPlanId.HOBBY) + return null; + const planCode = `micprotocol-${plan.id}-monthly`; + let paystackPlan = await this.paystackService.fetchPlan(planCode); + if (!paystackPlan) { + try { + paystackPlan = await this.paystackService.createPlan({ + name: `MicProtocol ${plan.name}`, + amount: plan.priceUsdCents, + currency: "USD", + interval: "monthly", + description: plan.description, + planCode, + }); + } + catch (err) { + paystackPlan = await this.paystackService.fetchPlan(planCode); + if (!paystackPlan) { + this.logger.error(`Failed to create/fetch Paystack plan for ${planId}:`, err); + return null; + } + } + } + return paystackPlan.plan_code; + } + async createPaystackSubscription(workspaceId, planId, email, authorizationCode) { + const planCode = await this.ensurePaystackPlan(planId); + if (!planCode) { + throw new Error(`Could not resolve Paystack plan for ${planId}`); + } + let customer; + try { + const created = await this.paystackService.createCustomer({ + email, + metadata: { workspaceId }, + }); + customer = { customer_code: created.customer_code }; + } + catch (err) { + this.logger.warn(`Paystack customer creation may have failed for ${email}:`, err); + customer = { customer_code: email }; + } + const subscription = await this.paystackService.createSubscription({ + customerCode: customer.customer_code, + planCode, + authorizationCode, + }); + return { + customerCode: customer.customer_code, + subscriptionCode: subscription.subscription_code, + emailToken: subscription.email_token ?? null, + planCode: subscription.plan.plan_code, + }; + } + validateSubscriptionTransition(currentStatus, newStatus) { + if (currentStatus === newStatus) + return; + const key = currentStatus || "__null__"; + const allowed = SUBSCRIPTION_STATE_TRANSITIONS[key]; + if (!allowed) { + if (newStatus === SubscriptionStatus.PENDING || newStatus === SubscriptionStatus.ACTIVE) + return; + throw new common_1.BadRequestException(`Invalid subscription transition from null to "${newStatus}". Initial status must be PENDING or ACTIVE.`); + } + if (!allowed.includes(newStatus)) { + throw new common_1.BadRequestException(`Invalid subscription status transition from "${currentStatus}" to "${newStatus}". ` + + `Allowed: ${allowed.join(", ")}`); + } + } + async isEventAlreadyProcessed(eventId) { + const result = await this.idempotencyService.checkAndMark(`webhook:${eventId}`); + return !result.isNew; + } + async markEventProcessed(eventId) { + await this.idempotencyService.storeResult(`webhook:${eventId}`, { processed: true, timestamp: new Date().toISOString() }); + } + async cancelSubscription(userId, workspaceId, options = {}) { + await this.ensureWorkspaceBillingAccess(workspaceId, userId); + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + if (!workspace) + throw new common_1.NotFoundException("Workspace not found"); + const settings = workspace.billingSettings || {}; + const subscription = settings.subscription; + const currentStatus = subscription?.status || null; + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.CANCELED); + const provider = subscription?.provider; + const stripeSubId = subscription?.stripeSubscriptionId; + const paystackSubCode = subscription?.paystackSubscriptionCode; + const paystackEmailToken = subscription?.paystackEmailToken; + const immediately = Boolean(options.immediately); + try { + if (provider === BillingProvider.STRIPE && stripeSubId) { + if (this.stripeService.isConfigured()) { + if (immediately) { + await this.stripeService.cancelSubscriptionImmediately(stripeSubId); + } + else { + await this.stripeService.cancelSubscriptionAtPeriodEnd(stripeSubId); + } + } + else { + this.logger.warn(`Stripe not configured; cancelling workspace ${workspaceId} locally only`); + } + } + else if (provider === BillingProvider.PAYSTACK && + paystackSubCode && + paystackEmailToken) { + if (this.paystackService.isConfigured()) { + await this.paystackService.disableSubscription(paystackSubCode, paystackEmailToken); + if (immediately) { + const lastRef = subscription?.payheroReference; + if (lastRef) { + try { + await this.paystackService.refundTransaction(lastRef); + } + catch (refundErr) { + this.logger.warn(`Paystack immediate-cancel refund failed for ${lastRef}:`, refundErr); + } + } + } + } + else { + this.logger.warn(`Paystack not configured; cancelling workspace ${workspaceId} locally only`); + } + } + } + catch (providerErr) { + this.logger.error(`Provider-side cancellation failed for workspace ${workspaceId}:`, providerErr); + throw new common_1.ConflictException("The payment provider rejected the cancellation. No changes were made — please retry."); + } + const newStatus = immediately + ? SubscriptionStatus.CANCELED + : SubscriptionStatus.CANCELED; + await this.updateWorkspaceBillingSettings(workspaceId, { + subscription: { + status: newStatus, + updatedAt: new Date().toISOString(), + failureReason: options.reason || null, + ...(immediately ? { renewsAt: null } : {}), + }, + }); + return { + workspaceId, + status: newStatus, + immediately, + cancelAtPeriodEnd: !immediately, + }; + } + async resumeSubscription(userId, workspaceId) { + await this.ensureWorkspaceBillingAccess(workspaceId, userId); + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + if (!workspace) + throw new common_1.NotFoundException("Workspace not found"); + const settings = workspace.billingSettings || {}; + const subscription = settings.subscription; + const currentStatus = subscription?.status || null; + if (currentStatus !== SubscriptionStatus.CANCELED) { + throw new common_1.BadRequestException("Only canceled subscriptions can be resumed. " + + (currentStatus === SubscriptionStatus.PENDING + ? "You have a pending checkout — complete the payment instead." + : "Start a new checkout if you want to subscribe.")); + } + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.ACTIVE); + const provider = subscription?.provider; + const stripeSubId = subscription?.stripeSubscriptionId; + const paystackSubCode = subscription?.paystackSubscriptionCode; + const paystackEmailToken = subscription?.paystackEmailToken; + if (provider === BillingProvider.STRIPE) { + if (!stripeSubId) { + throw new common_1.BadRequestException("No Stripe subscription reference found — start a new checkout instead."); + } + } + else if (provider === BillingProvider.PAYSTACK) { + if (!paystackSubCode || !paystackEmailToken) { + throw new common_1.BadRequestException("No Paystack subscription reference found — start a new checkout instead."); + } + } + else if (provider === BillingProvider.MPESA) { + throw new common_1.BadRequestException("M-Pesa payments are one-time and not recurring. Start a new checkout to renew."); + } + else { + throw new common_1.BadRequestException("No subscription to resume — start a new checkout to subscribe."); + } + try { + if (provider === BillingProvider.STRIPE && stripeSubId) { + if (this.stripeService.isConfigured()) { + await this.stripeService.resumeSubscription(stripeSubId); + } + else { + this.logger.warn(`Stripe not configured; resuming workspace ${workspaceId} locally only`); + } + } + else if (provider === BillingProvider.PAYSTACK && + paystackSubCode && + paystackEmailToken) { + if (this.paystackService.isConfigured()) { + await this.paystackService.enableSubscription(paystackSubCode, paystackEmailToken); + } + else { + this.logger.warn(`Paystack not configured; resuming workspace ${workspaceId} locally only`); + } + } + } + catch (providerErr) { + this.logger.error(`Provider-side resume failed for workspace ${workspaceId}:`, providerErr); + throw new common_1.ConflictException("Resume recorded locally but the provider rejected the request. Please retry."); + } + await this.updateWorkspaceBillingSettings(workspaceId, { + subscription: { + status: SubscriptionStatus.ACTIVE, + updatedAt: new Date().toISOString(), + failureReason: null, + }, + }); + return { workspaceId, status: SubscriptionStatus.ACTIVE }; + } + async changePlan(userId, workspaceId, newPlanId, options = {}) { + await this.ensureWorkspaceBillingAccess(workspaceId, userId); + const newPlan = this.getPlanById(newPlanId); + if (newPlan.id === SubscriptionPlanId.HOBBY) { + return this.cancelSubscription(userId, workspaceId, { immediately: false }); + } + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + if (!workspace) + throw new common_1.NotFoundException("Workspace not found"); + const settings = workspace.billingSettings || {}; + const subscription = settings.subscription; + const currentPlanId = this.parsePlanId(subscription?.planId); + if (currentPlanId === newPlan.id) { + return { workspaceId, planId: newPlan.id, status: subscription?.status, unchanged: true }; + } + const provider = subscription?.provider; + const stripeSubId = subscription?.stripeSubscriptionId; + if (provider === BillingProvider.STRIPE && stripeSubId) { + if (!this.stripeService.isConfigured()) { + throw new common_1.BadRequestException("Stripe is not configured"); + } + const newPriceId = this.configService.get(this.getStripePriceIdConfigKey(newPlan.id)); + if (!newPriceId) { + throw new common_1.BadRequestException(`No Stripe price ID configured for plan ${newPlan.id}`); + } + await this.stripeService.updateSubscriptionPrice(stripeSubId, newPriceId); + } + else if (provider === BillingProvider.PAYSTACK) { + throw new common_1.BadRequestException("Paystack plan changes require a fresh checkout. Use initiatePaystackCheckout."); + } + else if (provider === BillingProvider.MPESA) { + throw new common_1.BadRequestException("M-Pesa subscriptions are one-time payments. To change plans, cancel and start a new checkout."); + } + else if (provider === BillingProvider.STRIPE) { + throw new common_1.BadRequestException("No active Stripe subscription found. Complete a checkout before changing plans."); + } + else { + throw new common_1.BadRequestException("No active subscription found. Start a checkout to change plans."); + } + await this.updateWorkspaceBillingSettings(workspaceId, { + subscription: { + planId: newPlan.id, + updatedAt: new Date().toISOString(), + }, + }); + return { + workspaceId, + planId: newPlan.id, + previousPlanId: currentPlanId, + prorated: options.prorate !== false && provider === BillingProvider.STRIPE, + }; + } + async refundSubscription(adminUserId, workspaceId, options = {}) { + await (0, platform_admin_1.assertPlatformAdmin)(this.prisma, adminUserId); + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + if (!workspace) + throw new common_1.NotFoundException("Workspace not found"); + const settings = workspace.billingSettings || {}; + const subscription = settings.subscription; + const provider = subscription?.provider; + if (!subscription) { + throw new common_1.BadRequestException("Workspace has no subscription to refund"); + } + let refundResult; + if (provider === BillingProvider.STRIPE) { + const chargeId = subscription?.stripeChargeId; + const paymentIntentId = subscription?.stripePaymentIntentId; + const target = chargeId || paymentIntentId; + if (!target) { + throw new common_1.BadRequestException("No Stripe charge or payment intent recorded for this subscription"); + } + if (!this.stripeService.isConfigured()) { + throw new common_1.BadRequestException("Stripe is not configured"); + } + const refund = await this.stripeService.refundCharge(target, options.amountMinor, "requested_by_customer"); + refundResult = { + id: refund.id, + amount: refund.amount, + status: refund.status ?? "pending", + }; + } + else if (provider === BillingProvider.PAYSTACK) { + const reference = subscription?.payheroReference; + if (!reference) { + throw new common_1.BadRequestException("No Paystack reference recorded for this subscription"); + } + if (!this.paystackService.isConfigured()) { + throw new common_1.BadRequestException("Paystack is not configured"); + } + const refund = await this.paystackService.refundTransaction(reference, options.amountMinor); + refundResult = { + id: refund.reference, + amount: refund.amount, + status: refund.status, + }; + } + else if (provider === BillingProvider.MPESA) { + throw new common_1.BadRequestException("M-Pesa refunds are issued out-of-band via PayHero. Contact support."); + } + else { + throw new common_1.BadRequestException(`Unsupported billing provider: ${provider}`); + } + await this.updateWorkspaceBillingSettings(workspaceId, { + subscription: { + status: SubscriptionStatus.PAST_DUE, + updatedAt: new Date().toISOString(), + failureReason: options.reason || `Refund issued: ${refundResult.id}`, + }, + }); + return { + workspaceId, + provider, + refundId: refundResult.id, + amountMinor: refundResult.amount, + status: refundResult.status, + }; + } + getStripePriceIdConfigKey(planId) { + return `STRIPE_PRICE_${planId.toUpperCase()}_MONTHLY`; + } + async handleStripeWebhook(event) { + const eventType = event?.type; + const dataObject = event?.data?.object; + const eventId = event?.id; + if (!eventType || !dataObject) { + throw new common_1.BadRequestException("Invalid Stripe event payload"); + } + if (eventId) { + const alreadyProcessed = await this.isEventAlreadyProcessed(eventId); + if (alreadyProcessed) { + return { handled: true, idempotent: true }; + } + } + if (eventType === "checkout.session.completed" || eventType === "invoice.payment_succeeded") { + if (eventType === "checkout.session.completed" && + dataObject.payment_status === "unpaid") { + if (eventId) + await this.markEventProcessed(eventId); + return { + handled: true, + reason: "Checkout session completed but payment not settled", + }; + } + if (eventType === "checkout.session.completed" && + dataObject.metadata?.type === "ticket_order") { + const orderId = dataObject.metadata?.orderId || dataObject.client_reference_id; + if (!orderId) { + if (eventId) + await this.markEventProcessed(eventId); + return { handled: false, reason: "Missing order reference" }; + } + const order = await this.prisma.order.findUnique({ + where: { id: orderId }, + include: { event: { select: { workspaceId: true, currency: true } } }, + }); + if (!order) { + if (eventId) + await this.markEventProcessed(eventId); + return { handled: false, reason: "Order not found" }; + } + if (order.status !== client_1.OrderStatus.paid) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: order.event.workspaceId }, + select: { subscriptionPlanId: true }, + }); + const totalCents = Math.round(Number(order.total) * 100); + await this.prisma.order.update({ + where: { id: orderId }, + data: { status: client_1.OrderStatus.paid }, + }); + await this.walletService.creditWalletForOrder(order.event.workspaceId, orderId, totalCents, order.event.currency, workspace?.subscriptionPlanId ?? "studio"); + } + if (eventId) + await this.markEventProcessed(eventId); + return { handled: true }; + } + const workspaceId = dataObject.metadata?.workspaceId || dataObject.client_reference_id; + const planId = this.parsePlanId(dataObject.metadata?.planId); + if (!workspaceId || !planId) { + return { handled: false, reason: "Missing workspace metadata" }; + } + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + if (!workspace) { + return { handled: false, reason: "Workspace not found" }; + } + const currentSettings = (workspace.billingSettings || {}); + const currentStatus = currentSettings.subscription?.status || null; + if (currentStatus === SubscriptionStatus.ACTIVE && planId === SubscriptionPlanId.HOBBY) { + return { handled: false, reason: "Cannot downgrade to hobby via webhook" }; + } + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.ACTIVE); + const stripeSubscriptionId = typeof dataObject.subscription === "string" + ? dataObject.subscription + : currentSettings.subscription?.stripeSubscriptionId || null; + const stripeCustomerId = typeof dataObject.customer === "string" + ? dataObject.customer + : currentSettings.subscription?.stripeCustomerId || null; + const stripePaymentIntentId = typeof dataObject.payment_intent === "string" + ? dataObject.payment_intent + : null; + const stripeChargeId = (typeof dataObject.latest_charge === "string" + ? dataObject.latest_charge + : null) || null; + await this.markSubscriptionActive(workspaceId, { + planId, + provider: BillingProvider.STRIPE, + renewsAt: this.isoOrNull(dataObject.current_period_end), + }); + await this.updateWorkspaceBillingSettings(workspaceId, { + subscription: { + stripeSubscriptionId, + stripeCustomerId, + stripePaymentIntentId, + stripeChargeId, + }, + }); + if (eventId) + await this.markEventProcessed(eventId); + return { handled: true }; + } + if (eventType === "customer.subscription.updated") { + const workspaceId = dataObject.metadata?.workspaceId; + const status = dataObject.status; + if (!workspaceId) { + return { handled: false, reason: "Missing workspace metadata" }; + } + const mappedStatus = this.mapStripeStatusToInternal(status); + if (mappedStatus) { + await this.updateWorkspaceBillingSettings(workspaceId, { + subscription: { + status: mappedStatus, + updatedAt: new Date().toISOString(), + renewsAt: this.isoOrNull(dataObject.current_period_end), + failureReason: dataObject.pause_collection ? "payment_paused" : null, + }, + }); + } + if (eventId) + await this.markEventProcessed(eventId); + return { handled: true }; + } + if (eventType === "customer.subscription.deleted") { + const workspaceId = dataObject.metadata?.workspaceId; + if (!workspaceId) { + return { handled: false, reason: "Missing workspace metadata" }; + } + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + const currentStatus = workspace + ? (workspace.billingSettings?.subscription?.status || null) + : null; + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.CANCELED); + await this.updateWorkspaceBillingSettings(workspaceId, { + subscription: { + status: SubscriptionStatus.CANCELED, + updatedAt: new Date().toISOString(), + checkoutUrl: null, + checkoutSessionId: null, + }, + }); + if (eventId) + await this.markEventProcessed(eventId); + return { handled: true }; + } + if (eventType === "charge.refunded" || eventType === "charge.refund.updated") { + const workspaceId = dataObject.metadata?.workspaceId || dataObject.client_reference_id; + if (workspaceId) { + this.logger.warn(`Refund processed for workspace ${workspaceId}: ${dataObject.id}`); + } + if (eventId) + await this.markEventProcessed(eventId); + return { handled: true }; + } + return { handled: false, reason: `Unhandled event ${eventType}` }; + } + mapStripeStatusToInternal(stripeStatus) { + const map = { + active: SubscriptionStatus.ACTIVE, + past_due: SubscriptionStatus.PAST_DUE, + canceled: SubscriptionStatus.CANCELED, + incomplete: SubscriptionStatus.INCOMPLETE, + incomplete_expired: SubscriptionStatus.CANCELED, + trialing: SubscriptionStatus.ACTIVE, + unpaid: SubscriptionStatus.PAST_DUE, + paused: SubscriptionStatus.PAST_DUE, + }; + return map[stripeStatus] || null; + } + mapPaystackSubscriptionEventToStatus(eventType, currentStatus) { + switch (eventType) { + case "subscription.enable": + return SubscriptionStatus.ACTIVE; + case "subscription.disable": + return SubscriptionStatus.CANCELED; + case "subscription.not_renewed": + return SubscriptionStatus.PAST_DUE; + case "invoice.payment_failed": + return SubscriptionStatus.PAST_DUE; + case "invoice.payment_success": + return SubscriptionStatus.ACTIVE; + default: + return null; + } + } + async getWorkspaceOwnerUserIds(workspaceId) { + const members = await this.prisma.workspaceMember.findMany({ + where: { + workspaceId, + role: shared_types_1.WorkspaceRole.OWNER, + }, + select: { userId: true }, + }); + return members.map((m) => m.userId); + } + async notifySubscriptionInvoice(workspaceId, invoice) { + const ownerIds = await this.getWorkspaceOwnerUserIds(workspaceId); + if (ownerIds.length === 0) + return; + const statusLabel = invoice.status === "paid" ? "paid" : "failed"; + const amountStr = invoice.amount != null && invoice.currency + ? `${invoice.currency} ${(invoice.amount / 100).toFixed(2)}` + : "unknown amount"; + await Promise.all(ownerIds.map((userId) => this.notificationsService + .createNotification({ + userId, + type: shared_types_1.NotificationType.BILLING, + title: `Subscription invoice ${statusLabel}`, + message: `Your subscription invoice (${invoice.reference ?? "N/A"}) for ${amountStr} has ${statusLabel}.`, + link: "/settings/billing", + metadata: { + workspaceId, + invoiceReference: invoice.reference, + invoiceAmount: invoice.amount, + invoiceCurrency: invoice.currency, + invoiceStatus: invoice.status, + paidAt: invoice.paidAt, + createdAt: invoice.createdAt, + }, + }) + .catch((err) => this.logger.warn(`Failed to create invoice notification for user ${userId}:`, err)))); + } + async notifySubscriptionStatusChange(workspaceId, from, to, trigger) { + const ownerIds = await this.getWorkspaceOwnerUserIds(workspaceId); + if (ownerIds.length === 0) + return; + const statusLabels = { + active: "Active", + trialing: "Trialing", + pending: "Pending", + past_due: "Past Due", + canceled: "Canceled", + incomplete: "Incomplete", + }; + await Promise.all(ownerIds.map((userId) => this.notificationsService + .createNotification({ + userId, + type: shared_types_1.NotificationType.BILLING, + title: "Subscription status changed", + message: `Your workspace subscription changed from ${from ?? "none"} to ${statusLabels[to] ?? to} (trigger: ${trigger}).`, + link: "/settings/billing", + metadata: { + workspaceId, + previousStatus: from, + newStatus: to, + trigger, + }, + }) + .catch((err) => this.logger.warn(`Failed to create status change notification for user ${userId}:`, err)))); + } + async handleMpesaWebhook(payload) { + const callback = payload?.Body?.stkCallback; + if (!callback?.CheckoutRequestID) { + throw new common_1.BadRequestException("Invalid M-Pesa callback payload"); + } + const checkoutRequestId = callback.CheckoutRequestID; + const dedupKey = `mpesa:${checkoutRequestId}:${callback.ResultCode}`; + const alreadyProcessed = await this.isEventAlreadyProcessed(dedupKey); + if (alreadyProcessed) { + return { handled: true, idempotent: true }; + } + const workspace = await this.findWorkspaceByMpesaCheckoutId(checkoutRequestId); + if (!workspace) { + return { handled: false, reason: "No matching workspace for CheckoutRequestID" }; + } + const settings = (workspace.billingSettings || {}); + const pendingPlanId = this.parsePlanId(settings.subscription?.planId); + const currentStatus = settings.subscription?.status || null; + if (!pendingPlanId) { + return { handled: false, reason: "No pending plan found for workspace" }; + } + const resultCode = Number(callback.ResultCode || 1); + if (resultCode === 0) { + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.ACTIVE); + await this.markSubscriptionActive(workspace.id, { + planId: pendingPlanId, + provider: BillingProvider.MPESA, + renewsAt: null, + }); + await this.markEventProcessed(dedupKey); + return { handled: true, status: SubscriptionStatus.ACTIVE }; + } + if (currentStatus === SubscriptionStatus.PENDING || currentStatus === SubscriptionStatus.ACTIVE) { + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.PAST_DUE); + await this.updateWorkspaceBillingSettings(workspace.id, { + subscription: { + status: SubscriptionStatus.PAST_DUE, + updatedAt: new Date().toISOString(), + failureReason: callback.ResultDesc || "Payment failed", + }, + }); + } + this.markEventProcessed(dedupKey); + return { handled: true, status: SubscriptionStatus.PAST_DUE }; + } + async handlePayheroWebhook(payload, query) { + this.logger.debug(`Received PayHero webhook payload: ${JSON.stringify(payload)}`); + if (query.workspaceId && query.planId) { + const workspaceId = query.workspaceId; + const planId = query.planId; + const responseObj = payload?.response; + if (!responseObj?.CheckoutRequestID) { + throw new common_1.BadRequestException("Invalid PayHero subscription callback payload"); + } + const checkoutRequestId = responseObj.CheckoutRequestID; + const dedupKey = `payhero:sub:${checkoutRequestId}:${responseObj.ResultCode}`; + const alreadyProcessed = await this.isEventAlreadyProcessed(dedupKey); + if (alreadyProcessed) { + return { handled: true, idempotent: true }; + } + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + }); + if (!workspace) { + return { handled: false, reason: "No matching workspace found" }; + } + const settings = (workspace.billingSettings || {}); + const currentStatus = settings.subscription?.status || null; + const resultCode = Number(responseObj.ResultCode || 1); + if (resultCode === 0) { + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.ACTIVE); + await this.markSubscriptionActive(workspace.id, { + planId, + provider: BillingProvider.MPESA, + renewsAt: null, + }); + await this.markEventProcessed(dedupKey); + return { handled: true, status: SubscriptionStatus.ACTIVE }; + } + if (currentStatus === SubscriptionStatus.PENDING || currentStatus === SubscriptionStatus.ACTIVE) { + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.PAST_DUE); + await this.updateWorkspaceBillingSettings(workspace.id, { + subscription: { + status: SubscriptionStatus.PAST_DUE, + updatedAt: new Date().toISOString(), + failureReason: responseObj.ResultDesc || "Payment failed", + }, + }); + } + await this.markEventProcessed(dedupKey); + return { handled: true, status: SubscriptionStatus.PAST_DUE }; + } + if (query.podcastId && query.invoiceId) { + const podcastId = query.podcastId; + const invoiceId = query.invoiceId; + const responseObj = payload?.response; + if (!responseObj?.CheckoutRequestID) { + throw new common_1.BadRequestException("Invalid PayHero sponsor callback payload"); + } + const checkoutRequestId = responseObj.CheckoutRequestID; + const dedupKey = `payhero:invoice:${checkoutRequestId}:${responseObj.ResultCode}`; + const alreadyProcessed = await this.isEventAlreadyProcessed(dedupKey); + if (alreadyProcessed) { + return { handled: true, idempotent: true }; + } + const invoice = await this.prisma.sponsorInvoice.findUnique({ + where: { id: invoiceId }, + include: { podcast: true }, + }); + if (!invoice) { + return { handled: false, reason: "Invoice not found" }; + } + const resultCode = Number(responseObj.ResultCode || 1); + if (resultCode === 0) { + await this.prisma.sponsorInvoice.update({ + where: { id: invoiceId }, + data: { + status: "paid", + payheroReference: responseObj.MpesaReceiptNumber || responseObj.CheckoutRequestID, + }, + }); + const paymentSettings = (invoice.podcast.paymentSettings || {}); + const payoutPhone = paymentSettings.payoutPhone || paymentSettings.phoneNumber; + if (payoutPhone) { + try { + const payoutResponse = await this.payheroService.triggerWithdrawal(invoice.payoutAmountKes, payoutPhone, invoice.id); + await this.prisma.sponsorInvoice.update({ + where: { id: invoiceId }, + data: { + status: "disbursed", + payoutReference: payoutResponse.merchant_reference || payoutResponse.checkout_request_id, + }, + }); + } + catch (err) { + this.logger.error(`Automated withdrawal failed for invoice ${invoiceId}`, err); + } + } + else { + this.logger.warn(`No payout phone configured for podcast ${podcastId}. Skipping payout.`); + } + await this.markEventProcessed(dedupKey); + return { handled: true, status: "paid" }; + } + else { + await this.prisma.sponsorInvoice.update({ + where: { id: invoiceId }, + data: { status: "failed" }, + }); + await this.markEventProcessed(dedupKey); + return { handled: true, status: "failed" }; + } + } + throw new common_1.BadRequestException("Unknown webhook context"); + } + parsePlanId(value) { + if (!value) + return null; + if (value === SubscriptionPlanId.HOBBY) + return SubscriptionPlanId.HOBBY; + if (value === SubscriptionPlanId.CREATOR) + return SubscriptionPlanId.CREATOR; + if (value === SubscriptionPlanId.STUDIO) + return SubscriptionPlanId.STUDIO; + return null; + } + async findWorkspaceByMpesaCheckoutId(checkoutRequestId) { + try { + const workspace = await this.prisma.workspace.findFirst({ + where: { + billingSettings: { + path: ["subscription", "checkoutRequestId"], + equals: checkoutRequestId, + }, + }, + select: { + id: true, + billingSettings: true, + }, + }); + if (workspace) + return workspace; + } + catch (error) { + this.logger.warn("JSON path query failed, falling back to scan", error); + } + const workspaces = await this.prisma.workspace.findMany({ + select: { + id: true, + billingSettings: true, + }, + }); + return workspaces.find((workspace) => { + const settings = (workspace.billingSettings || {}); + return settings.subscription?.checkoutRequestId === checkoutRequestId; + }) || null; + } + async findWorkspaceByPaystackSubscriptionCode(subscriptionCode) { + try { + const workspace = await this.prisma.workspace.findFirst({ + where: { + billingSettings: { + path: ["subscription", "paystackSubscriptionCode"], + equals: subscriptionCode, + }, + }, + select: { + id: true, + billingSettings: true, + }, + }); + if (workspace) + return workspace; + } + catch (error) { + this.logger.warn("JSON path query failed, falling back to scan", error); + } + const workspaces = await this.prisma.workspace.findMany({ + select: { + id: true, + billingSettings: true, + }, + }); + return workspaces.find((workspace) => { + const settings = (workspace.billingSettings || {}); + return settings.subscription?.paystackSubscriptionCode === subscriptionCode; + }) || null; + } + async markSubscriptionActive(workspaceId, params) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + const currentSettings = (workspace?.billingSettings || {}); + const currentStatus = currentSettings.subscription?.status || null; + this.validateSubscriptionTransition(currentStatus, SubscriptionStatus.ACTIVE); + const previousPlan = currentSettings.subscription?.planId; + if (previousPlan && previousPlan !== params.planId) { + this.logger.log(`Plan change for workspace ${workspaceId}: ${previousPlan} -> ${params.planId}`); + } + await this.updateWorkspaceBillingSettings(workspaceId, { + subscription: { + planId: params.planId, + provider: params.provider, + status: SubscriptionStatus.ACTIVE, + checkoutUrl: null, + checkoutSessionId: null, + failureReason: null, + updatedAt: new Date().toISOString(), + renewsAt: params.renewsAt, + }, + }); + } + normalizeWorkspaceSubscription(workspaceId, billingSettings) { + const planId = this.parsePlanId(billingSettings?.subscription?.planId) || SubscriptionPlanId.HOBBY; + const provider = billingSettings?.subscription?.provider || BillingProvider.STRIPE; + const status = billingSettings?.subscription?.status || SubscriptionStatus.ACTIVE; + return { + workspaceId, + planId, + provider, + status, + updatedAt: billingSettings?.subscription?.updatedAt || new Date().toISOString(), + renewsAt: billingSettings?.subscription?.renewsAt || null, + checkoutUrl: billingSettings?.subscription?.checkoutUrl || null, + }; + } + getPlanById(planId) { + const plan = this.planCatalog.find((item) => item.id === planId); + if (!plan) { + throw new common_1.BadRequestException("Invalid plan"); + } + return plan; + } + async updateWorkspaceBillingSettings(workspaceId, patch) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { + id: true, + billingSettings: true, + }, + }); + if (!workspace) { + throw new common_1.NotFoundException("Workspace not found"); + } + const current = (workspace.billingSettings || {}); + const next = { + ...current, + subscription: { + ...(current.subscription || {}), + ...(patch.subscription || {}), + }, + }; + await this.prisma.workspace.update({ + where: { id: workspaceId }, + data: { + billingSettings: next, + }, + }); + if (next.subscription) { + const syncData = {}; + if (next.subscription.planId) { + syncData.subscriptionPlanId = next.subscription.planId; + } + if (next.subscription.status) { + syncData.subscriptionStatus = next.subscription.status; + } + if (next.subscription.provider) { + syncData.billingProvider = next.subscription.provider; + } + if (Object.keys(syncData).length > 0) { + await this.prisma.workspace.update({ + where: { id: workspaceId }, + data: syncData, + }); + } + } + return next; + } + async ensureWorkspaceAccess(workspaceId, userId) { + const membership = await this.prisma.workspaceMember.findUnique({ + where: { + userId_workspaceId: { + userId, + workspaceId, + }, + }, + }); + if (!membership) { + throw new common_1.ForbiddenException("You do not have access to this workspace"); + } + return membership; + } + async ensureWorkspaceBillingAccess(workspaceId, userId) { + const membership = await this.ensureWorkspaceAccess(workspaceId, userId); + const allowedRoles = [shared_types_1.WorkspaceRole.OWNER, shared_types_1.WorkspaceRole.ADMIN]; + if (!allowedRoles.includes(membership.role)) { + throw new common_1.ForbiddenException("Only workspace owners/admins can manage billing"); + } + return membership; + } + isoOrNull(value) { + if (typeof value !== "number") { + return null; + } + return new Date(value * 1000).toISOString(); + } + normalizeKenyanPhoneNumber(raw) { + return this.mpesaService.normalizePhoneNumber(raw); + } + async getUsage(workspaceId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { subscriptionPlanId: true, subscriptionStatus: true }, + }); + const planId = workspace?.subscriptionPlanId || SubscriptionPlanId.HOBBY; + const status = workspace?.subscriptionStatus || SubscriptionStatus.ACTIVE; + const effectivePlan = status === SubscriptionStatus.ACTIVE || status === SubscriptionStatus.TRIALING + ? planId + : SubscriptionPlanId.HOBBY; + const startOfMonth = new Date(); + startOfMonth.setDate(1); + startOfMonth.setHours(0, 0, 0, 0); + const podcastIds = await this.prisma.podcast + .findMany({ where: { workspaceId }, select: { id: true } }) + .then((rows) => rows.map((r) => r.id)); + const [guestsThisMonth, episodesThisMonth, clipsThisMonth, exportMinutes, apiCalls] = await Promise.all([ + this.prisma.guest.count({ + where: { podcastId: { in: podcastIds }, createdAt: { gte: startOfMonth } }, + }), + this.prisma.episode.count({ + where: { podcast: { workspaceId }, createdAt: { gte: startOfMonth } }, + }), + this.prisma.clip.count({ + where: { episode: { podcast: { workspaceId } }, createdAt: { gte: startOfMonth } }, + }), + this.prisma.exportJob + .aggregate({ + where: { + clip: { episode: { podcast: { workspaceId } } }, + status: "completed", + updatedAt: { gte: startOfMonth }, + }, + _count: true, + }) + .then((r) => r._count), + Promise.resolve(0), + ]); + const usage = { + guestsPerMonth: { + current: guestsThisMonth, + limit: (0, subscription_features_1.getPlanLimit)(effectivePlan, "maxGuestsPerMonth"), + }, + episodesPerMonth: { + current: episodesThisMonth, + limit: (0, subscription_features_1.getPlanLimit)(effectivePlan, "maxEpisodesPerMonth"), + }, + clipsPerMonth: { + current: clipsThisMonth, + limit: (0, subscription_features_1.getPlanLimit)(effectivePlan, "maxClipsPerMonth"), + }, + exportMinutesPerMonth: { + current: exportMinutes, + limit: (0, subscription_features_1.getPlanLimit)(effectivePlan, "maxExportMinutesPerMonth"), + }, + apiCallsPerMonth: { + current: apiCalls, + limit: (0, subscription_features_1.getPlanLimit)(effectivePlan, "maxApiCallsPerMonth"), + }, + }; + return { planId, status, usage }; + } + async getPaymentSettings(userId, podcastId) { + const podcast = await this.prisma.podcast.findUnique({ + where: { id: podcastId }, + include: { workspace: { include: { members: true } } }, + }); + if (!podcast) { + throw new common_1.NotFoundException("Podcast not found"); + } + const isMember = podcast.workspace.members.some((m) => m.userId === userId); + if (!isMember) { + throw new common_1.ForbiddenException("You do not have access to this podcast"); + } + return (podcast.paymentSettings || {}); + } + async updatePaymentSettings(userId, podcastId, settings) { + const podcast = await this.prisma.podcast.findUnique({ + where: { id: podcastId }, + include: { workspace: { include: { members: true } } }, + }); + if (!podcast) { + throw new common_1.NotFoundException("Podcast not found"); + } + const isMember = podcast.workspace.members.some((m) => m.userId === userId); + if (!isMember) { + throw new common_1.ForbiddenException("You do not have access to this podcast"); + } + const updated = await this.prisma.podcast.update({ + where: { id: podcastId }, + data: { + paymentSettings: settings, + }, + }); + return (updated.paymentSettings || {}); + } + async createSponsorInvoice(userId, input) { + const podcast = await this.prisma.podcast.findUnique({ + where: { id: input.podcastId }, + include: { workspace: { include: { members: true } } }, + }); + if (!podcast) { + throw new common_1.NotFoundException("Podcast not found"); + } + const isMember = podcast.workspace.members.some((m) => m.userId === userId); + if (!isMember) { + throw new common_1.ForbiddenException("You do not have access to this podcast"); + } + const platformCutKes = Math.round(input.amountKes * 0.015); + const payoutAmountKes = input.amountKes - platformCutKes; + const invoice = await this.prisma.sponsorInvoice.create({ + data: { + podcastId: input.podcastId, + sponsorName: input.sponsorName, + sponsorPhone: input.sponsorPhone, + amountKes: input.amountKes, + platformCutKes, + payoutAmountKes, + status: "pending", + }, + }); + return invoice; + } + async getSponsorInvoice(invoiceId) { + const invoice = await this.prisma.sponsorInvoice.findUnique({ + where: { id: invoiceId }, + include: { + podcast: { + select: { + id: true, + title: true, + }, + }, + }, + }); + if (!invoice) { + throw new common_1.NotFoundException("Invoice not found"); + } + return invoice; + } + async paySponsorInvoice(invoiceId, phone, method) { + const invoice = await this.prisma.sponsorInvoice.findUnique({ + where: { id: invoiceId }, + }); + if (!invoice) { + throw new common_1.NotFoundException("Invoice not found"); + } + const secret = this.payheroService.generateWebhookSecret(invoice.podcastId, invoice.id); + const callbackUrl = `${this.configService.get('PAYHERO_CALLBACK_URL') || ''}?secret=${secret}&podcastId=${invoice.podcastId}&invoiceId=${invoice.id}`; + if (method === "stk") { + const pushResult = await this.payheroService.initiateMpesaStkPush(invoice.amountKes, phone, invoice.id, callbackUrl); + await this.prisma.sponsorInvoice.update({ + where: { id: invoiceId }, + data: { + payheroReference: pushResult.CheckoutRequestID, + }, + }); + return { + status: "pending", + checkoutRequestId: pushResult.CheckoutRequestID || "", + customerMessage: "Complete payment on your phone", + paymentLink: "", + }; + } + else { + const paymentLink = this.payheroService.generatePaymentLink(invoice.amountKes, phone, invoice.sponsorName, invoice.id); + const finalLink = `${paymentLink}&callback_url=${encodeURIComponent(callbackUrl)}`; + return { + status: "redirect", + checkoutRequestId: "", + customerMessage: "", + paymentLink: finalLink, + }; + } + } + async retryFailedPayment(userId, workspaceId, paymentReference) { + const invoice = await this.prisma.sponsorInvoice.findUnique({ + where: { id: paymentReference }, + include: { + podcast: { + include: { workspace: { include: { members: true } } }, + }, + }, + }); + if (invoice) { + const isMember = invoice.podcast.workspace.members.some((m) => m.userId === userId); + if (!isMember) { + throw new common_1.ForbiddenException("You do not have access to this invoice"); + } + if (invoice.podcast.workspaceId !== workspaceId) { + throw new common_1.BadRequestException("Invoice does not belong to this workspace"); + } + if (invoice.status !== "failed") { + throw new common_1.BadRequestException("Only failed invoices can be retried"); + } + return this.paySponsorInvoice(paymentReference, invoice.sponsorPhone, "stk"); + } + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + if (!workspace) { + throw new common_1.NotFoundException("Workspace not found"); + } + const settings = (workspace.billingSettings || {}); + const subscription = settings.subscription; + if (!subscription || subscription.status !== SubscriptionStatus.PAST_DUE) { + throw new common_1.BadRequestException("No failed subscription payment to retry"); + } + const provider = subscription.provider; + const planId = this.parsePlanId(subscription.planId); + if (!planId) { + throw new common_1.BadRequestException("No pending plan on workspace"); + } + if (provider === BillingProvider.STRIPE) { + return this.createStripeCheckout(userId, { workspaceId, planId }); + } + if (provider === BillingProvider.PAYSTACK) { + return this.initiatePaystackCheckout(userId, { + workspaceId, + planId, + channel: "card", + email: subscription.paystackCustomerEmail || `${userId}@placeholder.local`, + }); + } + if (provider === BillingProvider.MPESA) { + return this.initiateMpesaCheckout(userId, { + workspaceId, + planId, + phoneNumber: subscription.accountReference || "", + }); + } + throw new common_1.BadRequestException(`Unsupported billing provider: ${provider}`); + } + async getCollectionStats(workspaceId, dateRange) { + const dateFilter = {}; + if (dateRange?.startDate || dateRange?.endDate) { + if (dateRange.startDate) + dateFilter.gte = new Date(dateRange.startDate); + if (dateRange.endDate) + dateFilter.lte = new Date(dateRange.endDate); + } + const hasDateFilter = Object.keys(dateFilter).length > 0; + const podcasts = await this.prisma.podcast.findMany({ + where: { workspaceId }, + select: { id: true }, + }); + const podcastIds = podcasts.map((p) => p.id); + const invoices = await this.prisma.sponsorInvoice.findMany({ + where: { + podcastId: { in: podcastIds }, + ...(hasDateFilter ? { createdAt: dateFilter } : {}), + }, + }); + const collectedKes = invoices + .filter((i) => i.status === "paid" || i.status === "disbursed") + .reduce((sum, i) => sum + i.amountKes, 0); + const pendingKes = invoices + .filter((i) => i.status === "pending") + .reduce((sum, i) => sum + i.amountKes, 0); + const failedKes = invoices + .filter((i) => i.status === "failed") + .reduce((sum, i) => sum + i.amountKes, 0); + const wallet = await this.prisma.wallet.findUnique({ + where: { workspaceId }, + include: { + transactions: { + where: { + type: "credit", + status: "completed", + ...(hasDateFilter ? { createdAt: dateFilter } : {}), + }, + }, + }, + }); + const totalCreditedCents = wallet?.transactions.reduce((sum, t) => sum + Math.round(Number(t.amount ?? 0) * 100), 0) ?? 0; + const totalPlatformFeeCents = wallet?.transactions.reduce((sum, t) => sum + Math.round(Number(t.platformCut ?? 0) * 100), 0) ?? 0; + return { + workspaceId, + sponsorInvoices: { + collectedKes, + pendingKes, + failedKes, + invoiceCount: invoices.length, + }, + ticketSales: { + totalCreditedUsd: totalCreditedCents / 100, + totalPlatformFeeUsd: totalPlatformFeeCents / 100, + netUsd: (totalCreditedCents - totalPlatformFeeCents) / 100, + transactionCount: wallet?.transactions.length ?? 0, + }, + currency: "KES/USD", + }; + } + async sendDunningNotice(workspaceId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true, name: true }, + }); + if (!workspace) { + throw new common_1.NotFoundException("Workspace not found"); + } + const settings = (workspace.billingSettings || {}); + const subscription = settings.subscription; + if (!subscription) { + throw new common_1.BadRequestException("Workspace has no subscription"); + } + const invoice = { + reference: subscription.lastInvoiceReference || null, + amount: subscription.lastInvoiceAmount || null, + currency: subscription.lastInvoiceCurrency || "USD", + status: "failed", + paidAt: null, + createdAt: new Date().toISOString(), + }; + await this.notifySubscriptionInvoice(workspaceId, invoice); + return { + workspaceId, + noticeSent: true, + status: subscription.status, + invoiceReference: invoice.reference, + }; + } + async getSubscriptionInvoices(workspaceId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + if (!workspace) { + throw new common_1.NotFoundException("Workspace not found"); + } + const settings = (workspace.billingSettings || {}); + const subscription = settings.subscription; + const history = Array.isArray(subscription?.invoiceHistory) + ? subscription.invoiceHistory + : []; + return history.map((inv) => ({ + reference: inv.reference, + amount: inv.amount, + currency: inv.currency, + status: inv.status, + paidAt: inv.paidAt, + createdAt: inv.createdAt, + })); + } + async downloadInvoice(invoiceId) { + const workspaces = await this.prisma.workspace.findMany({ + select: { id: true, name: true, slug: true, billingSettings: true }, + }); + for (const ws of workspaces) { + const settings = (ws.billingSettings || {}); + const history = Array.isArray(settings.subscription?.invoiceHistory) + ? settings.subscription.invoiceHistory + : []; + const inv = history.find((h) => h.reference === invoiceId || h.id === invoiceId); + if (inv) { + return { + invoiceId, + workspaceId: ws.id, + workspaceName: ws.name, + workspaceSlug: ws.slug, + reference: inv.reference, + amount: inv.amount, + currency: inv.currency, + status: inv.status, + paidAt: inv.paidAt, + createdAt: inv.createdAt, + receiptUrl: null, + }; + } + } + throw new common_1.NotFoundException("Invoice not found"); + } + async updatePaymentMethod(workspaceId, paymentMethodId) { + if (!this.stripeService.isConfigured()) { + throw new common_1.BadRequestException("Stripe is not configured"); + } + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { billingSettings: true }, + }); + if (!workspace) { + throw new common_1.NotFoundException("Workspace not found"); + } + const settings = (workspace.billingSettings || {}); + const subscription = settings.subscription; + const customerId = subscription?.stripeCustomerId; + if (!customerId) { + throw new common_1.BadRequestException("No Stripe customer on file — create a Stripe checkout first"); + } + const stripe = this.stripeService.getStripeClient(); + await stripe.paymentMethods.attach(paymentMethodId, { customer: customerId }); + await stripe.customers.update(customerId, { + invoice_settings: { default_payment_method: paymentMethodId }, + }); + await this.updateWorkspaceBillingSettings(workspaceId, { + subscription: { + updatedAt: new Date().toISOString(), + }, + }); + return { + workspaceId, + paymentMethodId, + customerId, + updated: true, + }; + } +}; +exports.BillingService = BillingService; +exports.BillingService = BillingService = BillingService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + config_1.ConfigService, + mpesa_service_1.MpesaService, + payhero_service_1.PayheroService, + paystack_service_1.PaystackService, + stripe_service_1.StripeService, + wallet_service_1.WalletService, + redis_idempotency_service_1.RedisIdempotencyService, + notifications_service_1.NotificationsService]) +], BillingService); +//# sourceMappingURL=billing.service.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/billing.service.js.map b/apps/api/dist/billing/billing.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..29bfbdda40ff5a46c83ec8950968dec49fc56315 --- /dev/null +++ b/apps/api/dist/billing/billing.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"billing.service.js","sourceRoot":"","sources":["../../src/billing/billing.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAQwB;AACxB,2CAA+C;AAC/C,4DAA4E;AAC5E,2CAA2D;AAE3D,6DAAyD;AACzD,6DAA+D;AAC/D,mDAA+C;AAC/C,uDAAmD;AACnD,yDAAwG;AACxG,qDAAiD;AACjD,6DAAyD;AACzD,mFAA8E;AAC9E,kFAA8E;AAE9E,mEAAuD;AA4DvD,MAAM,eAAe,GAAG;IACpB,MAAM,EAAE,QAA2B;IACnC,KAAK,EAAE,OAA0B;IACjC,QAAQ,EAAE,UAA6B;CAC1C,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACvB,KAAK,EAAE,OAA6B;IACpC,OAAO,EAAE,SAA+B;IACxC,MAAM,EAAE,QAA8B;CACzC,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACvB,MAAM,EAAE,QAA8B;IACtC,QAAQ,EAAE,UAAgC;IAC1C,OAAO,EAAE,SAA+B;IACxC,QAAQ,EAAE,UAAgC;IAC1C,QAAQ,EAAE,UAAgC;IAC1C,UAAU,EAAE,YAAkC;CACjD,CAAC;AAMF,MAAM,8BAA8B,GAAyC;IACzE,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC;IAClH,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,UAAU,CAAC;IAClJ,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,CAAC;IACpH,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC;IAC1D,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC;CAC7F,CAAC;AA8CK,IAAM,cAAc,sBAApB,MAAM,cAAc;IA+BN;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IAtCA,MAAM,GAAG,IAAI,eAAM,CAAC,gBAAc,CAAC,IAAI,CAAC,CAAC;IAEzC,WAAW,GAAqB;QAC7C;YACI,EAAE,EAAE,kBAAkB,CAAC,KAAK;YAC5B,IAAI,EAAE,OAAO;YACb,WAAW,EAAE,+BAA+B;YAC5C,aAAa,EAAE,CAAC;YAChB,QAAQ,EAAE,CAAC;YACX,QAAQ,EAAE,OAAO;SACpB;QACD;YACI,EAAE,EAAE,kBAAkB,CAAC,OAAO;YAC9B,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,2BAA2B;YACxC,aAAa,EAAE,IAAI;YACnB,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,OAAO;SACpB;QACD;YACI,EAAE,EAAE,kBAAkB,CAAC,MAAM;YAC7B,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,0BAA0B;YACvC,aAAa,EAAE,IAAI;YACnB,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,OAAO;SACpB;KACJ,CAAC;IAEJ,YACmB,MAAqB,EACrB,aAA4B,EAC5B,YAA0B,EAC1B,cAA8B,EAC9B,eAAgC,EAChC,aAA4B,EAC5B,aAA4B,EAC5B,kBAA2C,EAC3C,oBAA0C;QAR1C,WAAM,GAAN,MAAM,CAAe;QACrB,kBAAa,GAAb,aAAa,CAAe;QAC5B,iBAAY,GAAZ,YAAY,CAAc;QAC1B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,oBAAe,GAAf,eAAe,CAAiB;QAChC,kBAAa,GAAb,aAAa,CAAe;QAC5B,kBAAa,GAAb,aAAa,CAAe;QAC5B,uBAAkB,GAAlB,kBAAkB,CAAyB;QAC3C,yBAAoB,GAApB,oBAAoB,CAAsB;IAC1D,CAAC;IAEF,SAAS;QACL,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,wBAAwB,CAAC,MAAc,EAAE,WAAmB;QAC9D,MAAM,IAAI,CAAC,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAEtD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE;gBACJ,EAAE,EAAE,IAAI;gBACR,eAAe,EAAE,IAAI;aACxB;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,IAAI,CAAC,8BAA8B,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,CAAC,eAAsB,CAAC,CAAC;IAC/F,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,MAAc,EAAE,KAAgC;QACvE,MAAM,IAAI,CAAC,4BAA4B,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAEnE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,EAAE,KAAK,kBAAkB,CAAC,KAAK,EAAE,CAAC;YACvC,MAAM,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,WAAW,EAAE;gBACjD,MAAM,EAAE,kBAAkB,CAAC,KAAK;gBAChC,QAAQ,EAAE,eAAe,CAAC,MAAM;gBAChC,QAAQ,EAAE,IAAI;aACjB,CAAC,CAAC;YAEH,OAAO;gBACH,QAAQ,EAAE,eAAe,CAAC,MAAM;gBAChC,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,MAAM,EAAE,kBAAkB,CAAC,MAAM;gBACjC,WAAW,EAAE,IAAI;gBACjB,iBAAiB,EAAE,IAAI;aAC1B,CAAC;QACN,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,mBAAmB,CAAC,CAAC;QACzE,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,MAAM,IAAI,4BAAmB,CAAC,0BAA0B,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,SAAS,CAAC,IAAI,uBAAuB,CAAC;QACpF,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,GAAG,MAAM,2BAA2B,CAAC;QAC5E,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,GAAG,MAAM,6BAA6B,CAAC;QAE5E,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAClC,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QACnD,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC;QACzC,IAAI,CAAC,GAAG,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QACvD,IAAI,CAAC,GAAG,CAAC,wCAAwC,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC,GAAG,CAAC,gDAAgD,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1E,IAAI,CAAC,GAAG,CAAC,+CAA+C,EAAE,eAAe,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACtF,IAAI,CAAC,GAAG,CAAC,sCAAsC,EAAE,IAAI,CAAC,CAAC;QAEvD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,6CAA6C,EAAE;YACxE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,aAAa,EAAE,UAAU,YAAY,EAAE;gBACvC,cAAc,EAAE,mCAAmC;aACtD;YACD,IAAI;SACP,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;QAE9D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAChF,MAAM,IAAI,qCAA4B,CAAC,sCAAsC,CAAC,CAAC;QACnF,CAAC;QAED,MAAM,IAAI,CAAC,8BAA8B,CAAC,KAAK,CAAC,WAAW,EAAE;YACzD,YAAY,EAAE;gBACV,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,eAAe,CAAC,MAAM;gBAChC,MAAM,EAAE,kBAAkB,CAAC,OAAO;gBAClC,WAAW,EAAE,MAAM,CAAC,GAAG,IAAI,IAAI;gBAC/B,iBAAiB,EAAE,MAAM,CAAC,EAAE,IAAI,IAAI;gBACpC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,aAAa,EAAE,IAAI;aACtB;SACJ,CAAC,CAAC;QAEH,OAAO;YACH,QAAQ,EAAE,eAAe,CAAC,MAAM;YAChC,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,MAAM,EAAE,kBAAkB,CAAC,OAAO;YAClC,WAAW,EAAE,MAAM,CAAC,GAAG,IAAI,IAAI;YAC/B,iBAAiB,EAAE,MAAM,CAAC,EAAE,IAAI,IAAI;SACvC,CAAC;IACN,CAAC;IAEH,KAAK,CAAC,qBAAqB,CAAC,MAAc,EAAE,KAAiC;QAC3E,MAAM,IAAI,CAAC,4BAA4B,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAEnE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,EAAE,KAAK,kBAAkB,CAAC,KAAK,EAAE,CAAC;YACzC,MAAM,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,WAAW,EAAE;gBACnD,MAAM,EAAE,kBAAkB,CAAC,KAAK;gBAChC,QAAQ,EAAE,eAAe,CAAC,KAAK;gBAC/B,QAAQ,EAAE,IAAI;aACf,CAAC,CAAC;YAEH,OAAO;gBACL,QAAQ,EAAE,eAAe,CAAC,KAAK;gBAC/B,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,MAAM,EAAE,kBAAkB,CAAC,MAAM;gBACjC,iBAAiB,EAAE,IAAI;gBACvB,eAAe,EAAE,IAAI;aACtB,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,WAAW,CAAC;YACrE,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,qBAAqB,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;YAErF,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,sBAAsB,CAAC,IAAI,EAAE,WAAW,MAAM,gBAAgB,KAAK,CAAC,WAAW,WAAW,IAAI,CAAC,EAAE,EAAE,CAAC;YAE1J,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,oBAAoB,CAC/D,IAAI,CAAC,QAAQ,EACb,KAAK,CAAC,WAAW,EACjB,gBAAgB,EAChB,WAAW,CACZ,CAAC;YAEF,MAAM,IAAI,CAAC,8BAA8B,CAAC,KAAK,CAAC,WAAW,EAAE;gBAC3D,YAAY,EAAE;oBACZ,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,QAAQ,EAAE,eAAe,CAAC,KAAK;oBAC/B,MAAM,EAAE,kBAAkB,CAAC,OAAO;oBAClC,iBAAiB,EAAE,UAAU,CAAC,iBAAiB,IAAI,IAAI;oBACvD,gBAAgB,EAAE,UAAU,CAAC,SAAS,IAAI,IAAI;oBAC9C,gBAAgB;oBAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,aAAa,EAAE,IAAI;iBACpB;aACF,CAAC,CAAC;YAEH,OAAO;gBACL,QAAQ,EAAE,eAAe,CAAC,KAAK;gBAC/B,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,MAAM,EAAE,kBAAkB,CAAC,OAAO;gBAClC,iBAAiB,EAAE,UAAU,CAAC,iBAAiB,IAAI,IAAI;gBACvD,eAAe,EAAE,gCAAgC;aAClD,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;YACpD,MAAM,IAAI,qCAA4B,CAAC,yBAAyB,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAiBD,KAAK,CAAC,wBAAwB,CAAC,MAAc,EAAE,KAAoC;QACjF,MAAM,IAAI,CAAC,4BAA4B,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAEnE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAE5C,IAAI,IAAI,CAAC,EAAE,KAAK,kBAAkB,CAAC,KAAK,EAAE,CAAC;YACzC,MAAM,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,WAAW,EAAE;gBACnD,MAAM,EAAE,kBAAkB,CAAC,KAAK;gBAChC,QAAQ,EAAE,eAAe,CAAC,QAAQ;gBAClC,QAAQ,EAAE,IAAI;aACf,CAAC,CAAC;YAEH,OAAO;gBACL,QAAQ,EAAE,eAAe,CAAC,QAAQ;gBAClC,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,MAAM,EAAE,kBAAkB,CAAC,MAAM;gBACjC,SAAS,EAAE,IAAI;gBACf,gBAAgB,EAAE,IAAI;aACvB,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,EAAE,CAAC;YACzC,MAAM,IAAI,4BAAmB,CAAC,4BAA4B,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,yBAAyB,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAEjF,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7C,MAAM,IAAI,4BAAmB,CAAC,qCAAqC,CAAC,CAAC;QACvE,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,OAAO,IAAI,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YACpF,MAAM,IAAI,4BAAmB,CAC3B,gCAAgC,KAAK,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,WAAW,CACjG,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,SAAS,CAAC,IAAI,uBAAuB,CAAC;QACpF,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,GAAG,MAAM,6CAA6C,CAAC;QAC9F,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,GAAG,MAAM,+CAA+C,CAAC;QAE9F,IAAI,CAAC;YACH,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC;oBAC5D,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,MAAM;oBACN,QAAQ;oBACR,SAAS;oBACT,WAAW,EAAE,UAAU;oBACvB,QAAQ,EAAE;wBACR,WAAW,EAAE,KAAK,CAAC,WAAW;wBAC9B,MAAM,EAAE,IAAI,CAAC,EAAE;wBACf,MAAM;wBACN,QAAQ,EAAE,eAAe,CAAC,QAAQ;qBACnC;oBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;iBACnB,CAAC,CAAC;gBAEH,MAAM,IAAI,CAAC,8BAA8B,CAAC,KAAK,CAAC,WAAW,EAAE;oBAC3D,YAAY,EAAE;wBACZ,MAAM,EAAE,IAAI,CAAC,EAAE;wBACf,QAAQ,EAAE,eAAe,CAAC,QAAQ;wBAClC,MAAM,EAAE,kBAAkB,CAAC,OAAO;wBAClC,WAAW,EAAE,IAAI,CAAC,gBAAgB;wBAClC,gBAAgB,EAAE,SAAS;wBAC3B,qBAAqB,EAAE,KAAK,CAAC,KAAK;wBAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;wBACnC,aAAa,EAAE,IAAI;qBACpB;iBACF,CAAC,CAAC;gBAEH,OAAO;oBACL,QAAQ,EAAE,eAAe,CAAC,QAAQ;oBAClC,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,OAAO,EAAE,MAAM;oBACf,MAAM,EAAE,kBAAkB,CAAC,OAAO;oBAClC,SAAS;oBACT,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;iBACxC,CAAC;YACJ,CAAC;YAGD,MAAM,cAAc,GAClB,KAAK,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAE9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,uBAAuB,CAAC;gBAChE,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,MAAM;gBACN,QAAQ;gBACR,SAAS;gBACT,WAAW,EAAE,UAAU;gBACvB,QAAQ,EAAE;oBACR,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,MAAM;oBACN,QAAQ,EAAE,eAAe,CAAC,QAAQ;oBAClC,OAAO,EAAE,KAAK,CAAC,OAAO;iBACvB;gBACD,WAAW,EAAE;oBACX,KAAK,EAAE,KAAK,CAAC,WAAY;oBACzB,QAAQ,EAAE,cAAc;iBACzB;aACF,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,8BAA8B,CAAC,KAAK,CAAC,WAAW,EAAE;gBAC3D,YAAY,EAAE;oBACZ,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,QAAQ,EAAE,eAAe,CAAC,QAAQ;oBAClC,MAAM,EAAE,kBAAkB,CAAC,OAAO;oBAClC,WAAW,EAAE,IAAI;oBACjB,gBAAgB,EAAE,SAAS;oBAC3B,qBAAqB,EAAE,KAAK,CAAC,KAAK;oBAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,aAAa,EAAE,IAAI;iBACpB;aACF,CAAC,CAAC;YAEH,OAAO;gBACL,QAAQ,EAAE,eAAe,CAAC,QAAQ;gBAClC,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,MAAM,EAAE,kBAAkB,CAAC,OAAO;gBAClC,SAAS;gBACT,gBAAgB,EAAE,IAAI;gBACtB,cAAc,EACZ,MAAM,CAAC,cAAc;oBACrB,CAAC,KAAK,CAAC,OAAO,KAAK,OAAO;wBACxB,CAAC,CAAC,gEAAgE;wBAClE,CAAC,CAAC,uDAAuD,CAAC;aAC/D,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,qCAAqC,KAAK,CAAC,OAAO,GAAG,EACrD,KAAK,CACN,CAAC;YACF,MAAM,IAAI,qCAA4B,CACpC,YAAY,KAAK,CAAC,OAAO,kBAAkB,CAC5C,CAAC;QACJ,CAAC;IACH,CAAC;IAQO,yBAAyB,CAC/B,IAAoB,EACpB,OAAwB;QAExB,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YAEhD,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,GAAG,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC1D,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACzD,CAAC;IAOD,KAAK,CAAC,sBAAsB,CAAC,MAAc,EAAE,WAAmB,EAAE,SAAiB;QACjF,MAAM,IAAI,CAAC,4BAA4B,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAE7D,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,EAAE,CAAC;YACzC,MAAM,IAAI,4BAAmB,CAAC,4BAA4B,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAEvE,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,4BAAmB,CAC3B,wBAAwB,SAAS,8BAA8B,MAAM,CAAC,MAAM,GAAG,CAChF,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC,6BAA6B,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC/D,CAAC;IAMD,KAAK,CAAC,qBAAqB,CAAC,KAAU;QACpC,MAAM,SAAS,GAAG,KAAK,EAAE,KAA2B,CAAC;QACrD,MAAM,OAAO,GAAI,KAAK,EAAE,EAAyB,IAAI,SAAS,CAAC;QAC/D,MAAM,IAAI,GAAG,KAAK,EAAE,IAAI,CAAC;QAEzB,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,EAAE,CAAC;YACxB,MAAM,IAAI,4BAAmB,CAAC,gCAAgC,CAAC,CAAC;QAClE,CAAC;QAGD,MAAM,QAAQ,GAAG,OAAO;YACtB,CAAC,CAAC,kBAAkB,OAAO,EAAE;YAC7B,CAAC,CAAC,gBAAgB,IAAI,EAAE,SAAS,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACtD,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QACtE,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QAC7C,CAAC;QAGD,IAAI,SAAS,KAAK,gBAAgB,IAAI,SAAS,KAAK,qBAAqB,EAAE,CAAC;YAC1E,MAAM,MAAM,GAAgC;gBAC1C,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI;gBAC5B,eAAe,EAAE,IAAI,CAAC,gBAAgB;gBACtC,iBAAiB,EAAE,IAAI,CAAC,aAAa,EAAE,kBAAkB;aAC1D,CAAC;YAEF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,6BAA6B,CACtD,IAAI,CAAC,SAAS,EACd,MAAM,EACN,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,IAAI,CAC7B,CAAC;YAEF,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;gBACrB,MAAM,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YAC1C,CAAC;YAED,OAAO,OAAO,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;QACxE,CAAC;QAED,IAAI,SAAS,KAAK,eAAe,EAAE,CAAC;YAClC,MAAM,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,iCAAiC,EAAE,CAAC;QACtE,CAAC;QAKD,IACE,SAAS,KAAK,0BAA0B;YACxC,SAAS,KAAK,sBAAsB;YACpC,SAAS,KAAK,qBAAqB;YACnC,SAAS,KAAK,wBAAwB;YACtC,SAAS,KAAK,yBAAyB,EACvC,CAAC;YACD,MAAM,gBAAgB,GACpB,IAAI,EAAE,YAAY,EAAE,IAAI,IAAI,IAAI,EAAE,iBAAiB,CAAC;YACtD,MAAM,aAAa,GAAG,IAAI,EAAE,QAAQ,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,CAAC;YAE3D,IAAI,SAAS,GAAgD,IAAI,CAAC;YAElE,IAAI,gBAAgB,EAAE,CAAC;gBACrB,SAAS,GAAG,MAAM,IAAI,CAAC,uCAAuC,CAC5D,gBAAgB,CACjB,CAAC;YACJ,CAAC;YAGD,IAAI,CAAC,SAAS,IAAI,aAAa,EAAE,CAAC;gBAChC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;oBAC/C,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;iBAC5C,CAAC,CAAC;gBACH,SAAS;oBACP,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;wBACb,MAAM,CAAC,GAAI,CAAC,CAAC,eAA0C,IAAI,EAAE,CAAC;wBAC9D,OAAO,CACL,CAAC,CAAC,YAAY,EAAE,qBAAqB,KAAK,aAAa;4BACvD,CAAC,CAAC,YAAY,EAAE,QAAQ,KAAK,eAAe,CAAC,QAAQ,CACtD,CAAC;oBACJ,CAAC,CAAC,IAAI,IAAI,CAAC;YACf,CAAC;YAED,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,8CAA8C,EAAE,CAAC;YACpF,CAAC;YAED,MAAM,QAAQ,GAAI,SAAS,CAAC,eAA0C,IAAI,EAAE,CAAC;YAC7E,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;YAC3C,MAAM,aAAa,GAAG,YAAY,EAAE,MAAM,IAAI,IAAI,CAAC;YAEnD,MAAM,SAAS,GAAG,IAAI,CAAC,oCAAoC,CACzD,SAAS,EACT,aAAa,CACd,CAAC;YAEF,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;gBAE9D,MAAM,KAAK,GAAwB;oBACjC,MAAM,EAAE,SAAS;oBACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACpC,CAAC;gBAEF,IAAI,SAAS,KAAK,wBAAwB,EAAE,CAAC;oBAC3C,KAAK,CAAC,aAAa,GAAG,wBAAwB,CAAC;gBACjD,CAAC;gBACD,IAAI,SAAS,KAAK,0BAA0B,EAAE,CAAC;oBAC7C,KAAK,CAAC,aAAa,GAAG,0BAA0B,CAAC;gBACnD,CAAC;gBACD,IAAI,SAAS,KAAK,kBAAkB,CAAC,MAAM,EAAE,CAAC;oBAC5C,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;oBAE3B,IAAI,IAAI,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC;wBAC1C,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC;oBACvD,CAAC;gBACH,CAAC;gBAGD,IACE,SAAS,KAAK,yBAAyB;oBACvC,SAAS,KAAK,wBAAwB,EACtC,CAAC;oBACD,MAAM,OAAO,GAAG;wBACd,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,IAAI,IAAI,EAAE,SAAS;wBACtD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,IAAI,EAAE,MAAM;wBAC7C,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,QAAQ;wBACnD,MAAM,EAAE,SAAS,KAAK,yBAAyB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;wBACnE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,OAAO,IAAI,IAAI;wBACvD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;qBACpC,CAAC;oBAEF,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,cAAc,CAAC;wBACzD,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC;wBAC3C,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;oBAEd,KAAK,CAAC,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC;oBACxC,KAAK,CAAC,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;oBACzC,KAAK,CAAC,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC;oBAC7C,KAAK,CAAC,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;oBACzC,KAAK,CAAC,oBAAoB,GAAG,OAAO,CAAC,SAAS,CAAC;oBAC/C,KAAK,CAAC,cAAc,GAAG,OAAO,CAAC;oBAG/B,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;gBACxD,CAAC;gBAGD,IAAI,SAAS,IAAI,SAAS,KAAK,aAAa,EAAE,CAAC;oBAC7C,IAAI,CAAC,8BAA8B,CAAC,SAAS,CAAC,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;gBACzF,CAAC;gBAED,MAAM,IAAI,CAAC,8BAA8B,CAAC,SAAS,CAAC,EAAE,EAAE;oBACtD,YAAY,EAAE,KAAK;iBACpB,CAAC,CAAC;YACL,CAAC;YAED,MAAM,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,IAAI,aAAa,EAAE,CAAC;QAC/D,CAAC;QAGD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,4BAA4B,SAAS,EAAE,EAAE,CAAC;IAC7E,CAAC;IAOO,KAAK,CAAC,6BAA6B,CACzC,SAAiB,EACjB,MAAmC,EACnC,aAA6B;QAE7B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC;YACtD,KAAK,EAAE;gBACL,eAAe,EAAE;oBACf,IAAI,EAAE,CAAC,cAAc,EAAE,kBAAkB,CAAC;oBAC1C,MAAM,EAAE,SAAS;iBAClB;aACF;YACD,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;SAC5C,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,EAAE,CAAC;YAEf,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;gBAC/C,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;aAC5C,CAAC,CAAC;YACH,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC3B,MAAM,QAAQ,GAAI,CAAC,CAAC,eAA0C,IAAI,EAAE,CAAC;gBACrE,OAAO,QAAQ,CAAC,YAAY,EAAE,gBAAgB,KAAK,SAAS,CAAC;YAC/D,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;YAC7D,CAAC;YACD,OAAO,IAAI,CAAC,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;QACtE,CAAC;QAED,OAAO,IAAI,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IAC1E,CAAC;IAEO,KAAK,CAAC,yBAAyB,CACrC,SAA+C,EAC/C,MAAmC,EACnC,aAA6B;QAE7B,MAAM,QAAQ,GAAI,SAAS,CAAC,eAA0C,IAAI,EAAE,CAAC;QAC7E,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,aAAa,GAAG,QAAQ,CAAC,YAAY,EAAE,MAAM,IAAI,IAAI,CAAC;QAE5D,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B,EAAE,CAAC;QACpE,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAEhC,IACE,aAAa,KAAK,kBAAkB,CAAC,OAAO;gBAC5C,aAAa,KAAK,kBAAkB,CAAC,MAAM,EAC3C,CAAC;gBACD,IAAI,CAAC,8BAA8B,CACjC,aAAa,EACb,kBAAkB,CAAC,QAAQ,CAC5B,CAAC;gBACF,MAAM,IAAI,CAAC,8BAA8B,CAAC,SAAS,CAAC,EAAE,EAAE;oBACtD,YAAY,EAAE;wBACZ,MAAM,EAAE,kBAAkB,CAAC,QAAQ;wBACnC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;wBACnC,aAAa,EAAE,MAAM,CAAC,eAAe,IAAI,gBAAgB;qBAC1D;iBACF,CAAC,CAAC;YACL,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,kBAAkB,CAAC,QAAQ,EAAE,CAAC;QAChE,CAAC;QAED,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC9E,MAAM,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,EAAE,EAAE;YAC9C,MAAM,EAAE,aAAa;YACrB,QAAQ,EAAE,eAAe,CAAC,QAAQ;YAClC,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;QAGH,MAAM,KAAK,GACT,aAAa;YACb,QAAQ,CAAC,YAAY,EAAE,qBAAqB;YAC5C,IAAI,CAAC;QACP,IAAI,KAAK,IAAI,aAAa,KAAK,kBAAkB,CAAC,KAAK,EAAE,CAAC;YACxD,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAC/C,SAAS,CAAC,EAAE,EACZ,aAAa,EACb,KAAK,EACL,MAAM,CAAC,iBAAiB,IAAI,SAAS,CACtC,CAAC;gBAEF,MAAM,IAAI,CAAC,8BAA8B,CAAC,SAAS,CAAC,EAAE,EAAE;oBACtD,YAAY,EAAE;wBACZ,oBAAoB,EAAE,GAAG,CAAC,YAAY;wBACtC,wBAAwB,EAAE,GAAG,CAAC,gBAAgB;wBAC9C,kBAAkB,EAAE,GAAG,CAAC,UAAU;wBAClC,gBAAgB,EAAE,GAAG,CAAC,QAAQ;wBAC9B,qBAAqB,EAAE,KAAK;wBAC5B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;qBACpC;iBACF,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,wDAAwD,SAAS,CAAC,EAAE,GAAG,EACvE,GAAG,CACJ,CAAC;YAGJ,CAAC;QACH,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,kBAAkB,CAAC,MAAM,EAAE,CAAC;IAC9D,CAAC;IAOO,KAAK,CAAC,kBAAkB,CAC9B,MAA0B;QAE1B,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE;YAAE,OAAO,IAAI,CAAC;QAEtD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,IAAI,CAAC,EAAE,KAAK,kBAAkB,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAGtD,MAAM,QAAQ,GAAG,eAAe,IAAI,CAAC,EAAE,UAAU,CAAC;QAClD,IAAI,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAClE,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC;oBACnD,IAAI,EAAE,eAAe,IAAI,CAAC,IAAI,EAAE;oBAChC,MAAM,EAAE,IAAI,CAAC,aAAa;oBAC1B,QAAQ,EAAE,KAAK;oBACf,QAAQ,EAAE,SAAS;oBACnB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,QAAQ;iBACT,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBAEb,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;gBAC9D,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,MAAM,GAAG,EAAE,GAAG,CAAC,CAAC;oBAC9E,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,YAAY,CAAC,SAAS,CAAC;IAChC,CAAC;IAMO,KAAK,CAAC,0BAA0B,CACtC,WAAmB,EACnB,MAA0B,EAC1B,KAAa,EACb,iBAA0B;QAO1B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,uCAAuC,MAAM,EAAE,CAAC,CAAC;QACnE,CAAC;QAGD,IAAI,QAAmC,CAAC;QACxC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC;gBACxD,KAAK;gBACL,QAAQ,EAAE,EAAE,WAAW,EAAE;aAC1B,CAAC,CAAC;YACH,QAAQ,GAAG,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YAGb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kDAAkD,KAAK,GAAG,EAAE,GAAG,CAAC,CAAC;YAClF,QAAQ,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;QACtC,CAAC;QAGD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,CAAC;YACjE,YAAY,EAAE,QAAQ,CAAC,aAAa;YACpC,QAAQ;YACR,iBAAiB;SAClB,CAAC,CAAC;QAEH,OAAO;YACL,YAAY,EAAE,QAAQ,CAAC,aAAa;YACpC,gBAAgB,EAAE,YAAY,CAAC,iBAAiB;YAChD,UAAU,EAAE,YAAY,CAAC,WAAW,IAAI,IAAI;YAC5C,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,SAAS;SACtC,CAAC;IACJ,CAAC;IAKS,8BAA8B,CAClC,aAAwC,EACxC,SAA6B;QAE7B,IAAI,aAAa,KAAK,SAAS;YAAE,OAAO;QAExC,MAAM,GAAG,GAAG,aAAa,IAAI,UAAU,CAAC;QACxC,MAAM,OAAO,GAAG,8BAA8B,CAAC,GAAG,CAAC,CAAC;QACpD,IAAI,CAAC,OAAO,EAAE,CAAC;YAEX,IAAI,SAAS,KAAK,kBAAkB,CAAC,OAAO,IAAI,SAAS,KAAK,kBAAkB,CAAC,MAAM;gBAAE,OAAO;YAChG,MAAM,IAAI,4BAAmB,CACzB,iDAAiD,SAAS,8CAA8C,CAC3G,CAAC;QACN,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,4BAAmB,CACzB,gDAAgD,aAAa,SAAS,SAAS,KAAK;gBACpF,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACnC,CAAC;QACN,CAAC;IACL,CAAC;IAMO,KAAK,CAAC,uBAAuB,CAAC,OAAe;QACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,WAAW,OAAO,EAAE,CAAC,CAAC;QAChF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;IACzB,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,OAAe;QAC5C,MAAM,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,WAAW,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC9H,CAAC;IAqBD,KAAK,CAAC,kBAAkB,CACpB,MAAc,EACd,WAAmB,EACnB,UAAsD,EAAE;QAExD,MAAM,IAAI,CAAC,4BAA4B,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAE7D,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;SACpC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QAEnE,MAAM,QAAQ,GAAI,SAAS,CAAC,eAA0C,IAAI,EAAE,CAAC;QAC7E,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC3C,MAAM,aAAa,GAAG,YAAY,EAAE,MAAM,IAAI,IAAI,CAAC;QAEnD,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAEhF,MAAM,QAAQ,GAAG,YAAY,EAAE,QAAuC,CAAC;QACvE,MAAM,WAAW,GAAG,YAAY,EAAE,oBAA0C,CAAC;QAC7E,MAAM,eAAe,GAAG,YAAY,EAAE,wBAA8C,CAAC;QACrF,MAAM,kBAAkB,GAAG,YAAY,EAAE,kBAAwC,CAAC;QAClF,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAEjD,IAAI,CAAC;YACD,IAAI,QAAQ,KAAK,eAAe,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;gBACrD,IAAI,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,EAAE,CAAC;oBACpC,IAAI,WAAW,EAAE,CAAC;wBACd,MAAM,IAAI,CAAC,aAAa,CAAC,6BAA6B,CAAC,WAAW,CAAC,CAAC;oBACxE,CAAC;yBAAM,CAAC;wBACJ,MAAM,IAAI,CAAC,aAAa,CAAC,6BAA6B,CAAC,WAAW,CAAC,CAAC;oBACxE,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,+CAA+C,WAAW,eAAe,CAC5E,CAAC;gBACN,CAAC;YACL,CAAC;iBAAM,IACH,QAAQ,KAAK,eAAe,CAAC,QAAQ;gBACrC,eAAe;gBACf,kBAAkB,EACpB,CAAC;gBACC,IAAI,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,EAAE,CAAC;oBAItC,MAAM,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAC1C,eAAe,EACf,kBAAkB,CACrB,CAAC;oBACF,IAAI,WAAW,EAAE,CAAC;wBAGd,MAAM,OAAO,GAAG,YAAY,EAAE,gBAAsC,CAAC;wBACrE,IAAI,OAAO,EAAE,CAAC;4BACV,IAAI,CAAC;gCACD,MAAM,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;4BAC1D,CAAC;4BAAC,OAAO,SAAS,EAAE,CAAC;gCACjB,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,+CAA+C,OAAO,GAAG,EACzD,SAAS,CACZ,CAAC;4BACN,CAAC;wBACL,CAAC;oBACL,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,iDAAiD,WAAW,eAAe,CAC9E,CAAC;gBACN,CAAC;YACL,CAAC;QACL,CAAC;QAAC,OAAO,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,KAAK,CACb,mDAAmD,WAAW,GAAG,EACjE,WAAW,CACd,CAAC;YAIF,MAAM,IAAI,0BAAiB,CACvB,sFAAsF,CACzF,CAAC;QACN,CAAC;QAED,MAAM,SAAS,GAAG,WAAW;YACzB,CAAC,CAAC,kBAAkB,CAAC,QAAQ;YAC7B,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC;QAIlC,MAAM,IAAI,CAAC,8BAA8B,CAAC,WAAW,EAAE;YACnD,YAAY,EAAE;gBACV,MAAM,EAAE,SAAS;gBACjB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,aAAa,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;gBACrC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC7C;SACJ,CAAC,CAAC;QAEH,OAAO;YACH,WAAW;YACX,MAAM,EAAE,SAAS;YACjB,WAAW;YACX,iBAAiB,EAAE,CAAC,WAAW;SAClC,CAAC;IACN,CAAC;IAUD,KAAK,CAAC,kBAAkB,CAAC,MAAc,EAAE,WAAmB;QACxD,MAAM,IAAI,CAAC,4BAA4B,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAE7D,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;SACpC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QAEnE,MAAM,QAAQ,GAAI,SAAS,CAAC,eAA0C,IAAI,EAAE,CAAC;QAC7E,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC3C,MAAM,aAAa,GAAG,YAAY,EAAE,MAAM,IAAI,IAAI,CAAC;QAMnD,IAAI,aAAa,KAAK,kBAAkB,CAAC,QAAQ,EAAE,CAAC;YAChD,MAAM,IAAI,4BAAmB,CACzB,8CAA8C;gBAC1C,CAAC,aAAa,KAAK,kBAAkB,CAAC,OAAO;oBACzC,CAAC,CAAC,6DAA6D;oBAC/D,CAAC,CAAC,gDAAgD,CAAC,CAC9D,CAAC;QACN,CAAC;QAGD,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAE9E,MAAM,QAAQ,GAAG,YAAY,EAAE,QAAuC,CAAC;QACvE,MAAM,WAAW,GAAG,YAAY,EAAE,oBAA0C,CAAC;QAC7E,MAAM,eAAe,GAAG,YAAY,EAAE,wBAA8C,CAAC;QACrF,MAAM,kBAAkB,GAAG,YAAY,EAAE,kBAAwC,CAAC;QAKlF,IAAI,QAAQ,KAAK,eAAe,CAAC,MAAM,EAAE,CAAC;YACtC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACf,MAAM,IAAI,4BAAmB,CACzB,wEAAwE,CAC3E,CAAC;YACN,CAAC;QACL,CAAC;aAAM,IAAI,QAAQ,KAAK,eAAe,CAAC,QAAQ,EAAE,CAAC;YAC/C,IAAI,CAAC,eAAe,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC1C,MAAM,IAAI,4BAAmB,CACzB,0EAA0E,CAC7E,CAAC;YACN,CAAC;QACL,CAAC;aAAM,IAAI,QAAQ,KAAK,eAAe,CAAC,KAAK,EAAE,CAAC;YAC5C,MAAM,IAAI,4BAAmB,CACzB,gFAAgF,CACnF,CAAC;QACN,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,4BAAmB,CACzB,gEAAgE,CACnE,CAAC;QACN,CAAC;QAED,IAAI,CAAC;YACD,IAAI,QAAQ,KAAK,eAAe,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;gBACrD,IAAI,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,EAAE,CAAC;oBACpC,MAAM,IAAI,CAAC,aAAa,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;gBAC7D,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,6CAA6C,WAAW,eAAe,CAC1E,CAAC;gBACN,CAAC;YACL,CAAC;iBAAM,IACH,QAAQ,KAAK,eAAe,CAAC,QAAQ;gBACrC,eAAe;gBACf,kBAAkB,EACpB,CAAC;gBACC,IAAI,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,EAAE,CAAC;oBACtC,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,CACzC,eAAe,EACf,kBAAkB,CACrB,CAAC;gBACN,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,+CAA+C,WAAW,eAAe,CAC5E,CAAC;gBACN,CAAC;YACL,CAAC;QACL,CAAC;QAAC,OAAO,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,KAAK,CACb,6CAA6C,WAAW,GAAG,EAC3D,WAAW,CACd,CAAC;YACF,MAAM,IAAI,0BAAiB,CACvB,8EAA8E,CACjF,CAAC;QACN,CAAC;QAED,MAAM,IAAI,CAAC,8BAA8B,CAAC,WAAW,EAAE;YACnD,YAAY,EAAE;gBACV,MAAM,EAAE,kBAAkB,CAAC,MAAM;gBACjC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,aAAa,EAAE,IAAI;aACtB;SACJ,CAAC,CAAC;QAEH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,CAAC,MAAM,EAAE,CAAC;IAC9D,CAAC;IASD,KAAK,CAAC,UAAU,CACZ,MAAc,EACd,WAAmB,EACnB,SAA6B,EAC7B,UAAiC,EAAE;QAEnC,MAAM,IAAI,CAAC,4BAA4B,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAE7D,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,EAAE,KAAK,kBAAkB,CAAC,KAAK,EAAE,CAAC;YAE1C,OAAO,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;SACpC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QAEnE,MAAM,QAAQ,GAAI,SAAS,CAAC,eAA0C,IAAI,EAAE,CAAC;QAC7E,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC3C,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAE7D,IAAI,aAAa,KAAK,OAAO,CAAC,EAAE,EAAE,CAAC;YAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC9F,CAAC;QAED,MAAM,QAAQ,GAAG,YAAY,EAAE,QAAuC,CAAC;QACvE,MAAM,WAAW,GAAG,YAAY,EAAE,oBAA0C,CAAC;QAE7E,IAAI,QAAQ,KAAK,eAAe,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;YACrD,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,EAAE,CAAC;gBACrC,MAAM,IAAI,4BAAmB,CAAC,0BAA0B,CAAC,CAAC;YAC9D,CAAC;YACD,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CACrC,IAAI,CAAC,yBAAyB,CAAC,OAAO,CAAC,EAAE,CAAC,CAC7C,CAAC;YACF,IAAI,CAAC,UAAU,EAAE,CAAC;gBACd,MAAM,IAAI,4BAAmB,CACzB,0CAA0C,OAAO,CAAC,EAAE,EAAE,CACzD,CAAC;YACN,CAAC;YACD,MAAM,IAAI,CAAC,aAAa,CAAC,uBAAuB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QAC9E,CAAC;aAAM,IAAI,QAAQ,KAAK,eAAe,CAAC,QAAQ,EAAE,CAAC;YAI/C,MAAM,IAAI,4BAAmB,CACzB,+EAA+E,CAClF,CAAC;QACN,CAAC;aAAM,IAAI,QAAQ,KAAK,eAAe,CAAC,KAAK,EAAE,CAAC;YAC5C,MAAM,IAAI,4BAAmB,CACzB,+FAA+F,CAClG,CAAC;QACN,CAAC;aAAM,IAAI,QAAQ,KAAK,eAAe,CAAC,MAAM,EAAE,CAAC;YAI7C,MAAM,IAAI,4BAAmB,CACzB,iFAAiF,CACpF,CAAC;QACN,CAAC;aAAM,CAAC;YAGJ,MAAM,IAAI,4BAAmB,CACzB,iEAAiE,CACpE,CAAC;QACN,CAAC;QAED,MAAM,IAAI,CAAC,8BAA8B,CAAC,WAAW,EAAE;YACnD,YAAY,EAAE;gBACV,MAAM,EAAE,OAAO,CAAC,EAAE;gBAClB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACtC;SACJ,CAAC,CAAC;QAEH,OAAO;YACH,WAAW;YACX,MAAM,EAAE,OAAO,CAAC,EAAE;YAClB,cAAc,EAAE,aAAa;YAC7B,QAAQ,EAAE,OAAO,CAAC,OAAO,KAAK,KAAK,IAAI,QAAQ,KAAK,eAAe,CAAC,MAAM;SAC7E,CAAC;IACN,CAAC;IAOD,KAAK,CAAC,kBAAkB,CACpB,WAAmB,EACnB,WAAmB,EACnB,UAGI,EAAE;QAIN,MAAM,IAAA,oCAAmB,EAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAEpD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;SACpC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QAEnE,MAAM,QAAQ,GAAI,SAAS,CAAC,eAA0C,IAAI,EAAE,CAAC;QAC7E,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC3C,MAAM,QAAQ,GAAG,YAAY,EAAE,QAAuC,CAAC;QAEvE,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,MAAM,IAAI,4BAAmB,CAAC,yCAAyC,CAAC,CAAC;QAC7E,CAAC;QAED,IAAI,YAA4D,CAAC;QAEjE,IAAI,QAAQ,KAAK,eAAe,CAAC,MAAM,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,YAAY,EAAE,cAAoC,CAAC;YACpE,MAAM,eAAe,GAAG,YAAY,EAAE,qBAA2C,CAAC;YAClF,MAAM,MAAM,GAAG,QAAQ,IAAI,eAAe,CAAC;YAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;gBACV,MAAM,IAAI,4BAAmB,CACzB,mEAAmE,CACtE,CAAC;YACN,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,EAAE,CAAC;gBACrC,MAAM,IAAI,4BAAmB,CAAC,0BAA0B,CAAC,CAAC;YAC9D,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,CAChD,MAAM,EACN,OAAO,CAAC,WAAW,EACnB,uBAAuB,CAC1B,CAAC;YACF,YAAY,GAAG;gBACX,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,SAAS;aACrC,CAAC;QACN,CAAC;aAAM,IAAI,QAAQ,KAAK,eAAe,CAAC,QAAQ,EAAE,CAAC;YAC/C,MAAM,SAAS,GAAG,YAAY,EAAE,gBAAsC,CAAC;YACvE,IAAI,CAAC,SAAS,EAAE,CAAC;gBACb,MAAM,IAAI,4BAAmB,CACzB,sDAAsD,CACzD,CAAC;YACN,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,EAAE,CAAC;gBACvC,MAAM,IAAI,4BAAmB,CAAC,4BAA4B,CAAC,CAAC;YAChE,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,iBAAiB,CACvD,SAAS,EACT,OAAO,CAAC,WAAW,CACtB,CAAC;YACF,YAAY,GAAG;gBACX,EAAE,EAAE,MAAM,CAAC,SAAS;gBACpB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,MAAM,EAAE,MAAM,CAAC,MAAM;aACxB,CAAC;QACN,CAAC;aAAM,IAAI,QAAQ,KAAK,eAAe,CAAC,KAAK,EAAE,CAAC;YAC5C,MAAM,IAAI,4BAAmB,CACzB,qEAAqE,CACxE,CAAC;QACN,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,4BAAmB,CAAC,iCAAiC,QAAQ,EAAE,CAAC,CAAC;QAC/E,CAAC;QAED,MAAM,IAAI,CAAC,8BAA8B,CAAC,WAAW,EAAE;YACnD,YAAY,EAAE;gBACV,MAAM,EAAE,kBAAkB,CAAC,QAAQ;gBACnC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,aAAa,EAAE,OAAO,CAAC,MAAM,IAAI,kBAAkB,YAAY,CAAC,EAAE,EAAE;aACvE;SACJ,CAAC,CAAC;QAEH,OAAO;YACH,WAAW;YACX,QAAQ;YACR,QAAQ,EAAE,YAAY,CAAC,EAAE;YACzB,WAAW,EAAE,YAAY,CAAC,MAAM;YAChC,MAAM,EAAE,YAAY,CAAC,MAAM;SAC9B,CAAC;IACN,CAAC;IAMO,yBAAyB,CAAC,MAA0B;QACxD,OAAO,gBAAgB,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,KAAU;QAChC,MAAM,SAAS,GAAG,KAAK,EAAE,IAAI,CAAC;QAC9B,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC;QACvC,MAAM,OAAO,GAAG,KAAK,EAAE,EAAE,CAAC;QAE1B,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU,EAAE,CAAC;YAC5B,MAAM,IAAI,4BAAmB,CAAC,8BAA8B,CAAC,CAAC;QAClE,CAAC;QAGD,IAAI,OAAO,EAAE,CAAC;YACV,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;YACrE,IAAI,gBAAgB,EAAE,CAAC;gBACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;YAC/C,CAAC;QACL,CAAC;QAED,IAAI,SAAS,KAAK,4BAA4B,IAAI,SAAS,KAAK,2BAA2B,EAAE,CAAC;YAO1F,IACI,SAAS,KAAK,4BAA4B;gBAC1C,UAAU,CAAC,cAAc,KAAK,QAAQ,EACxC,CAAC;gBACC,IAAI,OAAO;oBAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;gBACpD,OAAO;oBACH,OAAO,EAAE,IAAI;oBACb,MAAM,EAAE,oDAAoD;iBAC/D,CAAC;YACN,CAAC;YAGD,IACI,SAAS,KAAK,4BAA4B;gBAC1C,UAAU,CAAC,QAAQ,EAAE,IAAI,KAAK,cAAc,EAC9C,CAAC;gBACC,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,OAAO,IAAI,UAAU,CAAC,mBAAmB,CAAC;gBAC/E,IAAI,CAAC,OAAO,EAAE,CAAC;oBACX,IAAI,OAAO;wBAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;oBACpD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;gBACjE,CAAC;gBAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;oBAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;oBACtB,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;iBACxE,CAAC,CAAC;gBACH,IAAI,CAAC,KAAK,EAAE,CAAC;oBACT,IAAI,OAAO;wBAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;oBACpD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;gBACzD,CAAC;gBAED,IAAI,KAAK,CAAC,MAAM,KAAK,oBAAW,CAAC,IAAI,EAAE,CAAC;oBACpC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;wBACrD,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE;wBACtC,MAAM,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE;qBACvC,CAAC,CAAC;oBAEH,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;oBAEzD,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;wBAC3B,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;wBACtB,IAAI,EAAE,EAAE,MAAM,EAAE,oBAAW,CAAC,IAAI,EAAE;qBACrC,CAAC,CAAC;oBAEH,MAAM,IAAI,CAAC,aAAa,CAAC,oBAAoB,CACzC,KAAK,CAAC,KAAK,CAAC,WAAW,EACvB,OAAO,EACP,UAAU,EACV,KAAK,CAAC,KAAK,CAAC,QAAQ,EACnB,SAAS,EAAE,kBAAyC,IAAI,QAAQ,CACpE,CAAC;gBACN,CAAC;gBAED,IAAI,OAAO;oBAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;gBACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YAC7B,CAAC;YAED,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,EAAE,WAAW,IAAI,UAAU,CAAC,mBAAmB,CAAC;YACvF,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAE7D,IAAI,CAAC,WAAW,IAAI,CAAC,MAAM,EAAE,CAAC;gBAC1B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC;YACpE,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;gBACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;gBAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;aACpC,CAAC,CAAC;YAEH,IAAI,CAAC,SAAS,EAAE,CAAC;gBACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;YAC7D,CAAC;YAED,MAAM,eAAe,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;YACpF,MAAM,aAAa,GAAG,eAAe,CAAC,YAAY,EAAE,MAAM,IAAI,IAAI,CAAC;YAGnE,IAAI,aAAa,KAAK,kBAAkB,CAAC,MAAM,IAAI,MAAM,KAAK,kBAAkB,CAAC,KAAK,EAAE,CAAC;gBACrF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,uCAAuC,EAAE,CAAC;YAC/E,CAAC;YAED,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;YAG9E,MAAM,oBAAoB,GACtB,OAAO,UAAU,CAAC,YAAY,KAAK,QAAQ;gBACvC,CAAC,CAAC,UAAU,CAAC,YAAY;gBACzB,CAAC,CAAC,eAAe,CAAC,YAAY,EAAE,oBAAoB,IAAI,IAAI,CAAC;YACrE,MAAM,gBAAgB,GAClB,OAAO,UAAU,CAAC,QAAQ,KAAK,QAAQ;gBACnC,CAAC,CAAC,UAAU,CAAC,QAAQ;gBACrB,CAAC,CAAC,eAAe,CAAC,YAAY,EAAE,gBAAgB,IAAI,IAAI,CAAC;YACjE,MAAM,qBAAqB,GACvB,OAAO,UAAU,CAAC,cAAc,KAAK,QAAQ;gBACzC,CAAC,CAAC,UAAU,CAAC,cAAc;gBAC3B,CAAC,CAAC,IAAI,CAAC;YACf,MAAM,cAAc,GAChB,CAAC,OAAO,UAAU,CAAC,aAAa,KAAK,QAAQ;gBACzC,CAAC,CAAC,UAAU,CAAC,aAAa;gBAC1B,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;YAExB,MAAM,IAAI,CAAC,sBAAsB,CAAC,WAAW,EAAE;gBAC3C,MAAM;gBACN,QAAQ,EAAE,eAAe,CAAC,MAAM;gBAChC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,kBAAkB,CAAC;aAC1D,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,8BAA8B,CAAC,WAAW,EAAE;gBACnD,YAAY,EAAE;oBACV,oBAAoB;oBACpB,gBAAgB;oBAChB,qBAAqB;oBACrB,cAAc;iBACjB;aACJ,CAAC,CAAC;YAEH,IAAI,OAAO;gBAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAED,IAAI,SAAS,KAAK,+BAA+B,EAAE,CAAC;YAChD,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC;YACrD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAgB,CAAC;YAC3C,IAAI,CAAC,WAAW,EAAE,CAAC;gBACf,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC;YACpE,CAAC;YAED,MAAM,YAAY,GAAG,IAAI,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC;YAC5D,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,IAAI,CAAC,8BAA8B,CAAC,WAAW,EAAE;oBACnD,YAAY,EAAE;wBACV,MAAM,EAAE,YAAY;wBACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;wBACnC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,kBAAkB,CAAC;wBACvD,aAAa,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI;qBACvE;iBACJ,CAAC,CAAC;YACP,CAAC;YAED,IAAI,OAAO;gBAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAED,IAAI,SAAS,KAAK,+BAA+B,EAAE,CAAC;YAChD,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC;YACrD,IAAI,CAAC,WAAW,EAAE,CAAC;gBACf,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC;YACpE,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;gBACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;gBAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;aACpC,CAAC,CAAC;YAEH,MAAM,aAAa,GAAG,SAAS;gBAC3B,CAAC,CAAC,CAAE,SAAS,CAAC,eAA0C,EAAE,YAAY,EAAE,MAAM,IAAI,IAAI,CAAC;gBACvF,CAAC,CAAC,IAAI,CAAC;YAEX,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YAEhF,MAAM,IAAI,CAAC,8BAA8B,CAAC,WAAW,EAAE;gBACnD,YAAY,EAAE;oBACV,MAAM,EAAE,kBAAkB,CAAC,QAAQ;oBACnC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,WAAW,EAAE,IAAI;oBACjB,iBAAiB,EAAE,IAAI;iBAC1B;aACJ,CAAC,CAAC;YAEH,IAAI,OAAO;gBAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAED,IAAI,SAAS,KAAK,iBAAiB,IAAI,SAAS,KAAK,uBAAuB,EAAE,CAAC;YAC3E,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,EAAE,WAAW,IAAI,UAAU,CAAC,mBAAmB,CAAC;YACvF,IAAI,WAAW,EAAE,CAAC;gBACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kCAAkC,WAAW,KAAK,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;YAExF,CAAC;YACD,IAAI,OAAO;gBAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;YACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,SAAS,EAAE,EAAE,CAAC;IACtE,CAAC;IAEO,yBAAyB,CAAC,YAAoB;QAClD,MAAM,GAAG,GAAuC;YAC5C,MAAM,EAAE,kBAAkB,CAAC,MAAM;YACjC,QAAQ,EAAE,kBAAkB,CAAC,QAAQ;YACrC,QAAQ,EAAE,kBAAkB,CAAC,QAAQ;YACrC,UAAU,EAAE,kBAAkB,CAAC,UAAU;YACzC,kBAAkB,EAAE,kBAAkB,CAAC,QAAQ;YAC/C,QAAQ,EAAE,kBAAkB,CAAC,MAAM;YACnC,MAAM,EAAE,kBAAkB,CAAC,QAAQ;YACnC,MAAM,EAAE,kBAAkB,CAAC,QAAQ;SACtC,CAAC;QACF,OAAO,GAAG,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC;IACrC,CAAC;IAMO,oCAAoC,CACxC,SAAiB,EACjB,aAAwC;QAExC,QAAQ,SAAS,EAAE,CAAC;YAChB,KAAK,qBAAqB;gBACtB,OAAO,kBAAkB,CAAC,MAAM,CAAC;YACrC,KAAK,sBAAsB;gBACvB,OAAO,kBAAkB,CAAC,QAAQ,CAAC;YACvC,KAAK,0BAA0B;gBAE3B,OAAO,kBAAkB,CAAC,QAAQ,CAAC;YACvC,KAAK,wBAAwB;gBACzB,OAAO,kBAAkB,CAAC,QAAQ,CAAC;YACvC,KAAK,yBAAyB;gBAC1B,OAAO,kBAAkB,CAAC,MAAM,CAAC;YACrC;gBACI,OAAO,IAAI,CAAC;QACpB,CAAC;IACL,CAAC;IAMO,KAAK,CAAC,wBAAwB,CAClC,WAAmB;QAEnB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC;YACvD,KAAK,EAAE;gBACH,WAAW;gBACX,IAAI,EAAE,4BAAa,CAAC,KAAK;aAC5B;YACD,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;SAC3B,CAAC,CAAC;QACH,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAMO,KAAK,CAAC,yBAAyB,CACnC,WAAmB,EACnB,OAOC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC,WAAW,CAAC,CAAC;QAClE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAElC,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;QAClE,MAAM,SAAS,GACX,OAAO,CAAC,MAAM,IAAI,IAAI,IAAI,OAAO,CAAC,QAAQ;YACtC,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YAC5D,CAAC,CAAC,gBAAgB,CAAC;QAE3B,MAAM,OAAO,CAAC,GAAG,CACb,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CACpB,IAAI,CAAC,oBAAoB;aACpB,kBAAkB,CAAC;YAChB,MAAM;YACN,IAAI,EAAE,+BAAgB,CAAC,OAAO;YAC9B,KAAK,EAAE,wBAAwB,WAAW,EAAE;YAC5C,OAAO,EAAE,8BACL,OAAO,CAAC,SAAS,IAAI,KACzB,SAAS,SAAS,QAAQ,WAAW,GAAG;YACxC,IAAI,EAAE,mBAAmB;YACzB,QAAQ,EAAE;gBACN,WAAW;gBACX,gBAAgB,EAAE,OAAO,CAAC,SAAS;gBACnC,aAAa,EAAE,OAAO,CAAC,MAAM;gBAC7B,eAAe,EAAE,OAAO,CAAC,QAAQ;gBACjC,aAAa,EAAE,OAAO,CAAC,MAAM;gBAC7B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,SAAS,EAAE,OAAO,CAAC,SAAS;aAC/B;SACJ,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACX,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,kDAAkD,MAAM,GAAG,EAC3D,GAAG,CACN,CACJ,CACR,CACJ,CAAC;IACN,CAAC;IAMO,KAAK,CAAC,8BAA8B,CACxC,WAAmB,EACnB,IAA+B,EAC/B,EAAsB,EACtB,OAAe;QAEf,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC,WAAW,CAAC,CAAC;QAClE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAElC,MAAM,YAAY,GAAuC;YACrD,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,SAAS;YAClB,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,UAAU;YACpB,UAAU,EAAE,YAAY;SAC3B,CAAC;QAEF,MAAM,OAAO,CAAC,GAAG,CACb,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CACpB,IAAI,CAAC,oBAAoB;aACpB,kBAAkB,CAAC;YAChB,MAAM;YACN,IAAI,EAAE,+BAAgB,CAAC,OAAO;YAC9B,KAAK,EAAE,6BAA6B;YACpC,OAAO,EAAE,4CACL,IAAI,IAAI,MACZ,OAAO,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,OAAO,IAAI;YACtD,IAAI,EAAE,mBAAmB;YACzB,QAAQ,EAAE;gBACN,WAAW;gBACX,cAAc,EAAE,IAAI;gBACpB,SAAS,EAAE,EAAE;gBACb,OAAO;aACV;SACJ,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACX,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,wDAAwD,MAAM,GAAG,EACjE,GAAG,CACN,CACJ,CACR,CACJ,CAAC;IACN,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,OAAY;QACjC,MAAM,QAAQ,GAAG,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC;QAE5C,IAAI,CAAC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;YAC/B,MAAM,IAAI,4BAAmB,CAAC,iCAAiC,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC;QAGrD,MAAM,QAAQ,GAAG,SAAS,iBAAiB,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QACrE,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QACtE,IAAI,gBAAgB,EAAE,CAAC;YACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QAC/C,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,8BAA8B,CAAC,iBAAiB,CAAC,CAAC;QAC/E,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,6CAA6C,EAAE,CAAC;QACrF,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;QAC7E,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,aAAa,GAAG,QAAQ,CAAC,YAAY,EAAE,MAAM,IAAI,IAAI,CAAC;QAE5D,IAAI,CAAC,aAAa,EAAE,CAAC;YACjB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC;QAC7E,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;QACpD,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;YAE9E,MAAM,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,EAAE,EAAE;gBAC5C,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,eAAe,CAAC,KAAK;gBAC/B,QAAQ,EAAE,IAAI;aACjB,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,kBAAkB,CAAC,MAAM,EAAE,CAAC;QAChE,CAAC;QAGD,IAAI,aAAa,KAAK,kBAAkB,CAAC,OAAO,IAAI,aAAa,KAAK,kBAAkB,CAAC,MAAM,EAAE,CAAC;YAC9F,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YAEhF,MAAM,IAAI,CAAC,8BAA8B,CAAC,SAAS,CAAC,EAAE,EAAE;gBACpD,YAAY,EAAE;oBACV,MAAM,EAAE,kBAAkB,CAAC,QAAQ;oBACnC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,aAAa,EAAE,QAAQ,CAAC,UAAU,IAAI,gBAAgB;iBACzD;aACJ,CAAC,CAAC;QACP,CAAC;QAED,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,kBAAkB,CAAC,QAAQ,EAAE,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,OAAY,EAAE,KAAU;QAC/C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAGlF,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACpC,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;YACtC,MAAM,MAAM,GAAG,KAAK,CAAC,MAA4B,CAAC;YAElD,MAAM,WAAW,GAAG,OAAO,EAAE,QAAQ,CAAC;YACtC,IAAI,CAAC,WAAW,EAAE,iBAAiB,EAAE,CAAC;gBAClC,MAAM,IAAI,4BAAmB,CAAC,+CAA+C,CAAC,CAAC;YACnF,CAAC;YAED,MAAM,iBAAiB,GAAG,WAAW,CAAC,iBAAiB,CAAC;YACxD,MAAM,QAAQ,GAAG,eAAe,iBAAiB,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;YAC9E,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;YACtE,IAAI,gBAAgB,EAAE,CAAC;gBACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;YAC/C,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;gBACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;aAC7B,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,EAAE,CAAC;gBACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAC;YACrE,CAAC;YAED,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;YAC7E,MAAM,aAAa,GAAG,QAAQ,CAAC,YAAY,EAAE,MAAM,IAAI,IAAI,CAAC;YAE5D,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;YACvD,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;gBACnB,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;gBAE9E,MAAM,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,EAAE,EAAE;oBAC5C,MAAM;oBACN,QAAQ,EAAE,eAAe,CAAC,KAAK;oBAC/B,QAAQ,EAAE,IAAI;iBACjB,CAAC,CAAC;gBAEH,MAAM,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;gBACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,kBAAkB,CAAC,MAAM,EAAE,CAAC;YAChE,CAAC;YAGD,IAAI,aAAa,KAAK,kBAAkB,CAAC,OAAO,IAAI,aAAa,KAAK,kBAAkB,CAAC,MAAM,EAAE,CAAC;gBAC9F,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;gBAEhF,MAAM,IAAI,CAAC,8BAA8B,CAAC,SAAS,CAAC,EAAE,EAAE;oBACpD,YAAY,EAAE;wBACV,MAAM,EAAE,kBAAkB,CAAC,QAAQ;wBACnC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;wBACnC,aAAa,EAAE,WAAW,CAAC,UAAU,IAAI,gBAAgB;qBAC5D;iBACJ,CAAC,CAAC;YACP,CAAC;YAED,MAAM,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,kBAAkB,CAAC,QAAQ,EAAE,CAAC;QAClE,CAAC;QAGD,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;YAClC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;YAElC,MAAM,WAAW,GAAG,OAAO,EAAE,QAAQ,CAAC;YACtC,IAAI,CAAC,WAAW,EAAE,iBAAiB,EAAE,CAAC;gBAClC,MAAM,IAAI,4BAAmB,CAAC,0CAA0C,CAAC,CAAC;YAC9E,CAAC;YAED,MAAM,iBAAiB,GAAG,WAAW,CAAC,iBAAiB,CAAC;YACxD,MAAM,QAAQ,GAAG,mBAAmB,iBAAiB,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;YAClF,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;YACtE,IAAI,gBAAgB,EAAE,CAAC;gBACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;YAC/C,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC;gBACxD,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;gBACxB,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;aAC7B,CAAC,CAAC;YACH,IAAI,CAAC,OAAO,EAAE,CAAC;gBACX,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;YAC3D,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;YACvD,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;gBAEnB,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;oBACpC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;oBACxB,IAAI,EAAE;wBACF,MAAM,EAAE,MAAM;wBACd,gBAAgB,EAAE,WAAW,CAAC,kBAAkB,IAAI,WAAW,CAAC,iBAAiB;qBACpF;iBACJ,CAAC,CAAC;gBAGH,MAAM,eAAe,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,IAAI,EAAE,CAAQ,CAAC;gBACvE,MAAM,WAAW,GAAG,eAAe,CAAC,WAAW,IAAI,eAAe,CAAC,WAAW,CAAC;gBAE/E,IAAI,WAAW,EAAE,CAAC;oBACd,IAAI,CAAC;wBACD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAC9D,OAAO,CAAC,eAAe,EACvB,WAAW,EACX,OAAO,CAAC,EAAE,CACb,CAAC;wBAEF,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;4BACpC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;4BACxB,IAAI,EAAE;gCACF,MAAM,EAAE,WAAW;gCACnB,eAAe,EAAE,cAAc,CAAC,kBAAkB,IAAI,cAAc,CAAC,mBAAmB;6BAC3F;yBACJ,CAAC,CAAC;oBACP,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,SAAS,EAAE,EAAE,GAAG,CAAC,CAAC;oBACnF,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,SAAS,oBAAoB,CAAC,CAAC;gBAC9F,CAAC;gBAED,MAAM,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;gBACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACJ,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;oBACpC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;oBACxB,IAAI,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;iBAC7B,CAAC,CAAC;gBACH,MAAM,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;gBACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YAC/C,CAAC;QACL,CAAC;QAED,MAAM,IAAI,4BAAmB,CAAC,yBAAyB,CAAC,CAAC;IAC7D,CAAC;IAEO,WAAW,CAAC,KAAgC;QAChD,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,KAAK,KAAK,kBAAkB,CAAC,KAAK;YAAE,OAAO,kBAAkB,CAAC,KAAK,CAAC;QACxE,IAAI,KAAK,KAAK,kBAAkB,CAAC,OAAO;YAAE,OAAO,kBAAkB,CAAC,OAAO,CAAC;QAC5E,IAAI,KAAK,KAAK,kBAAkB,CAAC,MAAM;YAAE,OAAO,kBAAkB,CAAC,MAAM,CAAC;QAC1E,OAAO,IAAI,CAAC;IAChB,CAAC;IAOO,KAAK,CAAC,8BAA8B,CAAC,iBAAyB;QAClE,IAAI,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC;gBACpD,KAAK,EAAE;oBACH,eAAe,EAAE;wBACb,IAAI,EAAE,CAAC,cAAc,EAAE,mBAAmB,CAAC;wBAC3C,MAAM,EAAE,iBAAiB;qBAC5B;iBACJ;gBACD,MAAM,EAAE;oBACJ,EAAE,EAAE,IAAI;oBACR,eAAe,EAAE,IAAI;iBACxB;aACJ,CAAC,CAAC;YAEH,IAAI,SAAS;gBAAE,OAAO,SAAS,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,8CAA8C,EAAE,KAAK,CAAC,CAAC;QAC5E,CAAC;QAID,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;YACpD,MAAM,EAAE;gBACJ,EAAE,EAAE,IAAI;gBACR,eAAe,EAAE,IAAI;aACxB;SACJ,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;YAC7E,OAAO,QAAQ,CAAC,YAAY,EAAE,iBAAiB,KAAK,iBAAiB,CAAC;QAC1E,CAAC,CAAC,IAAI,IAAI,CAAC;IACf,CAAC;IAKO,KAAK,CAAC,uCAAuC,CACjD,gBAAwB;QAExB,IAAI,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC;gBACpD,KAAK,EAAE;oBACH,eAAe,EAAE;wBACb,IAAI,EAAE,CAAC,cAAc,EAAE,0BAA0B,CAAC;wBAClD,MAAM,EAAE,gBAAgB;qBAC3B;iBACJ;gBACD,MAAM,EAAE;oBACJ,EAAE,EAAE,IAAI;oBACR,eAAe,EAAE,IAAI;iBACxB;aACJ,CAAC,CAAC;YAEH,IAAI,SAAS;gBAAE,OAAO,SAAS,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,8CAA8C,EAAE,KAAK,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;YACpD,MAAM,EAAE;gBACJ,EAAE,EAAE,IAAI;gBACR,eAAe,EAAE,IAAI;aACxB;SACJ,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;YAC7E,OAAO,QAAQ,CAAC,YAAY,EAAE,wBAAwB,KAAK,gBAAgB,CAAC;QAChF,CAAC,CAAC,IAAI,IAAI,CAAC;IACf,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAChC,WAAmB,EACnB,MAIC;QAGD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;SACpC,CAAC,CAAC;QAEH,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,eAAe,IAAI,EAAE,CAA2B,CAAC;QACrF,MAAM,aAAa,GAAG,eAAe,CAAC,YAAY,EAAE,MAAM,IAAI,IAAI,CAAC;QAGnE,IAAI,CAAC,8BAA8B,CAAC,aAAa,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAG9E,MAAM,YAAY,GAAG,eAAe,CAAC,YAAY,EAAE,MAAM,CAAC;QAC1D,IAAI,YAAY,IAAI,YAAY,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;YACjD,IAAI,CAAC,MAAM,CAAC,GAAG,CACX,6BAA6B,WAAW,KAAK,YAAY,OAAO,MAAM,CAAC,MAAM,EAAE,CAClF,CAAC;QACN,CAAC;QAED,MAAM,IAAI,CAAC,8BAA8B,CAAC,WAAW,EAAE;YACnD,YAAY,EAAE;gBACV,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,MAAM,EAAE,kBAAkB,CAAC,MAAM;gBACjC,WAAW,EAAE,IAAI;gBACjB,iBAAiB,EAAE,IAAI;gBACvB,aAAa,EAAE,IAAI;gBACnB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC5B;SACJ,CAAC,CAAC;IAEP,CAAC;IAEO,8BAA8B,CAAC,WAAmB,EAAE,eAAuC;QAC/F,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,YAAY,EAAE,MAAM,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC;QACnG,MAAM,QAAQ,GAAG,eAAe,EAAE,YAAY,EAAE,QAAQ,IAAI,eAAe,CAAC,MAAM,CAAC;QACnF,MAAM,MAAM,GAAG,eAAe,EAAE,YAAY,EAAE,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC;QAElF,OAAO;YACH,WAAW;YACX,MAAM;YACN,QAAQ;YACR,MAAM;YACN,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC/E,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,QAAQ,IAAI,IAAI;YACzD,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,IAAI,IAAI;SAClE,CAAC;IACN,CAAC;IAEO,WAAW,CAAC,MAA0B;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,MAAM,IAAI,4BAAmB,CAAC,cAAc,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,8BAA8B,CAAC,WAAmB,EAAE,KAA6B;QAC3F,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACrD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE;gBACJ,EAAE,EAAE,IAAI;gBACR,eAAe,EAAE,IAAI;aACxB;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,OAAO,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;QAC5E,MAAM,IAAI,GAA2B;YACjC,GAAG,OAAO;YACV,YAAY,EAAE;gBACV,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;gBAC/B,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;aAChC;SACJ,CAAC;QAEF,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;YAC/B,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,IAAI,EAAE;gBACF,eAAe,EAAE,IAAW;aAC/B;SACJ,CAAC,CAAC;QAKH,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAwB,EAAE,CAAC;YACzC,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;gBAC3B,QAAQ,CAAC,kBAAkB,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;YAC3D,CAAC;YACD,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;gBAC3B,QAAQ,CAAC,kBAAkB,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;YAC3D,CAAC;YACD,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC;gBAC7B,QAAQ,CAAC,eAAe,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;YAC1D,CAAC;YACD,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;oBAC/B,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;oBAC1B,IAAI,EAAE,QAAQ;iBACjB,CAAC,CAAC;YACP,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,qBAAqB,CAAC,WAAmB,EAAE,MAAc;QACnE,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC;YAC5D,KAAK,EAAE;gBACH,kBAAkB,EAAE;oBAChB,MAAM;oBACN,WAAW;iBACd;aACJ;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,MAAM,IAAI,2BAAkB,CAAC,0CAA0C,CAAC,CAAC;QAC7E,CAAC;QAED,OAAO,UAAU,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,4BAA4B,CAAC,WAAmB,EAAE,MAAc;QAC1E,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACzE,MAAM,YAAY,GAAoB,CAAC,4BAAa,CAAC,KAAK,EAAE,4BAAa,CAAC,KAAK,CAAC,CAAC;QAEjF,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAqB,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,2BAAkB,CAAC,iDAAiD,CAAC,CAAC;QACpF,CAAC;QAED,OAAO,UAAU,CAAC;IACtB,CAAC;IAEK,SAAS,CAAC,KAAc;QAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,CAAC;IAGO,0BAA0B,CAAC,GAAW;QAC5C,OAAO,IAAI,CAAC,YAAY,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,WAAmB;QAanB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE;SAC/D,CAAC,CAAC;QAEH,MAAM,MAAM,GACT,SAAS,EAAE,kBAAyC,IAAI,kBAAkB,CAAC,KAAK,CAAC;QACpF,MAAM,MAAM,GACT,SAAS,EAAE,kBAAyC,IAAI,kBAAkB,CAAC,MAAM,CAAC;QAErF,MAAM,aAAa,GACjB,MAAM,KAAK,kBAAkB,CAAC,MAAM,IAAI,MAAM,KAAK,kBAAkB,CAAC,QAAQ;YAC5E,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC;QAE/B,MAAM,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC;QAChC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACxB,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAGlC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO;aACzC,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;aAC1D,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAEzC,MAAM,CAAC,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,CAAC,GACjF,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;gBACtB,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE;aAC3E,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBACxB,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE;aACtE,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;gBACrB,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE;aACnF,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,SAAS;iBAClB,SAAS,CAAC;gBACT,KAAK,EAAE;oBACL,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;oBAC/C,MAAM,EAAE,WAAW;oBACnB,SAAS,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE;iBACjC;gBACD,MAAM,EAAE,IAAI;aACb,CAAC;iBACD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;YAExB,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;SACnB,CAAC,CAAC;QAEL,MAAM,KAAK,GAAG;YACZ,cAAc,EAAE;gBACd,OAAO,EAAE,eAAe;gBACxB,KAAK,EAAE,IAAA,oCAAY,EAAC,aAAwC,EAAE,mBAAmB,CAAC;aACnF;YACD,gBAAgB,EAAE;gBAChB,OAAO,EAAE,iBAAiB;gBAC1B,KAAK,EAAE,IAAA,oCAAY,EAAC,aAAwC,EAAE,qBAAqB,CAAC;aACrF;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,cAAc;gBACvB,KAAK,EAAE,IAAA,oCAAY,EAAC,aAAwC,EAAE,kBAAkB,CAAC;aAClF;YACD,qBAAqB,EAAE;gBACrB,OAAO,EAAE,aAAa;gBACtB,KAAK,EAAE,IAAA,oCAAY,EAAC,aAAwC,EAAE,0BAA0B,CAAC;aAC1F;YACD,gBAAgB,EAAE;gBAChB,OAAO,EAAE,QAAQ;gBACjB,KAAK,EAAE,IAAA,oCAAY,EAAC,aAAwC,EAAE,qBAAqB,CAAC;aACrF;SACF,CAAC;QAEF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,MAAc,EAAE,SAAiB;QACxD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YACnD,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;SACvD,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,0BAAiB,CAAC,mBAAmB,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;QAC5E,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,2BAAkB,CAAC,wCAAwC,CAAC,CAAC;QACzE,CAAC;QAED,OAAO,CAAC,OAAO,CAAC,eAAe,IAAI,EAAE,CAAwB,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,MAAc,EAAE,SAAiB,EAAE,QAAa;QAC1E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YACnD,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;SACvD,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,0BAAiB,CAAC,mBAAmB,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;QAC5E,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,2BAAkB,CAAC,wCAAwC,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAC/C,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,IAAI,EAAE;gBACJ,eAAe,EAAE,QAAQ;aAC1B;SACF,CAAC,CAAC;QAEH,OAAO,CAAC,OAAO,CAAC,eAAe,IAAI,EAAE,CAAwB,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,MAAc,EAAE,KAA0F;QACnI,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YACnD,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE;YAC9B,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;SACvD,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,0BAAiB,CAAC,mBAAmB,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;QAC5E,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,2BAAkB,CAAC,wCAAwC,CAAC,CAAC;QACzE,CAAC;QAGD,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC;QAC3D,MAAM,eAAe,GAAG,KAAK,CAAC,SAAS,GAAG,cAAc,CAAC;QAEzD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;YACtD,IAAI,EAAE;gBACJ,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,cAAc;gBACd,eAAe;gBACf,MAAM,EAAE,SAAS;aAClB;SACF,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,SAAiB;QACvC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC;YAC1D,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,OAAO,EAAE;gBACP,OAAO,EAAE;oBACP,MAAM,EAAE;wBACN,EAAE,EAAE,IAAI;wBACR,KAAK,EAAE,IAAI;qBACZ;iBACF;aACF;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,0BAAiB,CAAC,mBAAmB,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,SAAiB,EAAE,KAAa,EAAE,MAAsB;QAC9E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC;YAC1D,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;SACzB,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,0BAAiB,CAAC,mBAAmB,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,qBAAqB,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QACxF,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,sBAAsB,CAAC,IAAI,EAAE,WAAW,MAAM,cAAc,OAAO,CAAC,SAAS,cAAc,OAAO,CAAC,EAAE,EAAE,CAAC;QAE9J,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,oBAAoB,CAC/D,OAAO,CAAC,SAAS,EACjB,KAAK,EACL,OAAO,CAAC,EAAE,EACV,WAAW,CACZ,CAAC;YAEF,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;gBACtC,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;gBACxB,IAAI,EAAE;oBACJ,gBAAgB,EAAE,UAAU,CAAC,iBAAiB;iBAC/C;aACF,CAAC,CAAC;YAEH,OAAO;gBACL,MAAM,EAAE,SAAS;gBACjB,iBAAiB,EAAE,UAAU,CAAC,iBAAiB,IAAI,EAAE;gBACrD,eAAe,EAAE,gCAAgC;gBACjD,WAAW,EAAE,EAAE;aAChB,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,mBAAmB,CACzD,OAAO,CAAC,SAAS,EACjB,KAAK,EACL,OAAO,CAAC,WAAW,EACnB,OAAO,CAAC,EAAE,CACX,CAAC;YAEF,MAAM,SAAS,GAAG,GAAG,WAAW,iBAAiB,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;YAEnF,OAAO;gBACL,MAAM,EAAE,UAAU;gBAClB,iBAAiB,EAAE,EAAE;gBACrB,eAAe,EAAE,EAAE;gBACnB,WAAW,EAAE,SAAS;aACvB,CAAC;QACJ,CAAC;IACH,CAAC;IAUD,KAAK,CAAC,kBAAkB,CACtB,MAAc,EACd,WAAmB,EACnB,gBAAwB;QAGxB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC;YAC1D,KAAK,EAAE,EAAE,EAAE,EAAE,gBAAgB,EAAE;YAC/B,OAAO,EAAE;gBACP,OAAO,EAAE;oBACP,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;iBACvD;aACF;SACF,CAAC,CAAC;QAEH,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CACrD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAC3B,CAAC;YACF,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,2BAAkB,CAAC,wCAAwC,CAAC,CAAC;YACzE,CAAC;YACD,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;gBAChD,MAAM,IAAI,4BAAmB,CAAC,2CAA2C,CAAC,CAAC;YAC7E,CAAC;YACD,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAChC,MAAM,IAAI,4BAAmB,CAAC,qCAAqC,CAAC,CAAC;YACvE,CAAC;YAED,OAAO,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QAC/E,CAAC;QAGD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;QAC7E,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC3C,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,kBAAkB,CAAC,QAAQ,EAAE,CAAC;YACzE,MAAM,IAAI,4BAAmB,CAAC,yCAAyC,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAuC,CAAC;QACtE,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,4BAAmB,CAAC,8BAA8B,CAAC,CAAC;QAChE,CAAC;QAED,IAAI,QAAQ,KAAK,eAAe,CAAC,MAAM,EAAE,CAAC;YACxC,OAAO,IAAI,CAAC,oBAAoB,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,QAAQ,KAAK,eAAe,CAAC,QAAQ,EAAE,CAAC;YAC1C,OAAO,IAAI,CAAC,wBAAwB,CAAC,MAAM,EAAE;gBAC3C,WAAW;gBACX,MAAM;gBACN,OAAO,EAAE,MAAM;gBACf,KAAK,EAAG,YAAoB,CAAC,qBAAqB,IAAI,GAAG,MAAM,oBAAoB;aACpF,CAAC,CAAC;QACL,CAAC;QAED,IAAI,QAAQ,KAAK,eAAe,CAAC,KAAK,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE;gBACxC,WAAW;gBACX,MAAM;gBACN,WAAW,EAAG,YAAoB,CAAC,gBAAgB,IAAI,EAAE;aAC1D,CAAC,CAAC;QACL,CAAC;QAED,MAAM,IAAI,4BAAmB,CAAC,iCAAiC,QAAQ,EAAE,CAAC,CAAC;IAC7E,CAAC;IAOD,KAAK,CAAC,kBAAkB,CACtB,WAAmB,EACnB,SAAoD;QAEpD,MAAM,UAAU,GAA4B,EAAE,CAAC;QAC/C,IAAI,SAAS,EAAE,SAAS,IAAI,SAAS,EAAE,OAAO,EAAE,CAAC;YAC/C,IAAI,SAAS,CAAC,SAAS;gBAAE,UAAU,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACxE,IAAI,SAAS,CAAC,OAAO;gBAAE,UAAU,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAGzD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YAClD,KAAK,EAAE,EAAE,WAAW,EAAE;YACtB,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;SACrB,CAAC,CAAC;QACH,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAE7C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC;YACzD,KAAK,EAAE;gBACL,SAAS,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE;gBAC7B,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,UAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3D;SACF,CAAC,CAAC;QAEH,MAAM,YAAY,GAAG,QAAQ;aAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;aAC9D,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC5C,MAAM,UAAU,GAAG,QAAQ;aACxB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC;aACrC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,QAAQ;aACvB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC;aACpC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QAG5C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;YACjD,KAAK,EAAE,EAAE,WAAW,EAAE;YACtB,OAAO,EAAE;gBACP,YAAY,EAAE;oBACZ,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,MAAM,EAAE,WAAW;wBACnB,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,UAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC3D;iBACF;aACF;SACF,CAAC,CAAC;QAEH,MAAM,kBAAkB,GAAG,MAAM,EAAE,YAAY,CAAC,MAAM,CACpD,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,EACzD,CAAC,CACF,IAAI,CAAC,CAAC;QAEP,MAAM,qBAAqB,GAAG,MAAM,EAAE,YAAY,CAAC,MAAM,CACvD,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,EAC9D,CAAC,CACF,IAAI,CAAC,CAAC;QAEP,OAAO;YACL,WAAW;YACX,eAAe,EAAE;gBACf,YAAY;gBACZ,UAAU;gBACV,SAAS;gBACT,YAAY,EAAE,QAAQ,CAAC,MAAM;aAC9B;YACD,WAAW,EAAE;gBACX,gBAAgB,EAAE,kBAAkB,GAAG,GAAG;gBAC1C,mBAAmB,EAAE,qBAAqB,GAAG,GAAG;gBAChD,MAAM,EAAE,CAAC,kBAAkB,GAAG,qBAAqB,CAAC,GAAG,GAAG;gBAC1D,gBAAgB,EAAE,MAAM,EAAE,YAAY,CAAC,MAAM,IAAI,CAAC;aACnD;YACD,QAAQ,EAAE,SAAS;SACpB,CAAC;IACJ,CAAC;IAMD,KAAK,CAAC,iBAAiB,CAAC,WAAmB;QACzC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;SAC9C,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;QAC7E,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC3C,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,4BAAmB,CAAC,+BAA+B,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,OAAO,GAAG;YACd,SAAS,EAAE,YAAY,CAAC,oBAAoB,IAAI,IAAI;YACpD,MAAM,EAAE,YAAY,CAAC,iBAAiB,IAAI,IAAI;YAC9C,QAAQ,EAAE,YAAY,CAAC,mBAAmB,IAAI,KAAK;YACnD,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;QAEF,MAAM,IAAI,CAAC,yBAAyB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAE3D,OAAO;YACL,WAAW;YACX,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,YAAY,CAAC,MAAM;YAC3B,gBAAgB,EAAE,OAAO,CAAC,SAAS;SACpC,CAAC;IACJ,CAAC;IAQD,KAAK,CAAC,uBAAuB,CAAC,WAAmB;QAC/C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;QAC7E,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,cAAc,CAAC;YACzD,CAAC,CAAC,YAAY,CAAC,cAAc;YAC7B,CAAC,CAAC,EAAE,CAAC;QAEP,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC;YAChC,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,SAAS,EAAE,GAAG,CAAC,SAAS;SACzB,CAAC,CAAC,CAAC;IACN,CAAC;IAMD,KAAK,CAAC,eAAe,CAAC,SAAiB;QAGrC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;YACtD,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;SACpE,CAAC,CAAC;QAEH,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,CAAC,EAAE,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;YACtE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;gBAClE,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,cAAc;gBACtC,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CACtB,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,EAAE,KAAK,SAAS,CAC5D,CAAC;YACF,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO;oBACL,SAAS;oBACT,WAAW,EAAE,EAAE,CAAC,EAAE;oBAClB,aAAa,EAAE,EAAE,CAAC,IAAI;oBACtB,aAAa,EAAE,EAAE,CAAC,IAAI;oBACtB,SAAS,EAAE,GAAG,CAAC,SAAS;oBACxB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,SAAS,EAAE,GAAG,CAAC,SAAS;oBACxB,UAAU,EAAE,IAAI;iBACjB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,IAAI,0BAAiB,CAAC,mBAAmB,CAAC,CAAC;IACnD,CAAC;IAMD,KAAK,CAAC,mBAAmB,CACvB,WAAmB,EACnB,eAAuB;QAEvB,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,EAAE,CAAC;YACvC,MAAM,IAAI,4BAAmB,CAAC,0BAA0B,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE;YAC1B,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,0BAAiB,CAAC,qBAAqB,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,eAAe,IAAI,EAAE,CAA2B,CAAC;QAC7E,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC3C,MAAM,UAAU,GAAG,YAAY,EAAE,gBAAsC,CAAC;QACxE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,4BAAmB,CAC3B,6DAA6D,CAC9D,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,eAAe,EAAE,CAAC;QAGpD,MAAM,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,eAAe,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;QAG9E,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE;YACxC,gBAAgB,EAAE,EAAE,sBAAsB,EAAE,eAAe,EAAE;SAC9D,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,8BAA8B,CAAC,WAAW,EAAE;YACrD,YAAY,EAAE;gBACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC;SACF,CAAC,CAAC;QAEH,OAAO;YACL,WAAW;YACX,eAAe;YACf,UAAU;YACV,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;CACF,CAAA;AAnlFY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,mBAAU,GAAE;qCAgCgB,8BAAa;QACN,sBAAa;QACd,4BAAY;QACV,gCAAc;QACb,kCAAe;QACjB,8BAAa;QACb,8BAAa;QACR,mDAAuB;QACrB,4CAAoB;GAvClD,cAAc,CAmlF1B"} \ No newline at end of file diff --git a/apps/api/dist/billing/mpesa-webhook.guard.js b/apps/api/dist/billing/mpesa-webhook.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..2bb05536e808748ead4473e2901efd141201baab --- /dev/null +++ b/apps/api/dist/billing/mpesa-webhook.guard.js @@ -0,0 +1,143 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var MpesaWebhookGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MpesaWebhookGuard = void 0; +const common_1 = require("@nestjs/common"); +const mpesa_service_1 = require("./mpesa.service"); +const config_1 = require("@nestjs/config"); +const net = __importStar(require("net")); +const SAFARICOM_IP_RANGES = [ + '196.201.214.0/24', + '196.201.214.128/25', + '196.201.213.0/24', + '196.201.213.64/26', + '196.201.212.0/24', + '196.201.212.128/25', + '196.201.211.0/24', + '196.201.210.0/24', + '196.201.209.0/24', + '196.201.208.0/24', + '41.212.0.0/16', + '41.139.0.0/16', + '197.136.0.0/16', +]; +const MAX_CALLBACK_AGE_MS = 5 * 60 * 1000; +function ipInCidr(ip, cidr) { + const [range, bitsStr] = cidr.split('/'); + const bits = parseInt(bitsStr, 10); + const ipNum = ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0; + const rangeNum = range + .split('.') + .reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0; + const mask = ~(2 ** (32 - bits) - 1); + return (ipNum & mask) === (rangeNum & mask); +} +function isKnownSafaricomIp(ip) { + const cleaned = ip.startsWith('::ffff:') ? ip.slice(7) : ip; + if (!net.isIPv4(cleaned)) + return false; + return SAFARICOM_IP_RANGES.some((range) => ipInCidr(cleaned, range)); +} +let MpesaWebhookGuard = MpesaWebhookGuard_1 = class MpesaWebhookGuard { + mpesaService; + configService; + logger = new common_1.Logger(MpesaWebhookGuard_1.name); + isProduction; + constructor(mpesaService, configService) { + this.mpesaService = mpesaService; + this.configService = configService; + this.isProduction = this.configService.get('NODE_ENV') === 'production'; + } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + if (!this.isProduction) { + this.logger.debug('M-Pesa webhook guard: sandbox mode — accepting request'); + return true; + } + const clientIp = request.headers['x-forwarded-for']?.split(',')[0]?.trim() || + request.ip || + ''; + if (!isKnownSafaricomIp(clientIp)) { + this.logger.error(`M-Pesa webhook rejected — unknown IP: ${clientIp}`); + throw new common_1.UnauthorizedException('Unknown M-Pesa webhook source'); + } + const body = request.body; + if (body?.CreateTime && typeof body.CreateTime === 'string') { + const createTime = new Date(body.CreateTime).getTime(); + if (Number.isFinite(createTime)) { + const age = Date.now() - createTime; + if (age > MAX_CALLBACK_AGE_MS) { + this.logger.error(`M-Pesa webhook replay detected — callback age ${Math.round(age / 1000)}s exceeds max ${MAX_CALLBACK_AGE_MS / 1000}s`); + throw new common_1.UnauthorizedException('Stale M-Pesa webhook callback'); + } + } + } + const rawBody = request.rawBody || JSON.stringify(request.body); + const signature = request.headers['x-auth-checksum'] || + request.headers['x-mpesa-signature']; + if (signature) { + const isValid = this.mpesaService.verifyWebhookSignature(rawBody, request.headers, signature); + if (!isValid) { + this.logger.error('M-Pesa webhook signature verification failed'); + throw new common_1.UnauthorizedException('Invalid M-Pesa webhook signature'); + } + } + else { + const strictMode = this.configService.get('MPESA_STRICT_WEBHOOK') === 'true'; + if (strictMode) { + this.logger.error('M-Pesa webhook missing signature header'); + throw new common_1.UnauthorizedException('Missing M-Pesa webhook signature header'); + } + } + return true; + } +}; +exports.MpesaWebhookGuard = MpesaWebhookGuard; +exports.MpesaWebhookGuard = MpesaWebhookGuard = MpesaWebhookGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [mpesa_service_1.MpesaService, + config_1.ConfigService]) +], MpesaWebhookGuard); +//# sourceMappingURL=mpesa-webhook.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/mpesa-webhook.guard.js.map b/apps/api/dist/billing/mpesa-webhook.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..d24a4c798d88f652dbd0e6be49d1ee2132542a1b --- /dev/null +++ b/apps/api/dist/billing/mpesa-webhook.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"mpesa-webhook.guard.js","sourceRoot":"","sources":["../../src/billing/mpesa-webhook.guard.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/mpesa.service.js b/apps/api/dist/billing/mpesa.service.js new file mode 100644 index 0000000000000000000000000000000000000000..db9510f148782a98374900494ecfbf81bec642c9 --- /dev/null +++ b/apps/api/dist/billing/mpesa.service.js @@ -0,0 +1,121 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var MpesaService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MpesaService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const crypto_1 = require("crypto"); +let MpesaService = MpesaService_1 = class MpesaService { + configService; + logger = new common_1.Logger(MpesaService_1.name); + baseUrl; + consumerKey; + consumerSecret; + shortCode; + passKey; + callbackUrl; + constructor(configService) { + this.configService = configService; + this.consumerKey = this.configService.get('MPESA_CONSUMER_KEY') || ''; + this.consumerSecret = this.configService.get('MPESA_CONSUMER_SECRET') || ''; + this.shortCode = this.configService.get('MPESA_SHORTCODE') || '174379'; + this.passKey = this.configService.get('MPESA_PASSKEY') || ''; + this.callbackUrl = this.configService.get('MPESA_CALLBACK_URL') || ''; + const isProduction = this.configService.get('NODE_ENV') === 'production'; + const customBaseUrl = this.configService.get('MPESA_BASE_URL'); + if (customBaseUrl) { + this.baseUrl = customBaseUrl; + } + else { + this.baseUrl = isProduction + ? 'https://api.safaricom.co.ke' + : 'https://sandbox.safaricom.co.ke'; + } + if (!this.consumerKey || !this.consumerSecret) { + this.logger.warn('MPESA_CONSUMER_KEY or MPESA_CONSUMER_SECRET is not configured'); + } + } + async getAccessToken() { + const auth = Buffer.from(`${this.consumerKey}:${this.consumerSecret}`).toString('base64'); + try { + const response = await fetch(`${this.baseUrl}/oauth/v1/generate?grant_type=client_credentials`, { + method: 'GET', + headers: { + Authorization: `Basic ${auth}`, + 'Content-Type': 'application/json', + }, + }); + const payload = (await response.json()); + if (!response.ok || !payload.access_token) { + this.logger.error(`Failed to get M-Pesa token: ${JSON.stringify(payload)}`); + throw new Error('Unable to authenticate with M-Pesa'); + } + return payload.access_token; + } + catch (error) { + this.logger.error('M-Pesa OAuth failed', error); + throw error; + } + } + getMpesaCredentials() { + const now = new Date(); + const timestamp = now.toISOString().replace(/[-:]/g, '').slice(0, 14); + const password = Buffer.from(`${this.shortCode}${this.passKey}${timestamp}`).toString('base64'); + return { timestamp, password }; + } + normalizePhoneNumber(phone) { + const digits = phone.replace(/\D/g, ''); + if (digits.startsWith('254') && digits.length === 12) { + return digits; + } + if (digits.startsWith('0') && digits.length === 10) { + return `254${digits.slice(1)}`; + } + if (digits.length === 9) { + return `254${digits}`; + } + throw new Error('Invalid Kenyan phone number format'); + } + verifyWebhookSignature(rawBody, headers, expectedSignature) { + const isProduction = this.configService.get('NODE_ENV') === 'production'; + if (!isProduction) { + return true; + } + if (expectedSignature) { + const computedChecksum = this.computeMpesaChecksum(rawBody); + return computedChecksum === expectedSignature; + } + return false; + } + computeMpesaChecksum(rawBody) { + const secret = this.configService.get('MPESA_WEBHOOK_SECRET') || ''; + if (!secret) { + return ''; + } + return (0, crypto_1.createHmac)('sha256', secret).update(rawBody).digest('hex'); + } + getBaseUrl() { + return this.baseUrl; + } + getCallbackUrl() { + return this.callbackUrl; + } + getShortCode() { + return this.shortCode; + } +}; +exports.MpesaService = MpesaService; +exports.MpesaService = MpesaService = MpesaService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [config_1.ConfigService]) +], MpesaService); +//# sourceMappingURL=mpesa.service.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/mpesa.service.js.map b/apps/api/dist/billing/mpesa.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..7d0a259a1569ffd264f41bfdb49fea00f2f4166c --- /dev/null +++ b/apps/api/dist/billing/mpesa.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"mpesa.service.js","sourceRoot":"","sources":["../../src/billing/mpesa.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/payhero-webhook.guard.js b/apps/api/dist/billing/payhero-webhook.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..2fc72a96dd4960ca028d42884850859b12e2b46a --- /dev/null +++ b/apps/api/dist/billing/payhero-webhook.guard.js @@ -0,0 +1,80 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var PayheroWebhookGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PayheroWebhookGuard = void 0; +const common_1 = require("@nestjs/common"); +const payhero_service_1 = require("./payhero.service"); +const config_1 = require("@nestjs/config"); +const MAX_CALLBACK_AGE_MS = 10 * 60 * 1000; +let PayheroWebhookGuard = PayheroWebhookGuard_1 = class PayheroWebhookGuard { + payheroService; + configService; + logger = new common_1.Logger(PayheroWebhookGuard_1.name); + isProduction; + bypassValidation; + constructor(payheroService, configService) { + this.payheroService = payheroService; + this.configService = configService; + this.isProduction = this.configService.get('NODE_ENV') === 'production'; + this.bypassValidation = this.configService.get('BYPASS_PAYHERO_GUARD') === 'true'; + } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + const { secret, workspaceId, planId, podcastId, invoiceId } = request.query; + if (!secret) { + this.logger.error('PayHero webhook: Missing secret signature in query params'); + throw new common_1.UnauthorizedException('Missing webhook signature'); + } + let signatureValid = false; + if (workspaceId && planId) { + signatureValid = this.payheroService.verifyWebhookSecret(secret, workspaceId, planId); + } + else if (podcastId && invoiceId) { + signatureValid = this.payheroService.verifyWebhookSecret(secret, podcastId, invoiceId); + } + if (!signatureValid) { + this.logger.error('PayHero webhook signature verification failed: Invalid hash secret'); + throw new common_1.UnauthorizedException('Invalid PayHero webhook signature'); + } + if (this.isProduction || !this.bypassValidation) { + const resultDate = request.body?.response?.ResultDate; + if (resultDate) { + const ts = new Date(resultDate).getTime(); + if (Number.isFinite(ts) && Date.now() - ts > MAX_CALLBACK_AGE_MS) { + this.logger.error(`PayHero webhook replay detected — callback age ${(Date.now() - ts) / 1000}s exceeds max ${MAX_CALLBACK_AGE_MS / 1000}s`); + throw new common_1.UnauthorizedException('Stale PayHero webhook callback'); + } + } + } + if (!this.isProduction && this.bypassValidation) { + this.logger.debug('PayHero webhook guard: local dev — IP & replay checks skipped'); + return true; + } + const clientIp = request.headers['x-forwarded-for']?.split(',')[0]?.trim() || + request.ip || + ''; + const allowedIps = (this.configService.get('PAYHERO_ALLOWED_IPS') || '').split(',').filter(Boolean); + if (allowedIps.length > 0 && !allowedIps.includes(clientIp)) { + this.logger.error(`PayHero webhook rejected — IP ${clientIp} not in PAYHERO_ALLOWED_IPS`); + throw new common_1.UnauthorizedException('Unknown PayHero webhook source'); + } + this.logger.debug(`PayHero webhook guard: OK — source=${clientIp} context=${workspaceId ? 'subscription' : podcastId ? 'invoice' : 'unknown'}`); + return true; + } +}; +exports.PayheroWebhookGuard = PayheroWebhookGuard; +exports.PayheroWebhookGuard = PayheroWebhookGuard = PayheroWebhookGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [payhero_service_1.PayheroService, + config_1.ConfigService]) +], PayheroWebhookGuard); +//# sourceMappingURL=payhero-webhook.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/payhero-webhook.guard.js.map b/apps/api/dist/billing/payhero-webhook.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..294fb848bdfa042f778928536024b003f9a4906a --- /dev/null +++ b/apps/api/dist/billing/payhero-webhook.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"payhero-webhook.guard.js","sourceRoot":"","sources":["../../src/billing/payhero-webhook.guard.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/payhero.service.js b/apps/api/dist/billing/payhero.service.js new file mode 100644 index 0000000000000000000000000000000000000000..53a3e83a3f0b14c7b5fb5d718fdc252d16feb317 --- /dev/null +++ b/apps/api/dist/billing/payhero.service.js @@ -0,0 +1,166 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var PayheroService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PayheroService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const crypto_1 = require("crypto"); +let PayheroService = PayheroService_1 = class PayheroService { + configService; + logger = new common_1.Logger(PayheroService_1.name); + baseUrl; + authToken; + channelId; + callbackUrl; + constructor(configService) { + this.configService = configService; + this.authToken = this.configService.get('PAYHERO_AUTH_TOKEN') || ''; + this.channelId = Number(this.configService.get('PAYHERO_CHANNEL_ID') || '133'); + this.baseUrl = this.configService.get('PAYHERO_API_URL') || 'https://backend.payhero.co.ke/api/v2'; + this.callbackUrl = this.configService.get('PAYHERO_CALLBACK_URL') || ''; + if (!this.authToken) { + this.logger.warn('PAYHERO_AUTH_TOKEN is not configured'); + } + } + normalizePhoneNumber(phone, format = 'local') { + const digits = phone.replace(/\D/g, ''); + let rawNumber = ''; + if (digits.startsWith('254') && digits.length === 12) { + rawNumber = digits.slice(3); + } + else if (digits.startsWith('0') && digits.length === 10) { + rawNumber = digits.slice(1); + } + else if (digits.length === 9) { + rawNumber = digits; + } + else { + throw new Error('Invalid Kenyan phone number format. Must be 07xx, 01xx or 2547xx.'); + } + return format === 'local' ? `0${rawNumber}` : `254${rawNumber}`; + } + async initiateMpesaStkPush(amount, phoneNumber, externalReference, customCallbackUrl) { + const formattedPhone = this.normalizePhoneNumber(phoneNumber, 'local'); + const finalCallbackUrl = customCallbackUrl || this.callbackUrl; + try { + const response = await fetch(`${this.baseUrl}/payments`, { + method: 'POST', + headers: { + Authorization: `Basic ${this.authToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + amount: Math.round(amount), + phone_number: formattedPhone, + channel_id: this.channelId, + provider: 'm-pesa', + external_reference: externalReference, + callback_url: finalCallbackUrl, + }), + }); + const payload = (await response.json()); + if (!response.ok || !payload.success) { + this.logger.error(`PayHero STK Push request failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to initiate PayHero payment'); + } + return payload; + } + catch (error) { + this.logger.error('PayHero STK push failed', error); + throw error; + } + } + async triggerWithdrawal(amount, recipientPhone, externalReference, customCallbackUrl) { + const formattedPhone = this.normalizePhoneNumber(recipientPhone, 'international'); + const finalCallbackUrl = customCallbackUrl || this.callbackUrl; + try { + const response = await fetch(`${this.baseUrl}/withdraw`, { + method: 'POST', + headers: { + Authorization: `Basic ${this.authToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + external_reference: externalReference, + amount: Math.round(amount), + phone_number: formattedPhone, + network_code: '63902', + payment_service: 'b2c', + callback_url: finalCallbackUrl, + channel: 'mobile', + }), + }); + const payload = (await response.json()); + if (!response.ok || payload.response_code !== '0') { + this.logger.error(`PayHero withdrawal request failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to initiate PayHero withdrawal'); + } + return payload; + } + catch (error) { + this.logger.error('PayHero withdrawal failed', error); + throw error; + } + } + async getTransactionStatus(reference) { + try { + const response = await fetch(`${this.baseUrl}/transaction-status?reference=${encodeURIComponent(reference)}`, { + method: 'GET', + headers: { + Authorization: `Basic ${this.authToken}`, + 'Content-Type': 'application/json', + }, + }); + const payload = (await response.json()); + if (!response.ok) { + this.logger.error(`PayHero transaction status check failed: ${JSON.stringify(payload)}`); + throw new Error('Failed to query transaction status from PayHero'); + } + return payload; + } + catch (error) { + this.logger.error('PayHero status check failed', error); + throw error; + } + } + generatePaymentLink(amount, phone, name, reference, customChannelId) { + const channel = customChannelId || this.channelId; + const formattedPhone = this.normalizePhoneNumber(phone, 'local'); + const merchantId = this.authToken.split(':')[0] || 'merchant'; + const configMerchantId = this.configService.get('PAYHERO_MERCHANT_ID') || merchantId; + return `https://lipwa.link/${configMerchantId}?amount=${Math.round(amount)}&phone=${formattedPhone}&name=${encodeURIComponent(name)}&channel_id=${channel}&reference=${encodeURIComponent(reference)}`; + } + generateWebhookSecret(targetId, detailId) { + const secret = this.configService.get('COOKIE_SECRET'); + if (!secret) { + this.logger.error('COOKIE_SECRET is required to sign PayHero webhook secrets'); + throw new Error('COOKIE_SECRET is not configured'); + } + return (0, crypto_1.createHmac)('sha256', secret) + .update(`${targetId}:${detailId}`) + .digest('hex'); + } + verifyWebhookSecret(querySecret, targetId, detailId) { + const expected = this.generateWebhookSecret(targetId, detailId); + if (!querySecret || querySecret.length !== expected.length) + return false; + const a = Buffer.from(querySecret); + const b = Buffer.from(expected); + return (0, crypto_1.timingSafeEqual)(a, b); + } +}; +exports.PayheroService = PayheroService; +exports.PayheroService = PayheroService = PayheroService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [config_1.ConfigService]) +], PayheroService); +//# sourceMappingURL=payhero.service.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/payhero.service.js.map b/apps/api/dist/billing/payhero.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..acbfe55bd6bd4a80e0f63515fef7c3eca5934eea --- /dev/null +++ b/apps/api/dist/billing/payhero.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"payhero.service.js","sourceRoot":"","sources":["../../src/billing/payhero.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/paystack-webhook.guard.js b/apps/api/dist/billing/paystack-webhook.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..c7b56dbdbd2e0862f1d0478c734713969b37559c --- /dev/null +++ b/apps/api/dist/billing/paystack-webhook.guard.js @@ -0,0 +1,82 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var PaystackWebhookGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PaystackWebhookGuard = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const crypto_1 = require("crypto"); +let PaystackWebhookGuard = PaystackWebhookGuard_1 = class PaystackWebhookGuard { + configService; + logger = new common_1.Logger(PaystackWebhookGuard_1.name); + secretKey; + isProduction; + constructor(configService) { + this.configService = configService; + this.secretKey = this.configService.get("PAYSTACK_SECRET_KEY") || ""; + this.isProduction = + this.configService.get("NODE_ENV") === "production"; + } + canActivate(context) { + if (!this.secretKey) { + if (this.isProduction) { + this.logger.error("PAYSTACK_SECRET_KEY is not configured in production — rejecting webhook."); + throw new common_1.UnauthorizedException("Paystack is not configured"); + } + this.logger.warn("PAYSTACK_SECRET_KEY is not configured — webhook signature verification skipped (dev only)."); + return true; + } + const request = context.switchToHttp().getRequest(); + const signatureHeader = request.headers["x-paystack-signature"] || undefined; + if (!signatureHeader) { + throw new common_1.UnauthorizedException("Missing Paystack signature header"); + } + const rawBody = this.extractRawBody(request); + if (!rawBody) { + throw new common_1.UnauthorizedException("Unable to read raw request body"); + } + const expected = (0, crypto_1.createHmac)("sha512", this.secretKey) + .update(rawBody) + .digest("hex"); + if (expected.length !== signatureHeader.length) { + throw new common_1.UnauthorizedException("Invalid Paystack signature"); + } + let mismatch = 0; + for (let i = 0; i < expected.length; i++) { + mismatch |= expected.charCodeAt(i) ^ signatureHeader.charCodeAt(i); + } + if (mismatch !== 0) { + throw new common_1.UnauthorizedException("Invalid Paystack signature"); + } + return true; + } + extractRawBody(request) { + if (typeof request.rawBody === "string") + return request.rawBody; + if (Buffer.isBuffer(request.rawBody)) + return request.rawBody.toString("utf8"); + if (request.body && typeof request.body === "object") { + try { + return JSON.stringify(request.body); + } + catch { + return undefined; + } + } + return undefined; + } +}; +exports.PaystackWebhookGuard = PaystackWebhookGuard; +exports.PaystackWebhookGuard = PaystackWebhookGuard = PaystackWebhookGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [config_1.ConfigService]) +], PaystackWebhookGuard); +//# sourceMappingURL=paystack-webhook.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/paystack-webhook.guard.js.map b/apps/api/dist/billing/paystack-webhook.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..b908605735fd66dcca0f50e6afca6ba372e0f0d2 --- /dev/null +++ b/apps/api/dist/billing/paystack-webhook.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"paystack-webhook.guard.js","sourceRoot":"","sources":["../../src/billing/paystack-webhook.guard.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/paystack.service.js b/apps/api/dist/billing/paystack.service.js new file mode 100644 index 0000000000000000000000000000000000000000..ee5db67eab04b8954789f5a85b8ca2f99fdbd0bf --- /dev/null +++ b/apps/api/dist/billing/paystack.service.js @@ -0,0 +1,311 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var PaystackService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PaystackService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +let PaystackService = PaystackService_1 = class PaystackService { + configService; + logger = new common_1.Logger(PaystackService_1.name); + secretKey; + publicKey; + baseUrl = 'https://api.paystack.co'; + callbackUrl; + constructor(configService) { + this.configService = configService; + this.secretKey = this.configService.get('PAYSTACK_SECRET_KEY') || ''; + this.publicKey = this.configService.get('PAYSTACK_PUBLIC_KEY') || ''; + this.callbackUrl = this.configService.get('PAYSTACK_CALLBACK_URL') || ''; + if (!this.secretKey) { + this.logger.warn('PAYSTACK_SECRET_KEY is not configured'); + } + } + isConfigured() { + return Boolean(this.secretKey); + } + getPublicKey() { + return this.publicKey; + } + getHeaders() { + return { + Authorization: `Bearer ${this.secretKey}`, + 'Content-Type': 'application/json', + }; + } + async initializeTransaction(input) { + if (!this.secretKey) { + throw new Error('Paystack is not configured'); + } + const body = { + email: input.email, + amount: Math.round(input.amount), + reference: input.reference, + }; + if (input.currency) + body.currency = input.currency; + if (input.callbackUrl) + body.callback_url = input.callbackUrl; + if (input.metadata) + body.metadata = JSON.stringify(input.metadata); + if (input.channels?.length) + body.channels = input.channels; + const response = await fetch(`${this.baseUrl}/transaction/initialize`, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify(body), + }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) { + this.logger.error(`Paystack initialize failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to initialize Paystack transaction'); + } + return { + authorizationUrl: payload.data.authorization_url, + accessCode: payload.data.access_code, + reference: payload.data.reference, + }; + } + async createMobileMoneyCharge(input) { + if (!this.secretKey) { + throw new Error('Paystack is not configured'); + } + const body = { + email: input.email, + amount: Math.round(input.amount), + reference: input.reference, + mobile_money: { + phone: this.normalizeMobileMoneyPhone(input.mobileMoney.phone), + provider: input.mobileMoney.provider, + }, + }; + if (input.currency) + body.currency = input.currency; + if (input.callbackUrl) + body.callback_url = input.callbackUrl; + if (input.metadata) + body.metadata = JSON.stringify(input.metadata); + const response = await fetch(`${this.baseUrl}/charge`, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify(body), + }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) { + this.logger.error(`Paystack mobile money charge failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to initiate Paystack mobile money charge'); + } + return { + status: payload.data.status, + reference: payload.data.reference, + displayMessage: payload.data.display_message, + providerReference: payload.data.provider_reference, + }; + } + async verifyTransaction(reference) { + if (!this.secretKey) { + throw new Error('Paystack is not configured'); + } + const response = await fetch(`${this.baseUrl}/transaction/verify/${encodeURIComponent(reference)}`, { method: 'GET', headers: this.getHeaders() }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) { + this.logger.error(`Paystack verify failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to verify Paystack transaction'); + } + return { + status: payload.data.status, + amount: payload.data.amount, + currency: payload.data.currency, + reference: payload.data.reference, + channel: payload.data.channel, + paidAt: payload.data.paid_at ?? null, + gatewayResponse: payload.data.gateway_response, + authorizationCode: payload.data.authorization?.authorization_code, + }; + } + verifyWebhookSignature(rawBody, signatureHeader) { + if (!this.secretKey || !signatureHeader) + return false; + const { createHmac } = require('crypto'); + const expected = createHmac('sha512', this.secretKey).update(rawBody).digest('hex'); + if (expected.length !== signatureHeader.length) + return false; + let mismatch = 0; + for (let i = 0; i < expected.length; i++) { + mismatch |= expected.charCodeAt(i) ^ signatureHeader.charCodeAt(i); + } + return mismatch === 0; + } + async createPlan(input) { + if (!this.secretKey) + throw new Error('Paystack is not configured'); + const body = { + name: input.name, + amount: Math.round(input.amount), + interval: input.interval, + }; + if (input.currency) + body.currency = input.currency; + if (input.description) + body.description = input.description; + if (input.planCode) + body.plan_code = input.planCode; + const response = await fetch(`${this.baseUrl}/plan`, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify(body), + }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) { + this.logger.error(`Paystack plan creation failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to create Paystack plan'); + } + return payload.data; + } + async fetchPlan(idOrCode) { + if (!this.secretKey) + throw new Error('Paystack is not configured'); + const response = await fetch(`${this.baseUrl}/plan/${encodeURIComponent(idOrCode)}`, { method: 'GET', headers: this.getHeaders() }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) + return null; + return payload.data; + } + async createSubscription(input) { + if (!this.secretKey) + throw new Error('Paystack is not configured'); + const body = { + customer: input.customerCode, + plan: input.planCode, + }; + if (input.authorizationCode) + body.authorization = input.authorizationCode; + if (input.startDate) + body.start_date = input.startDate; + const response = await fetch(`${this.baseUrl}/subscription`, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify(body), + }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) { + this.logger.error(`Paystack subscription creation failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to create Paystack subscription'); + } + return payload.data; + } + async fetchSubscription(idOrCode) { + if (!this.secretKey) + throw new Error('Paystack is not configured'); + const response = await fetch(`${this.baseUrl}/subscription/${encodeURIComponent(idOrCode)}`, { method: 'GET', headers: this.getHeaders() }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) + return null; + return payload.data; + } + async disableSubscription(subscriptionCode, token) { + if (!this.secretKey) + throw new Error('Paystack is not configured'); + const response = await fetch(`${this.baseUrl}/subscription/disable`, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify({ + code: subscriptionCode, + token, + }), + }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) { + this.logger.error(`Paystack subscription disable failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to disable Paystack subscription'); + } + return payload.data; + } + async enableSubscription(subscriptionCode, token) { + if (!this.secretKey) + throw new Error('Paystack is not configured'); + const response = await fetch(`${this.baseUrl}/subscription/enable`, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify({ + code: subscriptionCode, + token, + }), + }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) { + this.logger.error(`Paystack subscription enable failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to enable Paystack subscription'); + } + return payload.data; + } + async refundTransaction(reference, amountInSubunits) { + if (!this.secretKey) + throw new Error('Paystack is not configured'); + const body = { transaction: reference }; + if (typeof amountInSubunits === 'number') + body.amount = amountInSubunits; + const response = await fetch(`${this.baseUrl}/refund`, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify(body), + }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) { + this.logger.error(`Paystack refund failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to refund Paystack transaction'); + } + return payload.data; + } + async createCustomer(input) { + if (!this.secretKey) + throw new Error('Paystack is not configured'); + const body = { email: input.email }; + if (input.firstName) + body.first_name = input.firstName; + if (input.lastName) + body.last_name = input.lastName; + if (input.phone) + body.phone = input.phone; + if (input.metadata) + body.metadata = input.metadata; + const response = await fetch(`${this.baseUrl}/customer`, { + method: 'POST', + headers: this.getHeaders(), + body: JSON.stringify(body), + }); + const payload = (await response.json()); + if (!response.ok || !payload.status || !payload.data) { + this.logger.error(`Paystack customer creation failed: ${JSON.stringify(payload)}`); + throw new Error(payload.message || 'Failed to create Paystack customer'); + } + return payload.data; + } + normalizeMobileMoneyPhone(phone) { + const digits = phone.replace(/\D/g, ''); + if (digits.startsWith('254') && digits.length === 12) { + return `0${digits.slice(3)}`; + } + if (digits.startsWith('0') && digits.length === 10) { + return digits; + } + if (digits.length === 9) { + return `0${digits}`; + } + throw new Error('Invalid phone number. Use 07XX, 01XX, or 2547XXXXXXXX format.'); + } +}; +exports.PaystackService = PaystackService; +exports.PaystackService = PaystackService = PaystackService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [config_1.ConfigService]) +], PaystackService); +//# sourceMappingURL=paystack.service.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/paystack.service.js.map b/apps/api/dist/billing/paystack.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..84b575f7e1ac23f7d865637ec3137c483a5d5019 --- /dev/null +++ b/apps/api/dist/billing/paystack.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"paystack.service.js","sourceRoot":"","sources":["../../src/billing/paystack.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/stripe-webhook.guard.js b/apps/api/dist/billing/stripe-webhook.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..661ec1808702f7a8b6dc672cdc762f11ffba8c95 --- /dev/null +++ b/apps/api/dist/billing/stripe-webhook.guard.js @@ -0,0 +1,48 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var StripeWebhookGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.StripeWebhookGuard = void 0; +const common_1 = require("@nestjs/common"); +const stripe_service_1 = require("./stripe.service"); +let StripeWebhookGuard = StripeWebhookGuard_1 = class StripeWebhookGuard { + stripeService; + logger = new common_1.Logger(StripeWebhookGuard_1.name); + constructor(stripeService) { + this.stripeService = stripeService; + } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + const rawBody = request.rawBody; + const signature = request.headers['stripe-signature']; + if (!rawBody) { + this.logger.error('Missing raw body - ensure RawBodyMiddleware is applied'); + throw new common_1.UnauthorizedException('Missing raw body for webhook verification'); + } + if (!signature) { + this.logger.error('Missing Stripe signature header'); + throw new common_1.UnauthorizedException('Missing Stripe signature'); + } + const event = this.stripeService.verifyWebhookSignature(rawBody, signature); + if (!event) { + this.logger.error('Webhook signature verification failed'); + throw new common_1.UnauthorizedException('Invalid webhook signature'); + } + request.stripeEvent = event; + return true; + } +}; +exports.StripeWebhookGuard = StripeWebhookGuard; +exports.StripeWebhookGuard = StripeWebhookGuard = StripeWebhookGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [stripe_service_1.StripeService]) +], StripeWebhookGuard); +//# sourceMappingURL=stripe-webhook.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/stripe-webhook.guard.js.map b/apps/api/dist/billing/stripe-webhook.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..2bfd48e805b87a0dc4bffcd1c6caca26191ba453 --- /dev/null +++ b/apps/api/dist/billing/stripe-webhook.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"stripe-webhook.guard.js","sourceRoot":"","sources":["../../src/billing/stripe-webhook.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAA0G;AAE1G,qDAAiD;AAO1C,IAAM,kBAAkB,0BAAxB,MAAM,kBAAkB;IAGA;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,oBAAkB,CAAC,IAAI,CAAC,CAAC;IAE9D,YAA6B,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAE7D,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAW,CAAC;QAG7D,MAAM,OAAO,GAAI,OAAe,CAAC,OAAO,CAAC;QACzC,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAW,CAAC;QAEhE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;YAC5E,MAAM,IAAI,8BAAqB,CAAC,2CAA2C,CAAC,CAAC;QAC/E,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;YACrD,MAAM,IAAI,8BAAqB,CAAC,0BAA0B,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,sBAAsB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAE5E,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;YAC3D,MAAM,IAAI,8BAAqB,CAAC,2BAA2B,CAAC,CAAC;QAC/D,CAAC;QAGA,OAAe,CAAC,WAAW,GAAG,KAAK,CAAC;QAErC,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAlCY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;qCAIiC,8BAAa;GAH9C,kBAAkB,CAkC9B"} \ No newline at end of file diff --git a/apps/api/dist/billing/stripe.service.js b/apps/api/dist/billing/stripe.service.js new file mode 100644 index 0000000000000000000000000000000000000000..6a831a011eabeaf651349c4ebce634289f4dc759 --- /dev/null +++ b/apps/api/dist/billing/stripe.service.js @@ -0,0 +1,110 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var StripeService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.StripeService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const stripe_1 = __importDefault(require("stripe")); +let StripeService = StripeService_1 = class StripeService { + configService; + logger = new common_1.Logger(StripeService_1.name); + stripe = null; + webhookSecret; + constructor(configService) { + this.configService = configService; + const stripeSecretKey = this.configService.get('STRIPE_SECRET_KEY'); + this.webhookSecret = this.configService.get('STRIPE_WEBHOOK_SECRET') || ''; + if (!stripeSecretKey) { + this.logger.warn('STRIPE_SECRET_KEY is not configured'); + } + else { + this.stripe = new stripe_1.default(stripeSecretKey, { + apiVersion: '2026-09-30.endive', + }); + } + } + isConfigured() { + return Boolean(this.stripe); + } + verifyWebhookSignature(rawBody, signature) { + if (!this.stripe) { + this.logger.error('Stripe client not initialized (STRIPE_SECRET_KEY is missing)'); + return null; + } + if (!rawBody || !signature) { + this.logger.warn('Missing raw body or signature'); + return null; + } + try { + return this.stripe.webhooks.constructEvent(rawBody, signature, this.webhookSecret); + } + catch (error) { + this.logger.error('Webhook signature verification failed', error); + return null; + } + } + getStripeClient() { + if (!this.stripe) { + throw new Error('Stripe client not initialized (STRIPE_SECRET_KEY is missing)'); + } + return this.stripe; + } + async cancelSubscriptionAtPeriodEnd(stripeSubscriptionId) { + const client = this.getStripeClient(); + return client.subscriptions.update(stripeSubscriptionId, { + cancel_at_period_end: true, + }); + } + async cancelSubscriptionImmediately(stripeSubscriptionId) { + const client = this.getStripeClient(); + return client.subscriptions.cancel(stripeSubscriptionId); + } + async resumeSubscription(stripeSubscriptionId) { + const client = this.getStripeClient(); + return client.subscriptions.update(stripeSubscriptionId, { + cancel_at_period_end: false, + }); + } + async updateSubscriptionPrice(stripeSubscriptionId, newStripePriceId) { + const client = this.getStripeClient(); + const current = (await client.subscriptions.retrieve(stripeSubscriptionId)); + if (!current.items.data.length) { + throw new Error(`Stripe subscription ${stripeSubscriptionId} has no items`); + } + return client.subscriptions.update(stripeSubscriptionId, { + items: [ + { + id: current.items.data[0].id, + price: newStripePriceId, + }, + ], + proration_behavior: 'create_prorations', + }); + } + async refundCharge(stripeChargeId, amountInMinorUnits, reason = 'requested_by_customer') { + const client = this.getStripeClient(); + return client.refunds.create({ + charge: stripeChargeId, + ...(typeof amountInMinorUnits === 'number' ? { amount: amountInMinorUnits } : {}), + reason, + }); + } +}; +exports.StripeService = StripeService; +exports.StripeService = StripeService = StripeService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [config_1.ConfigService]) +], StripeService); +//# sourceMappingURL=stripe.service.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/stripe.service.js.map b/apps/api/dist/billing/stripe.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..66556b47292eda83efd12c0b6097310fe466a129 --- /dev/null +++ b/apps/api/dist/billing/stripe.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"stripe.service.js","sourceRoot":"","sources":["../../src/billing/stripe.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/subscription-credentials.service.js b/apps/api/dist/billing/subscription-credentials.service.js new file mode 100644 index 0000000000000000000000000000000000000000..b276aeb44ffb016af395b2e72133e0808b2a72ce --- /dev/null +++ b/apps/api/dist/billing/subscription-credentials.service.js @@ -0,0 +1,372 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var SubscriptionCredentialsService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SubscriptionCredentialsService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const platform_admin_1 = require("../common/platform-admin"); +const DEFAULT_TIER_ACCESS = { + hobby: [ + { provider: "google_calendar", isAvailable: true, credentialSource: "platform" }, + { provider: "google_mail", isAvailable: false, credentialSource: "workspace" }, + { provider: "outlook", isAvailable: false, credentialSource: "workspace" }, + { provider: "riverside", isAvailable: false, credentialSource: "workspace" }, + { provider: "zoom", isAvailable: false, credentialSource: "workspace" }, + { provider: "cloudflare_r2", isAvailable: false, credentialSource: "workspace" }, + { provider: "deepgram", isAvailable: false, credentialSource: "platform" }, + { provider: "nvidia_nim", isAvailable: false, credentialSource: "platform" }, + { provider: "deepseek", isAvailable: false, credentialSource: "platform" }, + { provider: "moonshot", isAvailable: false, credentialSource: "platform" }, + { provider: "calcom", isAvailable: false, credentialSource: "workspace" }, + { provider: "chatwoot", isAvailable: false, credentialSource: "workspace" }, + { provider: "documenso", isAvailable: false, credentialSource: "workspace" }, + { provider: "formbricks", isAvailable: false, credentialSource: "workspace" }, + { provider: "listmonk", isAvailable: false, credentialSource: "workspace" }, + ], + creator: [ + { provider: "google_calendar", isAvailable: true, credentialSource: "platform" }, + { provider: "google_mail", isAvailable: true, credentialSource: "platform" }, + { provider: "outlook", isAvailable: true, credentialSource: "platform" }, + { provider: "riverside", isAvailable: true, credentialSource: "workspace" }, + { provider: "zoom", isAvailable: true, credentialSource: "workspace" }, + { provider: "cloudflare_r2", isAvailable: true, credentialSource: "platform" }, + { provider: "deepgram", isAvailable: true, credentialSource: "platform" }, + { provider: "nvidia_nim", isAvailable: false, credentialSource: "platform" }, + { provider: "deepseek", isAvailable: false, credentialSource: "platform" }, + { provider: "moonshot", isAvailable: false, credentialSource: "platform" }, + { provider: "calcom", isAvailable: true, credentialSource: "workspace" }, + { provider: "chatwoot", isAvailable: false, credentialSource: "workspace" }, + { provider: "documenso", isAvailable: true, credentialSource: "workspace" }, + { provider: "formbricks", isAvailable: true, credentialSource: "workspace" }, + { provider: "listmonk", isAvailable: true, credentialSource: "workspace" }, + ], + studio: [ + { provider: "google_calendar", isAvailable: true, credentialSource: "platform" }, + { provider: "google_mail", isAvailable: true, credentialSource: "platform" }, + { provider: "outlook", isAvailable: true, credentialSource: "platform" }, + { provider: "riverside", isAvailable: true, credentialSource: "workspace" }, + { provider: "zoom", isAvailable: true, credentialSource: "workspace" }, + { provider: "cloudflare_r2", isAvailable: true, credentialSource: "platform" }, + { provider: "deepgram", isAvailable: true, credentialSource: "platform" }, + { provider: "nvidia_nim", isAvailable: true, credentialSource: "platform" }, + { provider: "deepseek", isAvailable: true, credentialSource: "platform" }, + { provider: "moonshot", isAvailable: true, credentialSource: "platform" }, + { provider: "calcom", isAvailable: true, credentialSource: "workspace" }, + { provider: "chatwoot", isAvailable: true, credentialSource: "workspace" }, + { provider: "documenso", isAvailable: true, credentialSource: "workspace" }, + { provider: "formbricks", isAvailable: true, credentialSource: "workspace" }, + { provider: "listmonk", isAvailable: true, credentialSource: "workspace" }, + ], +}; +let SubscriptionCredentialsService = SubscriptionCredentialsService_1 = class SubscriptionCredentialsService { + prisma; + logger = new common_1.Logger(SubscriptionCredentialsService_1.name); + constructor(prisma) { + this.prisma = prisma; + } + async resolveCredentials(workspaceId, provider) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { + id: true, + subscriptionPlanId: true, + subscriptionStatus: true, + }, + }); + if (!workspace) { + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + } + if (workspace.subscriptionStatus !== "active" && workspace.subscriptionStatus !== "pending") { + throw new common_1.ForbiddenException(`Workspace subscription is "${workspace.subscriptionStatus}". Active subscription required to use integrations.`); + } + const planId = workspace.subscriptionPlanId; + const tierAccess = await this.prisma.subscriptionTierIntegrationAccess.findUnique({ + where: { planId_provider: { planId, provider: provider } }, + }); + const accessConfig = tierAccess + ? { + isAvailable: tierAccess.isAvailable, + credentialSource: tierAccess.credentialSource, + } + : DEFAULT_TIER_ACCESS[planId]?.find((p) => p.provider === provider) ?? { + isAvailable: false, + credentialSource: "platform", + }; + if (!accessConfig.isAvailable) { + throw new common_1.ForbiddenException(`Integration "${provider}" is not available on the ${planId} plan. ` + + `Upgrade to a higher tier to unlock this integration.`); + } + if (accessConfig.credentialSource === "platform") { + return this.resolvePlatformCredential(provider); + } + return this.resolveWorkspaceCredential(workspaceId, provider); + } + async checkAccess(workspaceId, provider) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { + subscriptionPlanId: true, + integrations: { + where: { provider: provider }, + select: { status: true }, + take: 1, + }, + }, + }); + if (!workspace) { + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + } + const planId = workspace.subscriptionPlanId; + const tierAccess = await this.prisma.subscriptionTierIntegrationAccess.findUnique({ + where: { planId_provider: { planId, provider: provider } }, + }); + const accessConfig = tierAccess + ? { + isAvailable: tierAccess.isAvailable, + credentialSource: tierAccess.credentialSource, + } + : DEFAULT_TIER_ACCESS[planId]?.find((p) => p.provider === provider) ?? { + isAvailable: false, + credentialSource: "platform", + }; + const workspaceIntegration = workspace.integrations[0]; + const isConnected = accessConfig.credentialSource === "platform" + ? true + : workspaceIntegration?.status === "connected"; + return { + isAvailable: accessConfig.isAvailable, + credentialSource: accessConfig.credentialSource, + isConnected, + planId, + requiresUpgrade: !accessConfig.isAvailable, + }; + } + async getWorkspaceIntegrationOverview(workspaceId) { + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { + subscriptionPlanId: true, + integrations: { + select: { provider: true, status: true }, + }, + }, + }); + if (!workspace) { + throw new common_1.NotFoundException(`Workspace ${workspaceId} not found`); + } + const planId = workspace.subscriptionPlanId; + const tierAccessRows = await this.prisma.subscriptionTierIntegrationAccess.findMany({ + where: { planId }, + }); + const allProviders = [ + "google_calendar", + "google_mail", + "outlook", + "riverside", + "zoom", + "cloudflare_r2", + "deepgram", + "nvidia_nim", + "deepseek", + "moonshot", + "calcom", + "chatwoot", + "documenso", + "formbricks", + "listmonk", + ]; + const integrationMap = new Map(workspace.integrations.map((i) => [i.provider, i.status])); + return allProviders.map((provider) => { + const tierAccess = tierAccessRows.find((r) => r.provider === provider); + const accessConfig = tierAccess + ? { + isAvailable: tierAccess.isAvailable, + credentialSource: tierAccess.credentialSource, + } + : DEFAULT_TIER_ACCESS[planId]?.find((p) => p.provider === provider) ?? { + isAvailable: false, + credentialSource: "platform", + }; + const integrationStatus = integrationMap.get(provider); + const isConnected = accessConfig.credentialSource === "platform" + ? accessConfig.isAvailable + : integrationStatus === "connected"; + return { + provider, + isAvailable: accessConfig.isAvailable, + credentialSource: accessConfig.credentialSource, + isConnected, + requiresUpgrade: !accessConfig.isAvailable, + planId, + }; + }); + } + async upsertTierAccess(userId, input) { + await this.ensureAdmin(userId); + const result = await this.prisma.subscriptionTierIntegrationAccess.upsert({ + where: { + planId_provider: { planId: input.planId, provider: input.provider }, + }, + create: { + planId: input.planId, + provider: input.provider, + isAvailable: input.isAvailable, + credentialSource: input.credentialSource, + }, + update: { + isAvailable: input.isAvailable, + credentialSource: input.credentialSource, + }, + }); + this.logger.log(`Updated tier access: ${input.planId}/${input.provider} → available=${input.isAvailable}, source=${input.credentialSource}`); + return result; + } + async listTierAccess(userId, planId) { + await this.ensureAdmin(userId); + const where = planId ? { planId: planId } : {}; + return this.prisma.subscriptionTierIntegrationAccess.findMany({ + where, + orderBy: [{ planId: "asc" }, { provider: "asc" }], + }); + } + async upsertPlatformCredential(userId, input) { + await this.ensureAdmin(userId); + const result = await this.prisma.platformIntegrationCredential.upsert({ + where: { provider: input.provider }, + create: { + provider: input.provider, + accessToken: input.accessToken ?? null, + refreshToken: input.refreshToken ?? null, + apiKey: input.apiKey ?? null, + apiSecret: input.apiSecret ?? null, + endpointUrl: input.endpointUrl ?? null, + scopes: input.scopes ?? [], + metadata: (input.metadata ?? {}), + isActive: input.isActive ?? true, + }, + update: { + ...(input.accessToken !== undefined ? { accessToken: input.accessToken } : {}), + ...(input.refreshToken !== undefined ? { refreshToken: input.refreshToken } : {}), + ...(input.apiKey !== undefined ? { apiKey: input.apiKey } : {}), + ...(input.apiSecret !== undefined ? { apiSecret: input.apiSecret } : {}), + ...(input.endpointUrl !== undefined ? { endpointUrl: input.endpointUrl } : {}), + ...(input.scopes !== undefined ? { scopes: input.scopes } : {}), + ...(input.metadata !== undefined ? { metadata: input.metadata } : {}), + ...(input.isActive !== undefined ? { isActive: input.isActive } : {}), + lastValidatedAt: new Date(), + }, + }); + this.logger.log(`Updated platform credentials for ${input.provider}`); + return { + provider: result.provider, + isActive: result.isActive, + hasAccessToken: !!result.accessToken, + hasRefreshToken: !!result.refreshToken, + hasApiKey: !!result.apiKey, + hasApiSecret: !!result.apiSecret, + scopes: result.scopes, + lastValidatedAt: result.lastValidatedAt, + }; + } + async listPlatformCredentials(userId) { + await this.ensureAdmin(userId); + const credentials = await this.prisma.platformIntegrationCredential.findMany({ + orderBy: { provider: "asc" }, + }); + return credentials.map((c) => ({ + provider: c.provider, + isActive: c.isActive, + hasAccessToken: !!c.accessToken, + hasRefreshToken: !!c.refreshToken, + hasApiKey: !!c.apiKey, + hasApiSecret: !!c.apiSecret, + endpointUrl: c.endpointUrl, + scopes: c.scopes, + lastValidatedAt: c.lastValidatedAt, + createdAt: c.createdAt, + updatedAt: c.updatedAt, + })); + } + async deletePlatformCredential(userId, provider) { + await this.ensureAdmin(userId); + await this.prisma.platformIntegrationCredential.deleteMany({ + where: { provider: provider }, + }); + this.logger.log(`Deleted platform credentials for ${provider}`); + return { success: true }; + } + async resolvePlatformCredential(provider) { + const credential = await this.prisma.platformIntegrationCredential.findUnique({ + where: { provider: provider }, + }); + if (!credential || !credential.isActive) { + throw new common_1.BadRequestException(`Platform credentials for "${provider}" are not configured. ` + + `Contact support or check your subscription tier settings.`); + } + const hasCredential = !!(credential.accessToken || + credential.apiKey || + credential.apiSecret); + if (!hasCredential) { + throw new common_1.BadRequestException(`Platform credentials for "${provider}" exist but are empty. ` + + `The platform administrator needs to configure them.`); + } + return { + source: "platform", + provider, + accessToken: credential.accessToken, + refreshToken: credential.refreshToken, + tokenExpiresAt: credential.tokenExpiresAt, + apiKey: credential.apiKey, + apiSecret: credential.apiSecret, + endpointUrl: credential.endpointUrl, + scopes: credential.scopes, + metadata: credential.metadata ?? {}, + }; + } + async resolveWorkspaceCredential(workspaceId, provider) { + const integration = await this.prisma.workspaceIntegration.findUnique({ + where: { + workspaceId_provider: { workspaceId, provider: provider }, + }, + }); + if (!integration || integration.status !== "connected") { + throw new common_1.BadRequestException(`"${provider}" is not connected for this workspace. ` + + `Please connect it in Settings > Integrations.`); + } + if (integration.tokenExpiresAt && + new Date() >= integration.tokenExpiresAt && + integration.refreshToken) { + this.logger.warn(`Token for ${provider} in workspace ${workspaceId} has expired. Refresh required.`); + } + return { + source: "workspace", + provider, + accessToken: integration.accessToken, + refreshToken: integration.refreshToken, + tokenExpiresAt: integration.tokenExpiresAt, + apiKey: null, + apiSecret: null, + endpointUrl: null, + scopes: integration.scopes, + metadata: integration.metadata ?? {}, + }; + } + async ensureAdmin(userId) { + await (0, platform_admin_1.assertPlatformAdmin)(this.prisma, userId); + } +}; +exports.SubscriptionCredentialsService = SubscriptionCredentialsService; +exports.SubscriptionCredentialsService = SubscriptionCredentialsService = SubscriptionCredentialsService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], SubscriptionCredentialsService); +//# sourceMappingURL=subscription-credentials.service.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/subscription-credentials.service.js.map b/apps/api/dist/billing/subscription-credentials.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..25c4804899fa7c3c3440de0f37183ec14f107977 --- /dev/null +++ b/apps/api/dist/billing/subscription-credentials.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"subscription-credentials.service.js","sourceRoot":"","sources":["../../src/billing/subscription-credentials.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/subscription-features.js b/apps/api/dist/billing/subscription-features.js new file mode 100644 index 0000000000000000000000000000000000000000..f41c0de9093703972eb1c09b00cfbd3ebcafc60b --- /dev/null +++ b/apps/api/dist/billing/subscription-features.js @@ -0,0 +1,147 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FEATURE_DESCRIPTIONS = exports.PLAN_LIMITS = exports.PLAN_FEATURES = exports.SubscriptionFeature = void 0; +exports.planHasFeature = planHasFeature; +exports.getPlanLimit = getPlanLimit; +exports.isUnlimited = isUnlimited; +exports.getPlanFeatures = getPlanFeatures; +exports.SubscriptionFeature = { + BASIC_PORTAL: "basic_portal", + MANUAL_OUTREACH: "manual_outreach", + GUEST_LIMIT_5: "guest_limit_5", + WORKSPACE_LIMIT_1: "workspace_limit_1", + STANDARD_SUPPORT: "standard_support", + UNLIMITED_GUESTS: "unlimited_guests", + AUTOMATED_OUTREACH: "automated_outreach", + BRANDED_PORTAL: "branded_portal", + ASSET_FORMS: "asset_forms", + WORKSPACE_LIMIT_3: "workspace_limit_3", + PRIORITY_EMAIL_SUPPORT: "priority_email_support", + CUSTOM_BRANDING: "custom_branding", + SELF_HOSTED_INTEGRATIONS: "self_hosted_integrations", + EMAIL_MARKETING: "email_marketing", + VIRALCLIP_ENGINE: "viralclip_engine", + MULTI_PLATFORM_EXPORT: "multi_platform_export", + API_WEBHOOKS: "api_webhooks", + ADVANCED_ANALYTICS: "advanced_analytics", + UNLIMITED_WORKSPACES: "unlimited_workspaces", + PRIORITY_24_7_SUPPORT: "priority_24_7_support", + AI_CLIP_DETECTION: "ai_clip_detection", + BULK_EXPORT: "bulk_export", + LIVEKIT_RECORDING: "livekit_recording", + DOCUMENT_MANAGEMENT: "document_management", + DOCUMENT_SECURITY_POLICY: "document_security_policy", +}; +exports.PLAN_FEATURES = { + hobby: [ + exports.SubscriptionFeature.BASIC_PORTAL, + exports.SubscriptionFeature.MANUAL_OUTREACH, + exports.SubscriptionFeature.GUEST_LIMIT_5, + exports.SubscriptionFeature.WORKSPACE_LIMIT_1, + exports.SubscriptionFeature.STANDARD_SUPPORT, + ], + creator: [ + exports.SubscriptionFeature.BASIC_PORTAL, + exports.SubscriptionFeature.MANUAL_OUTREACH, + exports.SubscriptionFeature.UNLIMITED_GUESTS, + exports.SubscriptionFeature.AUTOMATED_OUTREACH, + exports.SubscriptionFeature.BRANDED_PORTAL, + exports.SubscriptionFeature.ASSET_FORMS, + exports.SubscriptionFeature.WORKSPACE_LIMIT_3, + exports.SubscriptionFeature.PRIORITY_EMAIL_SUPPORT, + exports.SubscriptionFeature.CUSTOM_BRANDING, + exports.SubscriptionFeature.SELF_HOSTED_INTEGRATIONS, + exports.SubscriptionFeature.EMAIL_MARKETING, + exports.SubscriptionFeature.GUEST_LIMIT_5, + exports.SubscriptionFeature.WORKSPACE_LIMIT_1, + exports.SubscriptionFeature.STANDARD_SUPPORT, + ], + studio: [ + exports.SubscriptionFeature.BASIC_PORTAL, + exports.SubscriptionFeature.MANUAL_OUTREACH, + exports.SubscriptionFeature.UNLIMITED_GUESTS, + exports.SubscriptionFeature.AUTOMATED_OUTREACH, + exports.SubscriptionFeature.BRANDED_PORTAL, + exports.SubscriptionFeature.ASSET_FORMS, + exports.SubscriptionFeature.UNLIMITED_WORKSPACES, + exports.SubscriptionFeature.PRIORITY_24_7_SUPPORT, + exports.SubscriptionFeature.CUSTOM_BRANDING, + exports.SubscriptionFeature.VIRALCLIP_ENGINE, + exports.SubscriptionFeature.MULTI_PLATFORM_EXPORT, + exports.SubscriptionFeature.API_WEBHOOKS, + exports.SubscriptionFeature.ADVANCED_ANALYTICS, + exports.SubscriptionFeature.AI_CLIP_DETECTION, + exports.SubscriptionFeature.BULK_EXPORT, + exports.SubscriptionFeature.SELF_HOSTED_INTEGRATIONS, + exports.SubscriptionFeature.EMAIL_MARKETING, + exports.SubscriptionFeature.LIVEKIT_RECORDING, + exports.SubscriptionFeature.DOCUMENT_MANAGEMENT, + exports.SubscriptionFeature.DOCUMENT_SECURITY_POLICY, + ], +}; +exports.PLAN_LIMITS = { + hobby: { + maxGuestsPerMonth: 5, + maxWorkspaces: 1, + maxEpisodesPerMonth: 10, + maxClipsPerMonth: 0, + maxExportMinutesPerMonth: 0, + maxApiCallsPerMonth: 0, + }, + creator: { + maxGuestsPerMonth: null, + maxWorkspaces: 3, + maxEpisodesPerMonth: null, + maxClipsPerMonth: 50, + maxExportMinutesPerMonth: 120, + maxApiCallsPerMonth: 10000, + }, + studio: { + maxGuestsPerMonth: null, + maxWorkspaces: null, + maxEpisodesPerMonth: null, + maxClipsPerMonth: null, + maxExportMinutesPerMonth: null, + maxApiCallsPerMonth: null, + }, +}; +function planHasFeature(planId, feature) { + return exports.PLAN_FEATURES[planId]?.includes(feature) ?? false; +} +function getPlanLimit(planId, limitKey) { + return exports.PLAN_LIMITS[planId]?.[limitKey] ?? null; +} +function isUnlimited(planId, limitKey) { + return getPlanLimit(planId, limitKey) === null; +} +function getPlanFeatures(planId) { + return exports.PLAN_FEATURES[planId] ?? []; +} +exports.FEATURE_DESCRIPTIONS = { + [exports.SubscriptionFeature.BASIC_PORTAL]: "Basic booking portal for guests", + [exports.SubscriptionFeature.MANUAL_OUTREACH]: "Manual email outreach to guests", + [exports.SubscriptionFeature.GUEST_LIMIT_5]: "Up to 5 new guests per month", + [exports.SubscriptionFeature.WORKSPACE_LIMIT_1]: "1 podcast workspace", + [exports.SubscriptionFeature.STANDARD_SUPPORT]: "Standard email support", + [exports.SubscriptionFeature.UNLIMITED_GUESTS]: "Unlimited guests per month", + [exports.SubscriptionFeature.AUTOMATED_OUTREACH]: "Automated multi-step outreach sequences", + [exports.SubscriptionFeature.BRANDED_PORTAL]: "Custom branded guest portal", + [exports.SubscriptionFeature.ASSET_FORMS]: "Asset collection forms for guests", + [exports.SubscriptionFeature.WORKSPACE_LIMIT_3]: "Up to 3 podcast workspaces", + [exports.SubscriptionFeature.PRIORITY_EMAIL_SUPPORT]: "Priority email support", + [exports.SubscriptionFeature.CUSTOM_BRANDING]: "Custom colors, logo, domain", + [exports.SubscriptionFeature.VIRALCLIP_ENGINE]: "AI-powered viral clip detection", + [exports.SubscriptionFeature.MULTI_PLATFORM_EXPORT]: "Export to TikTok, Reels, Shorts, LinkedIn, X", + [exports.SubscriptionFeature.API_WEBHOOKS]: "REST API & webhook access", + [exports.SubscriptionFeature.ADVANCED_ANALYTICS]: "Advanced analytics & cohort reports", + [exports.SubscriptionFeature.UNLIMITED_WORKSPACES]: "Unlimited podcast workspaces", + [exports.SubscriptionFeature.PRIORITY_24_7_SUPPORT]: "24/7 priority support", + [exports.SubscriptionFeature.AI_CLIP_DETECTION]: "AI clip detection & scoring", + [exports.SubscriptionFeature.BULK_EXPORT]: "Bulk export multiple clips", + [exports.SubscriptionFeature.SELF_HOSTED_INTEGRATIONS]: "Connect self-hosted tools (Cal.com, Chatwoot, Documenso, Formbricks, Listmonk)", + [exports.SubscriptionFeature.EMAIL_MARKETING]: "Email marketing campaigns & newsletter automation", + [exports.SubscriptionFeature.LIVEKIT_RECORDING]: "LiveKit-powered real-time recording studio", + [exports.SubscriptionFeature.DOCUMENT_MANAGEMENT]: "Document management, sharing & tracking (upload, shares, access logs)", + [exports.SubscriptionFeature.DOCUMENT_SECURITY_POLICY]: "Security policy controls (sanction screening, forgery detection, admin takedown)", +}; +//# sourceMappingURL=subscription-features.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/subscription-features.js.map b/apps/api/dist/billing/subscription-features.js.map new file mode 100644 index 0000000000000000000000000000000000000000..e8f0a24f311462af2bd66c26736999882980b759 --- /dev/null +++ b/apps/api/dist/billing/subscription-features.js.map @@ -0,0 +1 @@ +{"version":3,"file":"subscription-features.js","sourceRoot":"","sources":["../../src/billing/subscription-features.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/billing/subscription.guard.js b/apps/api/dist/billing/subscription.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..8a43f17e5f18f8ee4b3e0daed23aa7592c5ece9b --- /dev/null +++ b/apps/api/dist/billing/subscription.guard.js @@ -0,0 +1,146 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var SubscriptionGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SubscriptionGuard = exports.RequireLimit = exports.RequireActiveSubscription = exports.RequireFeature = exports.LIMIT_CHECK_KEY = exports.ACTIVE_SUBSCRIPTION_KEY = exports.FEATURE_KEY = void 0; +const common_1 = require("@nestjs/common"); +const core_1 = require("@nestjs/core"); +const prisma_service_1 = require("../prisma/prisma.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +const subscription_features_1 = require("./subscription-features"); +exports.FEATURE_KEY = "subscription_feature"; +exports.ACTIVE_SUBSCRIPTION_KEY = "active_subscription_required"; +exports.LIMIT_CHECK_KEY = "subscription_limit_check"; +const RequireFeature = (feature) => (0, common_1.applyDecorators)((0, common_1.SetMetadata)(exports.FEATURE_KEY, feature)); +exports.RequireFeature = RequireFeature; +const RequireActiveSubscription = () => (0, common_1.applyDecorators)((0, common_1.SetMetadata)(exports.ACTIVE_SUBSCRIPTION_KEY, true)); +exports.RequireActiveSubscription = RequireActiveSubscription; +const RequireLimit = (limitKey, label) => (0, common_1.applyDecorators)((0, common_1.SetMetadata)(exports.LIMIT_CHECK_KEY, { limitKey, label })); +exports.RequireLimit = RequireLimit; +let SubscriptionGuard = SubscriptionGuard_1 = class SubscriptionGuard { + prisma; + reflector; + logger = new common_1.Logger(SubscriptionGuard_1.name); + constructor(prisma, reflector) { + this.prisma = prisma; + this.reflector = reflector; + } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + const workspaceId = await this.resolveWorkspaceId(request); + if (!workspaceId) { + return true; + } + const workspace = await this.prisma.workspace.findUnique({ + where: { id: workspaceId }, + select: { + subscriptionPlanId: true, + subscriptionStatus: true, + }, + }); + if (!workspace) { + throw new common_1.ForbiddenException("Workspace not found"); + } + const planId = workspace.subscriptionPlanId || + shared_types_1.SubscriptionPlanId.HOBBY; + const status = workspace.subscriptionStatus || + shared_types_1.SubscriptionStatus.ACTIVE; + request.workspaceId = workspaceId; + request.subscriptionPlanId = planId; + request.subscriptionStatus = status; + const requireActive = this.reflector.getAllAndOverride(exports.ACTIVE_SUBSCRIPTION_KEY, [context.getHandler(), context.getClass()]); + if (requireActive) { + if (status === shared_types_1.SubscriptionStatus.PAST_DUE || + status === shared_types_1.SubscriptionStatus.CANCELED || + status === shared_types_1.SubscriptionStatus.INCOMPLETE) { + throw new common_1.ForbiddenException("Your subscription is not active. Please update your payment method or upgrade your plan."); + } + } + const requiredFeature = this.reflector.getAllAndOverride(exports.FEATURE_KEY, [context.getHandler(), context.getClass()]); + if (requiredFeature) { + const isActive = status === shared_types_1.SubscriptionStatus.ACTIVE || + status === shared_types_1.SubscriptionStatus.TRIALING; + const effectivePlan = isActive ? planId : shared_types_1.SubscriptionPlanId.HOBBY; + if (!(0, subscription_features_1.planHasFeature)(effectivePlan, requiredFeature)) { + throw new common_1.ForbiddenException(`This feature requires a higher subscription plan. ` + + `The "${requiredFeature}" feature is not available on the ${effectivePlan} plan. ` + + `Upgrade your plan to access this feature.`); + } + } + const limitCheck = this.reflector.getAllAndOverride(exports.LIMIT_CHECK_KEY, [context.getHandler(), context.getClass()]); + if (limitCheck) { + const isActive = status === shared_types_1.SubscriptionStatus.ACTIVE || + status === shared_types_1.SubscriptionStatus.TRIALING; + const effectivePlan = isActive ? planId : shared_types_1.SubscriptionPlanId.HOBBY; + if (!(0, subscription_features_1.isUnlimited)(effectivePlan, limitCheck.limitKey)) { + const max = (0, subscription_features_1.getPlanLimit)(effectivePlan, limitCheck.limitKey); + if (max !== null && max === 0) { + throw new common_1.ForbiddenException(`${limitCheck.label} are not included in the ${effectivePlan} plan. ` + + `Upgrade your plan to access ${limitCheck.label}.`); + } + } + } + return true; + } + async resolveWorkspaceId(request) { + if (request.workspaceId) { + return request.workspaceId; + } + const routeParams = request.params; + if (routeParams?.workspaceId && typeof routeParams.workspaceId === "string") { + return routeParams.workspaceId; + } + if (routeParams?.podcastId && typeof routeParams.podcastId === "string") { + const podcast = await this.prisma.podcast.findUnique({ + where: { id: routeParams.podcastId }, + select: { workspaceId: true }, + }); + if (podcast) { + return podcast.workspaceId; + } + } + if (routeParams?.trackId && typeof routeParams.trackId === "string") { + const track = await this.prisma.recordingTrack.findUnique({ + where: { id: routeParams.trackId }, + select: { + session: { + select: { + episode: { + select: { + podcast: { select: { workspaceId: true } }, + }, + }, + }, + }, + }, + }); + if (track?.session?.episode?.podcast?.workspaceId) { + return track.session.episode.podcast.workspaceId; + } + } + const body = request.body; + if (body?.workspaceId) { + return body.workspaceId; + } + const query = request.query; + if (query?.workspaceId && typeof query.workspaceId === "string") { + return query.workspaceId; + } + return null; + } +}; +exports.SubscriptionGuard = SubscriptionGuard; +exports.SubscriptionGuard = SubscriptionGuard = SubscriptionGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + core_1.Reflector]) +], SubscriptionGuard); +//# sourceMappingURL=subscription.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/billing/subscription.guard.js.map b/apps/api/dist/billing/subscription.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..2475d15ec1c0eed9945b2e7b2eab29b67c1a7dd1 --- /dev/null +++ b/apps/api/dist/billing/subscription.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"subscription.guard.js","sourceRoot":"","sources":["../../src/billing/subscription.guard.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/chat/chat.module.js b/apps/api/dist/chat/chat.module.js new file mode 100644 index 0000000000000000000000000000000000000000..0df740057a56da8f6e21a763c3eddaf70bc01250 --- /dev/null +++ b/apps/api/dist/chat/chat.module.js @@ -0,0 +1,35 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChatModule = void 0; +const common_1 = require("@nestjs/common"); +const axios_1 = require("@nestjs/axios"); +const chat_service_1 = require("./chat.service"); +const chat_router_1 = require("./chat.router"); +const realtime_client_1 = require("./realtime.client"); +const internal_realtime_controller_1 = require("./internal-realtime.controller"); +const media_comments_service_1 = require("./media-comments.service"); +const media_comments_router_1 = require("./media-comments.router"); +let ChatModule = class ChatModule { +}; +exports.ChatModule = ChatModule; +exports.ChatModule = ChatModule = __decorate([ + (0, common_1.Module)({ + imports: [axios_1.HttpModule], + controllers: [internal_realtime_controller_1.InternalRealtimeController], + providers: [ + chat_service_1.ChatService, + chat_router_1.ChatRouter, + realtime_client_1.RealtimeClient, + media_comments_service_1.MediaCommentsService, + media_comments_router_1.MediaCommentsRouter, + ], + exports: [chat_service_1.ChatService, realtime_client_1.RealtimeClient, media_comments_service_1.MediaCommentsService, chat_router_1.ChatRouter, media_comments_router_1.MediaCommentsRouter], + }) +], ChatModule); +//# sourceMappingURL=chat.module.js.map \ No newline at end of file diff --git a/apps/api/dist/chat/chat.module.js.map b/apps/api/dist/chat/chat.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..51983b1988638c5d3fa9cf87c19eebb4516ec91f --- /dev/null +++ b/apps/api/dist/chat/chat.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"chat.module.js","sourceRoot":"","sources":["../../src/chat/chat.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,yCAA2C;AAE3C,iDAA6C;AAC7C,+CAA2C;AAC3C,uDAAmD;AACnD,iFAA4E;AAC5E,qEAAgE;AAChE,mEAA8D;AAcvD,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IAZtB,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,kBAAU,CAAC;QACrB,WAAW,EAAE,CAAC,yDAA0B,CAAC;QACzC,SAAS,EAAE;YACP,0BAAW;YACX,wBAAU;YACV,gCAAc;YACd,6CAAoB;YACpB,2CAAmB;SACtB;QACD,OAAO,EAAE,CAAC,0BAAW,EAAE,gCAAc,EAAE,6CAAoB,EAAE,wBAAU,EAAE,2CAAmB,CAAC;KAChG,CAAC;GACW,UAAU,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/chat/chat.router.js b/apps/api/dist/chat/chat.router.js new file mode 100644 index 0000000000000000000000000000000000000000..6b6bae4b357e9813c86283ed6049aadd9eedff02 --- /dev/null +++ b/apps/api/dist/chat/chat.router.js @@ -0,0 +1,140 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChatRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const shared_types_1 = require("@micprotocol/shared-types"); +const trpc_init_1 = require("../trpc/trpc.init"); +const chat_service_1 = require("./chat.service"); +const realtime_client_1 = require("./realtime.client"); +const prisma_service_1 = require("../prisma/prisma.service"); +const conversationTypeSchema = zod_1.z.nativeEnum(shared_types_1.ConversationType); +const memberRoleSchema = zod_1.z.nativeEnum(shared_types_1.ConversationMemberRole); +let ChatRouter = class ChatRouter { + chat; + realtime; + prisma; + constructor(chat, realtime, prisma) { + this.chat = chat; + this.realtime = realtime; + this.prisma = prisma; + } + get router() { + return (0, trpc_init_1.router)({ + listConversations: trpc_init_1.workspaceProcedure.query(async ({ ctx }) => { + if (!ctx.userId || !ctx.workspaceId) { + return []; + } + return this.chat.listConversations(ctx.workspaceId, ctx.userId); + }), + ensureConversation: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + type: conversationTypeSchema, + podcastId: zod_1.z.string().uuid().optional(), + episodeId: zod_1.z.string().uuid().optional(), + guestId: zod_1.z.string().uuid().optional(), + title: zod_1.z.string().min(1).max(200).optional(), + })) + .mutation(async ({ ctx, input }) => { + return this.chat.getOrCreateConversation({ + workspaceId: ctx.workspaceId, + userId: ctx.userId, + type: input.type, + podcastId: input.podcastId, + episodeId: input.episodeId, + guestId: input.guestId, + title: input.title, + }); + }), + listMessages: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + conversationId: zod_1.z.string().uuid(), + cursor: zod_1.z.number().int().nonnegative().optional(), + limit: zod_1.z.number().int().min(1).max(100).default(50), + })) + .query(async ({ ctx, input }) => { + await this.assertMember(input.conversationId, ctx.userId); + return this.chat.listMessages(input.conversationId, input.cursor, input.limit); + }), + sendMessage: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + conversationId: zod_1.z.string().uuid(), + body: zod_1.z.string().min(1).max(8_000), + replyToId: zod_1.z.string().uuid().optional(), + metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(), + })) + .mutation(async ({ ctx, input }) => { + await this.assertMember(input.conversationId, ctx.userId); + return this.chat.postMessage({ + conversationId: input.conversationId, + userId: ctx.userId, + body: input.body, + replyToId: input.replyToId, + metadata: input.metadata, + }); + }), + react: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + messageId: zod_1.z.string().uuid(), + emoji: zod_1.z.string().min(1).max(8), + })) + .mutation(async ({ ctx, input }) => { + return this.chat.addReaction(input.messageId, ctx.userId, input.emoji); + }), + markRead: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + conversationId: zod_1.z.string().uuid(), + seq: zod_1.z.number().int().nonnegative(), + })) + .mutation(async ({ ctx, input }) => { + await this.assertMember(input.conversationId, ctx.userId); + return this.chat.markRead(input.conversationId, ctx.userId, input.seq); + }), + realtimeToken: trpc_init_1.protectedProcedure.query(async ({ ctx }) => { + const token = await this.realtime.issueToken(ctx.userId, ctx.workspaceId, ctx.workspaceRole ?? "member"); + return { + token, + publicUrl: process.env.NEXT_PUBLIC_MIC_REALTIME_URL ?? + process.env.MIC_REALTIME_PUBLIC_URL ?? + "ws://localhost:4001", + ttlSeconds: 60 * 60, + }; + }), + addMember: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + conversationId: zod_1.z.string().uuid(), + userId: zod_1.z.string().uuid(), + role: memberRoleSchema.default(shared_types_1.ConversationMemberRole.MEMBER), + })) + .mutation(async ({ input }) => { + return this.chat.ensureMember(input.conversationId, input.userId, input.role); + }), + }); + } + async assertMember(conversationId, userId) { + const m = await this.prisma.conversationMember.findUnique({ + where: { conversationId_userId: { conversationId, userId } }, + select: { id: true }, + }); + if (!m) { + throw new Error("Forbidden: not a member of this conversation"); + } + } +}; +exports.ChatRouter = ChatRouter; +exports.ChatRouter = ChatRouter = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [chat_service_1.ChatService, + realtime_client_1.RealtimeClient, + prisma_service_1.PrismaService]) +], ChatRouter); +//# sourceMappingURL=chat.router.js.map \ No newline at end of file diff --git a/apps/api/dist/chat/chat.router.js.map b/apps/api/dist/chat/chat.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..16a8fb604f3a81c26ef4bdc7ea926172053ca696 --- /dev/null +++ b/apps/api/dist/chat/chat.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"chat.router.js","sourceRoot":"","sources":["../../src/chat/chat.router.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6BAAwB;AACxB,4DAGmC;AAEnC,iDAAmF;AACnF,iDAA6C;AAC7C,uDAAmD;AACnD,6DAAyD;AAEzD,MAAM,sBAAsB,GAAG,OAAC,CAAC,UAAU,CAAC,+BAAgB,CAAC,CAAC;AAC9D,MAAM,gBAAgB,GAAG,OAAC,CAAC,UAAU,CAAC,qCAAsB,CAAC,CAAC;AAGvD,IAAM,UAAU,GAAhB,MAAM,UAAU;IAEE;IACA;IACA;IAHrB,YACqB,IAAiB,EACjB,QAAwB,EACxB,MAAqB;QAFrB,SAAI,GAAJ,IAAI,CAAa;QACjB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAe;IACvC,CAAC;IAEJ,IAAI,MAAM;QACN,OAAO,IAAA,kBAAM,EAAC;YAKV,iBAAiB,EAAE,8BAAkB,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBAC1D,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;oBAClC,OAAO,EAAE,CAAC;gBACd,CAAC;gBACD,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACpE,CAAC,CAAC;YAMF,kBAAkB,EAAE,8BAAkB;iBACjC,KAAK,CACF,OAAC,CAAC,MAAM,CAAC;gBACL,IAAI,EAAE,sBAAsB;gBAC5B,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;gBACvC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;gBACvC,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;gBACrC,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;aAC/C,CAAC,CACL;iBACA,QAAQ,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,uBAAuB,CAAC;oBACrC,WAAW,EAAE,GAAG,CAAC,WAAY;oBAC7B,MAAM,EAAE,GAAG,CAAC,MAAO;oBACnB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,KAAK,EAAE,KAAK,CAAC,KAAK;iBACrB,CAAC,CAAC;YACP,CAAC,CAAC;YAON,YAAY,EAAE,8BAAkB;iBAC3B,KAAK,CACF,OAAC,CAAC,MAAM,CAAC;gBACL,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;gBACjC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;gBACjD,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;aACtD,CAAC,CACL;iBACA,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC5B,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,MAAO,CAAC,CAAC;gBAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,CACzB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,KAAK,CACd,CAAC;YACN,CAAC,CAAC;YAQN,WAAW,EAAE,8BAAkB;iBAC1B,KAAK,CACF,OAAC,CAAC,MAAM,CAAC;gBACL,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;gBACjC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;gBAClC,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;gBACvC,QAAQ,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;aACzD,CAAC,CACL;iBACA,QAAQ,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC/B,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,MAAO,CAAC,CAAC;gBAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;oBACzB,cAAc,EAAE,KAAK,CAAC,cAAc;oBACpC,MAAM,EAAE,GAAG,CAAC,MAAO;oBACnB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;iBAC3B,CAAC,CAAC;YACP,CAAC,CAAC;YAKN,KAAK,EAAE,8BAAkB;iBACpB,KAAK,CACF,OAAC,CAAC,MAAM,CAAC;gBACL,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;gBAC5B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;aAClC,CAAC,CACL;iBACA,QAAQ,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CACxB,KAAK,CAAC,SAAS,EACf,GAAG,CAAC,MAAO,EACX,KAAK,CAAC,KAAK,CACd,CAAC;YACN,CAAC,CAAC;YAKN,QAAQ,EAAE,8BAAkB;iBACvB,KAAK,CACF,OAAC,CAAC,MAAM,CAAC;gBACL,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;gBACjC,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;aACtC,CAAC,CACL;iBACA,QAAQ,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC/B,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,MAAO,CAAC,CAAC;gBAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CACrB,KAAK,CAAC,cAAc,EACpB,GAAG,CAAC,MAAO,EACX,KAAK,CAAC,GAAG,CACZ,CAAC;YACN,CAAC,CAAC;YAON,aAAa,EAAE,8BAAkB,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBACtD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CACxC,GAAG,CAAC,MAAO,EACX,GAAG,CAAC,WAAW,EACf,GAAG,CAAC,aAAa,IAAI,QAAQ,CAChC,CAAC;gBACF,OAAO;oBACH,KAAK;oBACL,SAAS,EACL,OAAO,CAAC,GAAG,CAAC,4BAA4B;wBACxC,OAAO,CAAC,GAAG,CAAC,uBAAuB;wBACnC,qBAAqB;oBACzB,UAAU,EAAE,EAAE,GAAG,EAAE;iBACtB,CAAC;YACN,CAAC,CAAC;YAMF,SAAS,EAAE,8BAAkB;iBACxB,KAAK,CACF,OAAC,CAAC,MAAM,CAAC;gBACL,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;gBACjC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;gBACzB,IAAI,EAAE,gBAAgB,CAAC,OAAO,CAAC,qCAAsB,CAAC,MAAM,CAAC;aAChE,CAAC,CACL;iBACA,QAAQ,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC1B,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,CACzB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,IAAI,CACb,CAAC;YACN,CAAC,CAAC;SACT,CAAC,CAAC;IACP,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,cAAsB,EAAE,MAAc;QAC7D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC;YACtD,KAAK,EAAE,EAAE,qBAAqB,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE;YAC5D,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,EAAE,CAAC;YACL,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACpE,CAAC;IACL,CAAC;CACJ,CAAA;AAvLY,gCAAU;qBAAV,UAAU;IADtB,IAAA,mBAAU,GAAE;qCAGkB,0BAAW;QACP,gCAAc;QAChB,8BAAa;GAJjC,UAAU,CAuLtB"} \ No newline at end of file diff --git a/apps/api/dist/chat/chat.service.js b/apps/api/dist/chat/chat.service.js new file mode 100644 index 0000000000000000000000000000000000000000..e12e1a26c0bace3a6b0c3e4f3ea2a6814dc6baa2 --- /dev/null +++ b/apps/api/dist/chat/chat.service.js @@ -0,0 +1,256 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var ChatService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChatService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const realtime_client_1 = require("./realtime.client"); +const shared_types_1 = require("@micprotocol/shared-types"); +let ChatService = ChatService_1 = class ChatService { + prisma; + realtime; + logger = new common_1.Logger(ChatService_1.name); + constructor(prisma, realtime) { + this.prisma = prisma; + this.realtime = realtime; + } + async listConversations(workspaceId, userId) { + return this.prisma.conversation.findMany({ + where: { + workspaceId, + isArchived: false, + members: { some: { userId } }, + }, + include: this.conversationInclude(), + orderBy: [{ lastMessageAt: "desc" }, { updatedAt: "desc" }], + }); + } + async getOrCreateConversation(args) { + const existing = await this.findUniqueConversation(args); + if (existing) + return existing; + return this.prisma.conversation.create({ + data: { + workspaceId: args.workspaceId, + type: args.type, + podcastId: args.podcastId ?? null, + episodeId: args.episodeId ?? null, + guestId: args.guestId ?? null, + title: args.title ?? defaultTitle(args), + createdById: args.userId, + members: { + create: { + userId: args.userId, + role: shared_types_1.ConversationMemberRole.OWNER, + }, + }, + }, + include: this.conversationInclude(), + }); + } + async ensureMember(conversationId, userId, role = shared_types_1.ConversationMemberRole.MEMBER) { + const conv = await this.prisma.conversation.findUnique({ + where: { id: conversationId }, + select: { workspaceId: true }, + }); + if (!conv) + throw new common_1.NotFoundException("Conversation not found"); + return this.prisma.conversationMember.upsert({ + where: { conversationId_userId: { conversationId, userId } }, + update: { role }, + create: { conversationId, userId, role }, + }); + } + async seedWorkspaceMembers(conversationId, workspaceId) { + const members = await this.prisma.workspaceMember.findMany({ + where: { workspaceId }, + select: { userId: true }, + }); + if (members.length === 0) + return; + await this.prisma.conversationMember.createMany({ + data: members.map((m) => ({ + conversationId, + userId: m.userId, + role: shared_types_1.ConversationMemberRole.MEMBER, + })), + skipDuplicates: true, + }); + } + async listMessages(conversationId, cursor, limit = 50) { + const messages = await this.prisma.conversationMessage.findMany({ + where: { + conversationId, + ...(cursor ? { seq: { lt: cursor } } : {}), + }, + orderBy: { seq: "desc" }, + take: limit, + include: { + user: { select: { id: true, profile: true, email: true } }, + reactions: true, + }, + }); + const nextCursor = messages.length === limit ? messages[messages.length - 1].seq : null; + return { messages, nextCursor }; + } + async postMessage(args) { + const conv = await this.prisma.conversation.findUnique({ + where: { id: args.conversationId }, + select: { id: true, workspaceId: true, podcastId: true, episodeId: true }, + }); + if (!conv) + throw new common_1.NotFoundException("Conversation not found"); + const lastSeq = await this.prisma.conversationMessage.aggregate({ + where: { conversationId: conv.id }, + _max: { seq: true }, + }); + const seq = (lastSeq._max.seq ?? 0) + 1; + const message = await this.prisma.conversationMessage.create({ + data: { + conversationId: conv.id, + userId: args.userId, + body: args.body, + eventType: args.eventType ?? "message", + metadata: (args.metadata ?? {}), + replyToId: args.replyToId ?? null, + seq, + }, + include: { + user: { select: { id: true, profile: true, email: true } }, + }, + }); + await this.prisma.conversation.update({ + where: { id: conv.id }, + data: { lastMessageAt: message.createdAt }, + }); + await this.realtime.publish("message.created", { + conversationId: conv.id, + message: serializeMessage(message), + }, { conversationId: conv.id }); + return message; + } + async postSystemEvent(args) { + const conv = await this.prisma.conversation.findUnique({ + where: { id: args.conversationId }, + select: { id: true, workspaceId: true, podcastId: true, episodeId: true }, + }); + if (!conv) + throw new common_1.NotFoundException("Conversation not found"); + const lastSeq = await this.prisma.conversationMessage.aggregate({ + where: { conversationId: conv.id }, + _max: { seq: true }, + }); + const seq = (lastSeq._max.seq ?? 0) + 1; + const message = await this.prisma.conversationMessage.create({ + data: { + conversationId: conv.id, + userId: null, + body: args.body, + eventType: args.eventType, + metadata: (args.metadata ?? {}), + seq, + }, + }); + await this.prisma.conversation.update({ + where: { id: conv.id }, + data: { lastMessageAt: message.createdAt }, + }); + await this.realtime.publish(args.eventType, { + conversationId: conv.id, + message: serializeMessage(message), + }, { conversationId: conv.id }); + return message; + } + async addReaction(messageId, userId, emoji) { + if (!emoji) + throw new common_1.BadRequestException("emoji required"); + const reaction = await this.prisma.messageReaction.upsert({ + where: { + messageId_userId_emoji: { messageId, userId, emoji }, + }, + update: {}, + create: { messageId, userId, emoji }, + }); + const msg = await this.prisma.conversationMessage.findUnique({ + where: { id: messageId }, + select: { conversationId: true }, + }); + if (msg) { + await this.realtime.publish("message.reaction", { conversationId: msg.conversationId, messageId, userId, emoji }, { conversationId: msg.conversationId }); + } + return reaction; + } + async markRead(conversationId, userId, seq) { + await this.prisma.conversationMember.update({ + where: { + conversationId_userId: { conversationId, userId }, + }, + data: { lastReadSeq: seq }, + }); + await this.realtime.publish("message.read", { conversationId, userId, seq }, { conversationId }); + } + async findUniqueConversation(args) { + return this.prisma.conversation.findFirst({ + where: { + workspaceId: args.workspaceId, + type: args.type, + podcastId: args.podcastId ?? null, + episodeId: args.episodeId ?? null, + guestId: args.guestId ?? null, + }, + include: this.conversationInclude(), + }); + } + conversationInclude() { + return { + members: { + include: { + user: { select: { id: true, profile: true, email: true } }, + }, + }, + _count: { select: { messages: true } }, + podcast: { select: { id: true, title: true } }, + episode: { select: { id: true, title: true } }, + guest: { select: { id: true, fullName: true } }, + }; + } +}; +exports.ChatService = ChatService; +exports.ChatService = ChatService = ChatService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + realtime_client_1.RealtimeClient]) +], ChatService); +function defaultTitle(args) { + switch (args.type) { + case shared_types_1.ConversationType.EPISODE: return "Episode discussion"; + case shared_types_1.ConversationType.GUEST: return "Guest chat"; + case shared_types_1.ConversationType.WORKSPACE: return "Workspace"; + case shared_types_1.ConversationType.DIRECT: return "Direct message"; + default: return "General"; + } +} +function serializeMessage(m) { + return { + id: m.id, + conversationId: m.conversationId, + userId: m.userId, + body: m.body, + seq: m.seq, + eventType: m.eventType, + metadata: m.metadata, + replyToId: m.replyToId, + createdAt: m.createdAt.toISOString(), + editedAt: m.editedAt?.toISOString() ?? null, + }; +} +//# sourceMappingURL=chat.service.js.map \ No newline at end of file diff --git a/apps/api/dist/chat/chat.service.js.map b/apps/api/dist/chat/chat.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..32e2e99a45c634d4207368063ee8cb243a858b4a --- /dev/null +++ b/apps/api/dist/chat/chat.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"chat.service.js","sourceRoot":"","sources":["../../src/chat/chat.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/chat/internal-realtime.controller.js b/apps/api/dist/chat/internal-realtime.controller.js new file mode 100644 index 0000000000000000000000000000000000000000..7e0cf4bc5a430898a7f6f68dfb95af6ce258150a --- /dev/null +++ b/apps/api/dist/chat/internal-realtime.controller.js @@ -0,0 +1,267 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var InternalRealtimeController_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.InternalRealtimeController = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const chat_service_1 = require("./chat.service"); +const internal_token_guard_1 = require("../auth/internal-token.guard"); +let InternalRealtimeController = InternalRealtimeController_1 = class InternalRealtimeController { + prisma; + chat; + logger = new common_1.Logger(InternalRealtimeController_1.name); + constructor(prisma, chat) { + this.prisma = prisma; + this.chat = chat; + } + async getState(conversationId, workspaceId) { + return this.statePayload(conversationId, workspaceId); + } + async postState(conversationId, workspaceId) { + return this.statePayload(conversationId, workspaceId); + } + async postEvent(conversationId, body) { + const event = body.event; + const evType = String(event?.["eventType"] ?? event?.["type"] ?? "message"); + if (evType === "typing") + return { ok: true, skipped: "typing" }; + if (evType === "read") + return { ok: true, skipped: "read" }; + if (evType === "presence") + return { ok: true, skipped: "presence" }; + const seq = Number(event?.["seq"] ?? 0); + const exists = await this.prisma.conversationMessage.findUnique({ + where: { conversationId_seq: { conversationId, seq } }, + select: { id: true }, + }); + if (exists) + return { ok: true, skipped: "duplicate" }; + const created = await this.prisma.conversationMessage.create({ + data: { + conversationId, + userId: event?.["userId"] ?? null, + body: event?.["body"] ?? null, + eventType: evType, + metadata: (event?.["meta"] ?? event?.["metadata"] ?? {}), + seq, + }, + }); + await this.prisma.conversation.update({ + where: { id: conversationId }, + data: { lastMessageAt: created.createdAt }, + }).catch(() => undefined); + return { ok: true, id: created.id }; + } + async recent(conversationId, limitParam) { + const limit = this.parseLimit(limitParam); + const messages = await this.fetchRecent(conversationId, limit); + return { messages, nextCursor: null }; + } + async recentPost(conversationId, body) { + const limit = this.parseLimit(body?.limit === undefined ? undefined : String(body.limit)); + const messages = await this.fetchRecent(conversationId, limit); + return { messages, nextCursor: null }; + } + async getMembers(conversationId) { + const members = (await this.membersPayload(conversationId)); + return { members }; + } + async addMember(conversationId, body) { + if (!body?.userId) + throw new common_1.BadRequestException("userId required"); + const role = body.role ?? "member"; + return this.chat.ensureMember(conversationId, body.userId, role); + } + async userConversations(userId) { + const memberships = await this.prisma.conversationMember.findMany({ + where: { userId }, + select: { + conversation: { + select: { id: true, title: true, lastMessageAt: true }, + }, + }, + orderBy: { joinedAt: "desc" }, + take: 100, + }); + return { + conversations: memberships.map((m) => m.conversation), + }; + } + async getUser(userId) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + select: { id: true, name: true, email: true, image: true }, + }); + if (!user) + return { user: null }; + return { user }; + } + async statePayload(conversationId, workspaceId) { + if (!workspaceId) { + throw new common_1.BadRequestException("workspaceId is required"); + } + const conv = await this.prisma.conversation.findUnique({ + where: { id: conversationId }, + include: { + members: true, + messages: { + orderBy: { seq: "desc" }, + take: 200, + }, + }, + }); + if (!conv || conv.workspaceId !== workspaceId) { + throw new common_1.UnauthorizedException("conversation unavailable"); + } + const recent = conv.messages + .slice() + .reverse() + .map((m) => ({ + id: m.id, + userId: m.userId, + conversationId: m.conversationId, + body: m.body, + eventType: m.eventType, + metadata: m.metadata, + seq: m.seq, + at: m.createdAt.toISOString(), + })); + const members = {}; + for (const m of conv.members) { + members[m.userId] = { + role: m.role, + lastReadSeq: m.lastReadSeq, + }; + } + return { members, recent, sequence: conv.messages[0]?.seq ?? 0 }; + } + async fetchRecent(conversationId, limit) { + const rows = await this.prisma.conversationMessage.findMany({ + where: { conversationId }, + orderBy: { seq: "desc" }, + take: limit, + }); + return rows + .slice() + .reverse() + .map((m) => ({ + id: m.id, + userId: m.userId, + conversationId: m.conversationId, + body: m.body, + eventType: m.eventType, + metadata: m.metadata, + seq: m.seq, + at: m.createdAt.toISOString(), + })); + } + async membersPayload(conversationId) { + const conv = await this.prisma.conversation.findUnique({ + where: { id: conversationId }, + select: { members: true }, + }); + const members = {}; + for (const m of conv?.members ?? []) { + members[m.userId] = { role: m.role, lastReadSeq: m.lastReadSeq }; + } + return members; + } + parseLimit(raw) { + if (raw === undefined || raw === "") + return 50; + const n = Number(raw); + if (!Number.isFinite(n)) { + throw new common_1.BadRequestException("limit must be a number"); + } + return Math.min(Math.max(Math.trunc(n), 1), 200); + } +}; +exports.InternalRealtimeController = InternalRealtimeController; +__decorate([ + (0, common_1.Get)("conversations/:conversationId/state"), + __param(0, (0, common_1.Param)("conversationId")), + __param(1, (0, common_1.Query)("workspaceId")), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], InternalRealtimeController.prototype, "getState", null); +__decorate([ + (0, common_1.Post)("conversations/:conversationId/state"), + __param(0, (0, common_1.Param)("conversationId")), + __param(1, (0, common_1.Query)("workspaceId")), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], InternalRealtimeController.prototype, "postState", null); +__decorate([ + (0, common_1.Post)("conversations/:conversationId/events"), + __param(0, (0, common_1.Param)("conversationId")), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], InternalRealtimeController.prototype, "postEvent", null); +__decorate([ + (0, common_1.Get)("conversations/:conversationId/recent"), + __param(0, (0, common_1.Param)("conversationId")), + __param(1, (0, common_1.Query)("limit")), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", Promise) +], InternalRealtimeController.prototype, "recent", null); +__decorate([ + (0, common_1.Post)("conversations/:conversationId/recent"), + __param(0, (0, common_1.Param)("conversationId")), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], InternalRealtimeController.prototype, "recentPost", null); +__decorate([ + (0, common_1.Get)("conversations/:conversationId/members"), + __param(0, (0, common_1.Param)("conversationId")), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], InternalRealtimeController.prototype, "getMembers", null); +__decorate([ + (0, common_1.Post)("conversations/:conversationId/members"), + __param(0, (0, common_1.Param)("conversationId")), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, Object]), + __metadata("design:returntype", Promise) +], InternalRealtimeController.prototype, "addMember", null); +__decorate([ + (0, common_1.Get)("users/:userId/conversations"), + __param(0, (0, common_1.Param)("userId")), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], InternalRealtimeController.prototype, "userConversations", null); +__decorate([ + (0, common_1.Get)("users/:userId"), + __param(0, (0, common_1.Param)("userId")), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], InternalRealtimeController.prototype, "getUser", null); +exports.InternalRealtimeController = InternalRealtimeController = InternalRealtimeController_1 = __decorate([ + (0, common_1.Controller)("internal/realtime"), + (0, common_1.UseGuards)(internal_token_guard_1.InternalTokenGuard), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + chat_service_1.ChatService]) +], InternalRealtimeController); +//# sourceMappingURL=internal-realtime.controller.js.map \ No newline at end of file diff --git a/apps/api/dist/chat/internal-realtime.controller.js.map b/apps/api/dist/chat/internal-realtime.controller.js.map new file mode 100644 index 0000000000000000000000000000000000000000..6063d5ccefde6f0628758316efa2179b256b8dd7 --- /dev/null +++ b/apps/api/dist/chat/internal-realtime.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"internal-realtime.controller.js","sourceRoot":"","sources":["../../src/chat/internal-realtime.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/chat/media-comments.router.js b/apps/api/dist/chat/media-comments.router.js new file mode 100644 index 0000000000000000000000000000000000000000..37595626b126daeb9fd3dabe115c592c79dbe0e6 --- /dev/null +++ b/apps/api/dist/chat/media-comments.router.js @@ -0,0 +1,55 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MediaCommentsRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const trpc_init_1 = require("../trpc/trpc.init"); +const media_comments_service_1 = require("./media-comments.service"); +let MediaCommentsRouter = class MediaCommentsRouter { + comments; + constructor(comments) { + this.comments = comments; + } + get router() { + return (0, trpc_init_1.router)({ + list: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + episodeId: zod_1.z.string().uuid(), + assetId: zod_1.z.string().optional(), + })) + .query(({ input }) => this.comments.list(input.episodeId, input.assetId)), + create: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + episodeId: zod_1.z.string().uuid(), + assetId: zod_1.z.string(), + assetType: zod_1.z.enum(["audio", "video", "transcript"]).default("audio"), + mediaTimestampSeconds: zod_1.z.number().min(0), + body: zod_1.z.string().min(1).max(4000), + conversationMessageId: zod_1.z.string().uuid().optional(), + })) + .mutation(({ ctx, input }) => this.comments.create({ + workspaceId: ctx.workspaceId, + userId: ctx.userId, + ...input, + })), + resolve: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ id: zod_1.z.string().uuid() })) + .mutation(({ ctx, input }) => this.comments.resolve(input.id, ctx.userId)), + }); + } +}; +exports.MediaCommentsRouter = MediaCommentsRouter; +exports.MediaCommentsRouter = MediaCommentsRouter = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [media_comments_service_1.MediaCommentsService]) +], MediaCommentsRouter); +//# sourceMappingURL=media-comments.router.js.map \ No newline at end of file diff --git a/apps/api/dist/chat/media-comments.router.js.map b/apps/api/dist/chat/media-comments.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..9dcad41ef2086282cb4afa86d37df5392f1a825a --- /dev/null +++ b/apps/api/dist/chat/media-comments.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"media-comments.router.js","sourceRoot":"","sources":["../../src/chat/media-comments.router.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6BAAwB;AAExB,iDAA+D;AAC/D,qEAAgE;AAGzD,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IACC;IAA7B,YAA6B,QAA8B;QAA9B,aAAQ,GAAR,QAAQ,CAAsB;IAAG,CAAC;IAE/D,IAAI,MAAM;QACN,OAAO,IAAA,kBAAM,EAAC;YACV,IAAI,EAAE,8BAAkB;iBACnB,KAAK,CACF,OAAC,CAAC,MAAM,CAAC;gBACL,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;gBAC5B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;aACjC,CAAC,CACL;iBACA,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAE7E,MAAM,EAAE,8BAAkB;iBACrB,KAAK,CACF,OAAC,CAAC,MAAM,CAAC;gBACL,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;gBAC5B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE;gBACnB,SAAS,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;gBACpE,qBAAqB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;gBACxC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;gBACjC,qBAAqB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;aACtD,CAAC,CACL;iBACA,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CACzB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACjB,WAAW,EAAE,GAAG,CAAC,WAAY;gBAC7B,MAAM,EAAE,GAAG,CAAC,MAAO;gBACnB,GAAG,KAAK;aACX,CAAC,CACL;YAEL,OAAO,EAAE,8BAAkB;iBACtB,KAAK,CAAC,OAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;iBAC1C,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CACzB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,MAAO,CAAC,CAC/C;SACR,CAAC,CAAC;IACP,CAAC;CACJ,CAAA;AAxCY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;qCAE8B,6CAAoB;GADlD,mBAAmB,CAwC/B"} \ No newline at end of file diff --git a/apps/api/dist/chat/media-comments.service.js b/apps/api/dist/chat/media-comments.service.js new file mode 100644 index 0000000000000000000000000000000000000000..daeda2a422822396f2d0c69bef2ac8ea243a4f22 --- /dev/null +++ b/apps/api/dist/chat/media-comments.service.js @@ -0,0 +1,88 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MediaCommentsService = void 0; +const common_1 = require("@nestjs/common"); +const shared_types_1 = require("@micprotocol/shared-types"); +const prisma_service_1 = require("../prisma/prisma.service"); +const realtime_client_1 = require("./realtime.client"); +let MediaCommentsService = class MediaCommentsService { + prisma; + realtime; + constructor(prisma, realtime) { + this.prisma = prisma; + this.realtime = realtime; + } + async list(episodeId, assetId) { + return this.prisma.mediaComment.findMany({ + where: { episodeId, ...(assetId ? { assetId } : {}) }, + include: { + user: { select: { id: true, profile: true, email: true } }, + resolvedBy: { select: { id: true, profile: true } }, + }, + orderBy: { mediaTimestampSeconds: "asc" }, + }); + } + async create(args) { + const comment = await this.prisma.mediaComment.create({ + data: { + workspaceId: args.workspaceId, + episodeId: args.episodeId, + assetId: args.assetId, + assetType: args.assetType, + userId: args.userId, + body: args.body, + mediaTimestampSeconds: args.mediaTimestampSeconds, + conversationMessageId: args.conversationMessageId ?? null, + }, + include: { + user: { select: { id: true, profile: true, email: true } }, + }, + }); + await this.realtime.publish("media.comment.created", { comment: serialize(comment) }, { workspaceId: args.workspaceId }); + return comment; + } + async resolve(id, userId) { + const existing = await this.prisma.mediaComment.findUnique({ + where: { id }, + }); + if (!existing) + throw new common_1.NotFoundException("comment not found"); + const updated = await this.prisma.mediaComment.update({ + where: { id }, + data: { + status: shared_types_1.MediaCommentStatus.RESOLVED, + resolvedById: userId, + resolvedAt: new Date(), + }, + }); + await this.realtime.publish("media.comment.resolved", { commentId: id }, { workspaceId: existing.workspaceId }); + return updated; + } +}; +exports.MediaCommentsService = MediaCommentsService; +exports.MediaCommentsService = MediaCommentsService = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + realtime_client_1.RealtimeClient]) +], MediaCommentsService); +function serialize(c) { + return { + ...c, + mediaTimestampSeconds: c["mediaTimestampSeconds"]?.toString(), + createdAt: c["createdAt"]?.toISOString(), + updatedAt: c["updatedAt"]?.toISOString(), + resolvedAt: c["resolvedAt"] + ? c["resolvedAt"].toISOString() + : null, + }; +} +//# sourceMappingURL=media-comments.service.js.map \ No newline at end of file diff --git a/apps/api/dist/chat/media-comments.service.js.map b/apps/api/dist/chat/media-comments.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..76e178922796fe042483e8f402f1bca2c9718836 --- /dev/null +++ b/apps/api/dist/chat/media-comments.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"media-comments.service.js","sourceRoot":"","sources":["../../src/chat/media-comments.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/chat/realtime.client.js b/apps/api/dist/chat/realtime.client.js new file mode 100644 index 0000000000000000000000000000000000000000..54a769cc1444eafc742caf993878941247e9c53d --- /dev/null +++ b/apps/api/dist/chat/realtime.client.js @@ -0,0 +1,103 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var RealtimeClient_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RealtimeClient = void 0; +const common_1 = require("@nestjs/common"); +const axios_1 = require("@nestjs/axios"); +const rxjs_1 = require("rxjs"); +let RealtimeClient = RealtimeClient_1 = class RealtimeClient { + http; + logger = new common_1.Logger(RealtimeClient_1.name); + baseUrl; + internalToken; + sharedSecret; + constructor(http) { + this.http = http; + this.baseUrl = process.env.MIC_REALTIME_URL ?? "http://localhost:4001"; + this.internalToken = process.env.MIC_REALTIME_INTERNAL_TOKEN ?? ""; + this.sharedSecret = process.env.MIC_REALTIME_SHARED_SECRET ?? ""; + const usesInsecureDefaults = this.internalToken === "internal_token_change_me" || + this.sharedSecret === "change_me_to_long_random_string"; + if (process.env.NODE_ENV === "production" && + (!this.internalToken || !this.sharedSecret || usesInsecureDefaults)) { + throw new Error("MIC_REALTIME_INTERNAL_TOKEN and MIC_REALTIME_SHARED_SECRET must be set to strong random values in production"); + } + } + async issueToken(userId, workspaceId, role = "member", ttlSeconds = 60 * 60) { + const now = Math.floor(Date.now() / 1000); + const header = { alg: "HS256", typ: "JWT" }; + const payload = { + sub: userId, + workspace_id: workspaceId, + role, + iat: now, + exp: now + ttlSeconds, + }; + const headerB64 = b64url(JSON.stringify(header)); + const payloadB64 = b64url(JSON.stringify(payload)); + const signingInput = `${headerB64}.${payloadB64}`; + const sig = await hmacSha256(this.sharedSecret, signingInput); + return `${signingInput}.${sig}`; + } + async publish(eventType, payload, opts = {}) { + try { + const merged = { ...payload, ...opts }; + await (0, rxjs_1.firstValueFrom)(this.http.post(`${this.baseUrl}/internal/dispatch`, { eventType, payload: merged }, { headers: this.internalHeaders(), timeout: 5_000 })); + } + catch (err) { + this.logger.warn(`realtime publish failed (${eventType}): ${describeError(err)}`); + } + } + async publishToUser(userId, eventType, payload) { + try { + await (0, rxjs_1.firstValueFrom)(this.http.post(`${this.baseUrl}/internal/users/${userId}/broadcast`, { type: eventType, ...payload }, { headers: this.internalHeaders(), timeout: 5_000 })); + } + catch (err) { + this.logger.warn(`realtime user publish failed (${eventType}): ${describeError(err)}`); + } + } + async publishToWorkspace(workspaceId, eventType, payload) { + try { + await (0, rxjs_1.firstValueFrom)(this.http.post(`${this.baseUrl}/internal/workspaces/${workspaceId}/broadcast`, { type: eventType, ...payload }, { headers: this.internalHeaders(), timeout: 5_000 })); + } + catch (err) { + this.logger.warn(`realtime workspace publish failed (${eventType}): ${describeError(err)}`); + } + } + internalHeaders() { + return { + "x-internal-token": this.internalToken, + "content-type": "application/json", + }; + } +}; +exports.RealtimeClient = RealtimeClient; +exports.RealtimeClient = RealtimeClient = RealtimeClient_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [axios_1.HttpService]) +], RealtimeClient); +function b64url(input) { + const b64 = Buffer.from(input).toString("base64"); + return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); +} +async function hmacSha256(secret, input) { + const encoder = new TextEncoder(); + const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]); + const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(input)); + return b64url(Buffer.from(sig).toString("binary")); +} +function describeError(err) { + if (err instanceof Error) + return err.message; + return String(err); +} +//# sourceMappingURL=realtime.client.js.map \ No newline at end of file diff --git a/apps/api/dist/chat/realtime.client.js.map b/apps/api/dist/chat/realtime.client.js.map new file mode 100644 index 0000000000000000000000000000000000000000..9f4e8b01b3b2bff30d7db53f16caf5bb45ebb569 --- /dev/null +++ b/apps/api/dist/chat/realtime.client.js.map @@ -0,0 +1 @@ +{"version":3,"file":"realtime.client.js","sourceRoot":"","sources":["../../src/chat/realtime.client.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/authorization.service.js b/apps/api/dist/common/authorization.service.js new file mode 100644 index 0000000000000000000000000000000000000000..c4480b62a950bb6c30eb95ddf3cf4c3c8c0aac5e --- /dev/null +++ b/apps/api/dist/common/authorization.service.js @@ -0,0 +1,117 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthorizationService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const platform_admin_1 = require("./platform-admin"); +let AuthorizationService = class AuthorizationService { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + async checkWorkspaceAccess(workspaceId, userId, allowedRoles) { + const membership = await this.prisma.workspaceMember.findUnique({ + where: { userId_workspaceId: { userId, workspaceId } }, + }); + if (!membership) { + throw new common_1.ForbiddenException("You do not have access to this workspace"); + } + const role = membership.role; + if (allowedRoles && !allowedRoles.includes(role)) { + throw new common_1.ForbiddenException("You do not have permission to perform this action"); + } + } + async checkPodcastAccess(podcastId, userId) { + const podcast = await this.prisma.podcast.findUnique({ + where: { id: podcastId }, + select: { workspaceId: true }, + }); + if (!podcast) { + throw new common_1.NotFoundException(`Podcast ${podcastId} not found`); + } + await this.checkWorkspaceAccess(podcast.workspaceId, userId); + return { workspaceId: podcast.workspaceId }; + } + async checkEpisodeAccess(episodeId, userId) { + const episode = await this.prisma.episode.findUnique({ + where: { id: episodeId }, + select: { podcastId: true, podcast: { select: { workspaceId: true } } }, + }); + if (!episode) { + throw new common_1.NotFoundException(`Episode ${episodeId} not found`); + } + await this.checkWorkspaceAccess(episode.podcast.workspaceId, userId); + return { podcastId: episode.podcastId, workspaceId: episode.podcast.workspaceId }; + } + async checkSequenceAccess(sequenceId, userId) { + const sequence = await this.prisma.outreachSequence.findUnique({ + where: { id: sequenceId }, + select: { guest: { select: { podcast: { select: { workspaceId: true } } } } }, + }); + if (!sequence) { + throw new common_1.NotFoundException(`Sequence ${sequenceId} not found`); + } + await this.checkWorkspaceAccess(sequence.guest.podcast.workspaceId, userId); + } + async checkRecordingAccess(episodeId, userId) { + await this.checkEpisodeAccess(episodeId, userId); + } + async checkClipAccess(clipId, userId) { + const clip = await this.prisma.clip.findUnique({ + where: { id: clipId }, + select: { episode: { select: { podcast: { select: { workspaceId: true } } } } }, + }); + if (!clip) { + throw new common_1.NotFoundException(`Clip ${clipId} not found`); + } + await this.checkWorkspaceAccess(clip.episode.podcast.workspaceId, userId); + } + async checkAdminAccess(userId) { + await (0, platform_admin_1.assertPlatformAdmin)(this.prisma, userId); + } + async checkAssetRequestAccess(requestId, userId) { + const request = await this.prisma.assetRequest.findUnique({ + where: { id: requestId }, + select: { episode: { select: { podcast: { select: { workspaceId: true } } } } }, + }); + if (!request) { + throw new common_1.NotFoundException(`Asset request ${requestId} not found`); + } + await this.checkWorkspaceAccess(request.episode.podcast.workspaceId, userId); + } + async checkAssetSubmissionAccess(submissionId, userId) { + const submission = await this.prisma.assetSubmission.findUnique({ + where: { id: submissionId }, + select: { request: { select: { episode: { select: { podcast: { select: { workspaceId: true } } } } } } }, + }); + if (!submission) { + throw new common_1.NotFoundException(`Asset submission ${submissionId} not found`); + } + await this.checkWorkspaceAccess(submission.request.episode.podcast.workspaceId, userId); + } + async checkBookingAccess(bookingId, userId) { + const booking = await this.prisma.booking.findUnique({ + where: { id: bookingId }, + select: { episode: { select: { podcast: { select: { workspaceId: true } } } } }, + }); + if (!booking) { + throw new common_1.NotFoundException(`Booking ${bookingId} not found`); + } + await this.checkWorkspaceAccess(booking.episode.podcast.workspaceId, userId); + } +}; +exports.AuthorizationService = AuthorizationService; +exports.AuthorizationService = AuthorizationService = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], AuthorizationService); +//# sourceMappingURL=authorization.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/authorization.service.js.map b/apps/api/dist/common/authorization.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..cf3e3409c3d0dc243b94b02980fb7b2726e3a802 --- /dev/null +++ b/apps/api/dist/common/authorization.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"authorization.service.js","sourceRoot":"","sources":["../../src/common/authorization.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/cache/cache.module.js b/apps/api/dist/common/cache/cache.module.js new file mode 100644 index 0000000000000000000000000000000000000000..8da396b6721f71d6581e963c48e49d60fb641e5d --- /dev/null +++ b/apps/api/dist/common/cache/cache.module.js @@ -0,0 +1,24 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CacheModule = void 0; +const common_1 = require("@nestjs/common"); +const cache_service_1 = require("./cache.service"); +const redis_module_1 = require("../../redis/redis.module"); +let CacheModule = class CacheModule { +}; +exports.CacheModule = CacheModule; +exports.CacheModule = CacheModule = __decorate([ + (0, common_1.Global)(), + (0, common_1.Module)({ + imports: [redis_module_1.RedisModule], + providers: [cache_service_1.CacheService], + exports: [cache_service_1.CacheService], + }) +], CacheModule); +//# sourceMappingURL=cache.module.js.map \ No newline at end of file diff --git a/apps/api/dist/common/cache/cache.module.js.map b/apps/api/dist/common/cache/cache.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..1d570eb04bfa7cca03783a1a395df3c6417f823e --- /dev/null +++ b/apps/api/dist/common/cache/cache.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cache.module.js","sourceRoot":"","sources":["../../../src/common/cache/cache.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,mDAA+C;AAC/C,2DAAuD;AAehD,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IANvB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,0BAAW,CAAC;QACtB,SAAS,EAAE,CAAC,4BAAY,CAAC;QACzB,OAAO,EAAE,CAAC,4BAAY,CAAC;KAC1B,CAAC;GACW,WAAW,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/common/cache/cache.service.js b/apps/api/dist/common/cache/cache.service.js new file mode 100644 index 0000000000000000000000000000000000000000..57a57f01123843bf5e106eef0f2af58b3d6f6ce5 --- /dev/null +++ b/apps/api/dist/common/cache/cache.service.js @@ -0,0 +1,90 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var CacheService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CacheService = void 0; +const common_1 = require("@nestjs/common"); +const common_2 = require("@nestjs/common"); +const redis_constants_1 = require("../redis.constants"); +let CacheService = CacheService_1 = class CacheService { + logger = new common_1.Logger(CacheService_1.name); + PREFIX = "mic:cache:"; + redis = null; + constructor(redisClient) { + this.redis = redisClient; + } + onModuleInit() { + this.logger.log("CacheService initialized (Redis-backed)"); + } + onModuleDestroy() { + this.redis = null; + } + key(name, ...parts) { + return this.PREFIX + [name, ...parts.map(String).filter(Boolean)].join(":"); + } + async get(name, ...parts) { + if (!this.redis) + return null; + try { + const raw = await this.redis.get(this.key(name, ...parts)); + if (!raw) + return null; + return JSON.parse(raw); + } + catch (err) { + this.logger.warn(`Cache get failed for ${this.key(name, ...parts)}: ${err.message}`); + return null; + } + } + async set(name, value, parts = [], ttlSeconds = 300) { + if (!this.redis) + return; + try { + await this.redis.setEx(this.key(name, ...parts), ttlSeconds, JSON.stringify(value)); + } + catch (err) { + this.logger.warn(`Cache set failed for ${this.key(name, ...parts)}: ${err.message}`); + } + } + async del(name, ...parts) { + if (!this.redis) + return; + try { + await this.redis.del(this.key(name, ...parts)); + } + catch (err) { + this.logger.warn(`Cache del failed for ${this.key(name, ...parts)}: ${err.message}`); + } + } + async delPattern(pattern) { + if (!this.redis) + return; + try { + const keys = await this.redis.keys(this.PREFIX + pattern); + if (keys.length > 0) { + await this.redis.del(keys); + } + } + catch (err) { + this.logger.warn(`Cache delPattern failed for ${pattern}: ${err.message}`); + } + } +}; +exports.CacheService = CacheService; +exports.CacheService = CacheService = CacheService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(0, (0, common_2.Inject)(redis_constants_1.REDIS_CLIENT)), + __metadata("design:paramtypes", [Object]) +], CacheService); +//# sourceMappingURL=cache.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/cache/cache.service.js.map b/apps/api/dist/common/cache/cache.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..e53b18a87bf846c52a0afd04088f70d33eca607e --- /dev/null +++ b/apps/api/dist/common/cache/cache.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cache.service.js","sourceRoot":"","sources":["../../../src/common/cache/cache.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/common.module.js b/apps/api/dist/common/common.module.js new file mode 100644 index 0000000000000000000000000000000000000000..077aa78695f0b946dacbc176c87f21a9b4545cc9 --- /dev/null +++ b/apps/api/dist/common/common.module.js @@ -0,0 +1,47 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CommonModule = void 0; +const common_1 = require("@nestjs/common"); +const authorization_service_1 = require("./authorization.service"); +const subdomain_resolver_service_1 = require("./subdomain-resolver.service"); +const redis_idempotency_service_1 = require("./redis-idempotency.service"); +const redis_lock_service_1 = require("./redis-lock.service"); +const redis_rate_limiter_service_1 = require("./redis-rate-limiter.service"); +const route_collision_service_1 = require("./route-collision.service"); +const rls_middleware_1 = require("./middleware/rls.middleware"); +let CommonModule = class CommonModule { + configure(consumer) { + consumer.apply(rls_middleware_1.RlsMiddleware).forRoutes({ path: "*", method: common_1.RequestMethod.ALL }); + } +}; +exports.CommonModule = CommonModule; +exports.CommonModule = CommonModule = __decorate([ + (0, common_1.Global)(), + (0, common_1.Module)({ + providers: [ + authorization_service_1.AuthorizationService, + subdomain_resolver_service_1.SubdomainResolverService, + redis_idempotency_service_1.RedisIdempotencyService, + redis_lock_service_1.RedisLockService, + redis_rate_limiter_service_1.RedisRateLimiterService, + route_collision_service_1.RouteCollisionService, + rls_middleware_1.RlsMiddleware, + ], + exports: [ + authorization_service_1.AuthorizationService, + subdomain_resolver_service_1.SubdomainResolverService, + redis_idempotency_service_1.RedisIdempotencyService, + redis_lock_service_1.RedisLockService, + redis_rate_limiter_service_1.RedisRateLimiterService, + route_collision_service_1.RouteCollisionService, + rls_middleware_1.RlsMiddleware, + ], + }) +], CommonModule); +//# sourceMappingURL=common.module.js.map \ No newline at end of file diff --git a/apps/api/dist/common/common.module.js.map b/apps/api/dist/common/common.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..f22339e57083b156ce9c332503f0ab5c213e981d --- /dev/null +++ b/apps/api/dist/common/common.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"common.module.js","sourceRoot":"","sources":["../../src/common/common.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAmF;AACnF,mEAA+D;AAC/D,6EAAwE;AACxE,2EAAsE;AACtE,6DAAwD;AACxD,6EAAuE;AACvE,uEAAkE;AAClE,gEAA4D;AAuBrD,IAAM,YAAY,GAAlB,MAAM,YAAY;IACrB,SAAS,CAAC,QAA4B;QAClC,QAAQ,CAAC,KAAK,CAAC,8BAAa,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,sBAAa,CAAC,GAAG,EAAE,CAAC,CAAC;IACtF,CAAC;CACJ,CAAA;AAJY,oCAAY;uBAAZ,YAAY;IArBxB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACJ,SAAS,EAAE;YACP,4CAAoB;YACpB,qDAAwB;YACxB,mDAAuB;YACvB,qCAAgB;YAChB,oDAAuB;YACvB,+CAAqB;YACrB,8BAAa;SAChB;QACD,OAAO,EAAE;YACL,4CAAoB;YACpB,qDAAwB;YACxB,mDAAuB;YACvB,qCAAgB;YAChB,oDAAuB;YACvB,+CAAqB;YACrB,8BAAa;SAChB;KACJ,CAAC;GACW,YAAY,CAIxB"} \ No newline at end of file diff --git a/apps/api/dist/common/config/env.validation.js b/apps/api/dist/common/config/env.validation.js new file mode 100644 index 0000000000000000000000000000000000000000..b90558daf16cb5c39bc8be33a1388e3a4b90a6c3 --- /dev/null +++ b/apps/api/dist/common/config/env.validation.js @@ -0,0 +1,46 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.envSchema = void 0; +exports.validate = validate; +const zod_1 = require("zod"); +exports.envSchema = zod_1.z.object({ + NODE_ENV: zod_1.z.enum(['development', 'production', 'test']).default('development'), + API_PORT: zod_1.z.coerce.number().default(4000), + DATABASE_URL: zod_1.z.string().url(), + REDIS_URL: zod_1.z.string().url().optional(), + REDIS_HOST: zod_1.z.string().default('localhost'), + REDIS_PORT: zod_1.z.coerce.number().default(6379), + REDIS_PASSWORD: zod_1.z.string().optional(), + COOKIE_SECRET: zod_1.z.string().min(1, { message: 'COOKIE_SECRET is required in production' }), + BETTER_AUTH_SECRET: zod_1.z.string().min(32, { message: 'BETTER_AUTH_SECRET must be at least 32 characters' }), + BETTER_AUTH_URL: zod_1.z.string().url().default('http://localhost:4000'), + R2_ACCESS_KEY_ID: zod_1.z.string().optional(), + R2_SECRET_ACCESS_KEY: zod_1.z.string().optional(), + R2_ENDPOINT: zod_1.z.string().url().optional(), + R2_BUCKET: zod_1.z.string().optional(), + RESEND_API_KEY: zod_1.z.string().optional(), + NVIDIA_NIM_API_KEY: zod_1.z.string().optional(), + DEEPSEEK_API_KEY: zod_1.z.string().optional(), + MOONSHOT_API_KEY: zod_1.z.string().optional(), + GOOGLE_CLIENT_ID: zod_1.z.string().optional(), + GOOGLE_CLIENT_SECRET: zod_1.z.string().optional(), + GOOGLE_REDIRECT_URI: zod_1.z.string().url().optional(), + WEB_URL: zod_1.z.string().url().default('http://localhost:3000'), + API_URL: zod_1.z.string().url().default('http://localhost:4000'), + FRONTEND_URL: zod_1.z.string().url().optional(), + ROOT_DOMAIN: zod_1.z.string().min(1).optional(), + SENTRY_DSN: zod_1.z.string().url().optional(), + SENTRY_TRACE_SAMPLE_RATE: zod_1.z.coerce.number().min(0).max(1).optional(), + SENTRY_PROFILE_SAMPLE_RATE: zod_1.z.coerce.number().min(0).max(1).optional(), + RATE_LIMIT_TTL: zod_1.z.coerce.number().default(60), + RATE_LIMIT_MAX: zod_1.z.coerce.number().default(100), +}); +function validate(config) { + const result = exports.envSchema.safeParse(config); + if (!result.success) { + console.error('❌ Invalid environment variables:', result.error.format()); + throw new Error('Invalid environment variables'); + } + return result.data; +} +//# sourceMappingURL=env.validation.js.map \ No newline at end of file diff --git a/apps/api/dist/common/config/env.validation.js.map b/apps/api/dist/common/config/env.validation.js.map new file mode 100644 index 0000000000000000000000000000000000000000..3f4421221d8e482788951805c6338606889b9628 --- /dev/null +++ b/apps/api/dist/common/config/env.validation.js.map @@ -0,0 +1 @@ +{"version":3,"file":"env.validation.js","sourceRoot":"","sources":["../../../src/common/config/env.validation.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/decorators/admin-permission.decorator.js b/apps/api/dist/common/decorators/admin-permission.decorator.js new file mode 100644 index 0000000000000000000000000000000000000000..ecbbfa40dda25f64be5a2658bf033baaa87c7271 --- /dev/null +++ b/apps/api/dist/common/decorators/admin-permission.decorator.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.requireAdminPermission = exports.ADMIN_PERMISSION_KEY = void 0; +const common_1 = require("@nestjs/common"); +exports.ADMIN_PERMISSION_KEY = "admin_permission"; +const requireAdminPermission = (permission) => (0, common_1.SetMetadata)(exports.ADMIN_PERMISSION_KEY, permission); +exports.requireAdminPermission = requireAdminPermission; +//# sourceMappingURL=admin-permission.decorator.js.map \ No newline at end of file diff --git a/apps/api/dist/common/decorators/admin-permission.decorator.js.map b/apps/api/dist/common/decorators/admin-permission.decorator.js.map new file mode 100644 index 0000000000000000000000000000000000000000..eabb74cf8e5c19f21fedb375f14cfc73461ede13 --- /dev/null +++ b/apps/api/dist/common/decorators/admin-permission.decorator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-permission.decorator.js","sourceRoot":"","sources":["../../../src/common/decorators/admin-permission.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAGhC,QAAA,oBAAoB,GAAG,kBAAkB,CAAC;AAahD,MAAM,sBAAsB,GAAG,CAAC,UAA+B,EAAE,EAAE,CACxE,IAAA,oBAAW,EAAC,4BAAoB,EAAE,UAAU,CAAC,CAAC;AADnC,QAAA,sBAAsB,0BACa"} \ No newline at end of file diff --git a/apps/api/dist/common/escape-html.js b/apps/api/dist/common/escape-html.js new file mode 100644 index 0000000000000000000000000000000000000000..7e409249388c9ef53d2a99a0825d13d290be6ca3 --- /dev/null +++ b/apps/api/dist/common/escape-html.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.escapeHtml = escapeHtml; +function escapeHtml(input) { + if (input === null || input === undefined) { + return ""; + } + const str = typeof input === "string" ? input : String(input); + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} +//# sourceMappingURL=escape-html.js.map \ No newline at end of file diff --git a/apps/api/dist/common/escape-html.js.map b/apps/api/dist/common/escape-html.js.map new file mode 100644 index 0000000000000000000000000000000000000000..70b6e77225f750fb2d72e9423f013c35549188ad --- /dev/null +++ b/apps/api/dist/common/escape-html.js.map @@ -0,0 +1 @@ +{"version":3,"file":"escape-html.js","sourceRoot":"","sources":["../../src/common/escape-html.ts"],"names":[],"mappings":";;AAWA,gCAWC;AAXD,SAAgB,UAAU,CAAC,KAAc;IACrC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxC,OAAO,EAAE,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9D,OAAO,GAAG;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACjC,CAAC"} \ No newline at end of file diff --git a/apps/api/dist/common/export/export-renderer.service.js b/apps/api/dist/common/export/export-renderer.service.js new file mode 100644 index 0000000000000000000000000000000000000000..d6ae1287f107e5ee45d4c036d36191456cf9a726 --- /dev/null +++ b/apps/api/dist/common/export/export-renderer.service.js @@ -0,0 +1,301 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var ExportRendererService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ExportRendererService = void 0; +const common_1 = require("@nestjs/common"); +const path = __importStar(require("path")); +const os = __importStar(require("os")); +const fs = __importStar(require("fs/promises")); +const crypto_1 = require("crypto"); +const shared_types_1 = require("@micprotocol/shared-types"); +const r2_storage_service_1 = require("../storage/r2-storage.service"); +const safe_fetch_1 = require("../safe-fetch"); +let ExportRendererService = ExportRendererService_1 = class ExportRendererService { + r2Storage; + logger = new common_1.Logger(ExportRendererService_1.name); + ffmpegModule = null; + constructor(r2Storage) { + this.r2Storage = r2Storage; + } + async loadFfmpeg() { + if (this.ffmpegModule) + return this.ffmpegModule; + try { + this.ffmpegModule = (await Promise.resolve().then(() => __importStar(require("fluent-ffmpeg")))); + return this.ffmpegModule; + } + catch { + this.logger.warn("fluent-ffmpeg is not installed; export will use placeholder output."); + return null; + } + } + async renderAndUpload(jobData, sourceUrl, clipStartSeconds, clipEndSeconds, onProgress) { + const preset = shared_types_1.PLATFORM_CONFIGS[jobData.platform] ?? shared_types_1.PLATFORM_CONFIGS[shared_types_1.ExportPlatform.TIKTOK]; + const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "mp-export-")); + const sourcePath = path.join(tmpDir, `source-${(0, crypto_1.randomUUID)()}.bin`); + const outputPath = path.join(tmpDir, `clip-${jobData.clipId}-${jobData.platform}.mp4`); + try { + onProgress?.(10); + await this.downloadSource(sourceUrl, sourcePath); + onProgress?.(30); + const duration = Math.max(0, clipEndSeconds - clipStartSeconds); + const trimmedPath = path.join(tmpDir, `trimmed-${(0, crypto_1.randomUUID)()}.mp4`); + await this.trimSource(sourcePath, trimmedPath, clipStartSeconds, duration); + onProgress?.(50); + await this.applyPlatformRender(trimmedPath, outputPath, preset, jobData); + onProgress?.(80); + const fileBuffer = await fs.readFile(outputPath); + const r2Key = this.r2Storage.generateExportKey(jobData.clipId, jobData.platform); + const { url: cdnUrl } = await this.r2Storage.uploadBuffer(r2Key, fileBuffer, "video/mp4"); + onProgress?.(100); + const stat = await fs.stat(outputPath); + const outputDuration = await this.probeDuration(outputPath).catch(() => duration); + return { + exportJobId: "", + r2Key, + cdnUrl, + fileSizeBytes: stat.size, + durationSeconds: outputDuration, + completedAt: new Date().toISOString(), + }; + } + catch (err) { + this.logger.error(`Export render failed for clip ${jobData.clipId} (${jobData.platform}): ${err instanceof Error ? err.message : String(err)}`); + return null; + } + finally { + await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => undefined); + } + } + async downloadSource(url, destPath) { + const r2PublicHost = process.env.R2_PUBLIC_HOST || "r2.cloudflarestorage.com"; + const allowedHostSuffixes = [ + `.${r2PublicHost}`, + ".s3.amazonaws.com", + ".storage.googleapis.com", + ".blob.core.windows.net", + ".digitaloceanspaces.com", + ]; + try { + (0, safe_fetch_1.assertSafeUrl)(url, { allowedHostSuffixes }); + } + catch (err) { + try { + const u = new URL(url); + (0, safe_fetch_1.assertSafeUrl)(url, { allowedHosts: [u.hostname] }); + } + catch { + throw err; + } + } + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to download source media: ${response.status} ${response.statusText}`); + } + const arrayBuffer = await response.arrayBuffer(); + await fs.writeFile(destPath, Buffer.from(arrayBuffer)); + } + async trimSource(sourcePath, outputPath, startSeconds, duration) { + const ffmpeg = await this.loadFfmpeg(); + if (!ffmpeg) { + await fs.copyFile(sourcePath, outputPath); + return; + } + return new Promise((resolve, reject) => { + let command = ffmpeg(sourcePath) + .videoCodec("copy") + .audioCodec("copy") + .outputOptions(["-avoid_negative_ts make_zero"]) + .format("mp4") + .save(outputPath) + .on("end", () => resolve()) + .on("error", (err) => reject(err)); + if (duration > 0) { + command = command.setStartTime(startSeconds).setDuration(duration); + } + }); + } + async applyPlatformRender(sourcePath, outputPath, preset, jobData) { + const ffmpeg = await this.loadFfmpeg(); + if (!ffmpeg) { + await fs.copyFile(sourcePath, outputPath); + return; + } + const filters = []; + const isVertical = preset.width < preset.height; + let baseStream = "0:v"; + if (isVertical) { + filters.push(`[0:v]split=2[v1][v2]`, `[v1]scale=${preset.width}:${preset.height}:force_original_aspect_ratio=increase,crop=${preset.width}:${preset.height},boxblur=luma_radius=35:luma_power=2:chroma_radius=35:chroma_power=2[bg]`, `[v2]scale=${preset.width}:${preset.height}:force_original_aspect_ratio=decrease[fg]`, `[bg][fg]overlay=(W-w)/2:(H-h)/2[base]`); + baseStream = "base"; + } + else { + const padColor = jobData.renderingParams?.padColor || "black"; + filters.push(`[0:v]scale=${preset.width}:${preset.height}:force_original_aspect_ratio=decrease,pad=${preset.width}:${preset.height}:(ow-iw)/2:(oh-ih)/2:color=${padColor}[base]`); + baseStream = "base"; + } + let currentStream = baseStream; + if (jobData.hook && jobData.hook.trim().length > 0) { + const escapedHook = this.escapeFfmpegText(jobData.hook); + const hookY = Math.round(preset.height * 0.08); + const fontSize = this.getHookFontSize(preset); + filters.push(`[${currentStream}]drawtext=text='${escapedHook}':` + + `fontsize=${fontSize}:` + + `fontcolor=white:` + + `borderw=3:bordercolor=black:` + + `x=(w-text_w)/2:` + + `y=${hookY}:` + + `enable='between(t,0.5,3.5)'[withHook]`); + currentStream = "withHook"; + } + if (jobData.caption && jobData.caption.trim().length > 0) { + const escapedCaption = this.escapeFfmpegText(jobData.caption); + const captionY = Math.round(preset.height * 0.72); + const fontSize = this.getCaptionFontSize(preset); + switch (preset.captionStyle) { + case "bold-bottom": + filters.push(`[${currentStream}]drawtext=text='${escapedCaption}':` + + `fontsize=${fontSize}:` + + `fontcolor=white:` + + `borderw=4:bordercolor=black:` + + `x=(w-text_w)/2:` + + `y=${captionY}[final]`); + break; + case "gradient-bottom": + filters.push(`[${currentStream}]drawbox=y=ih-${preset.height * 0.35}:color=black@0.45:w=iw:h=${preset.height * 0.35}[withBox]`, `[withBox]drawtext=text='${escapedCaption}':` + + `fontsize=${fontSize}:` + + `fontcolor=white:` + + `borderw=2:bordercolor=black@0.6:` + + `x=(w-text_w)/2:` + + `y=${captionY}[final]`); + break; + case "clean-center": + filters.push(`[${currentStream}]drawtext=text='${escapedCaption}':` + + `fontsize=${fontSize}:` + + `fontcolor=white:` + + `borderw=3:bordercolor=black:` + + `x=(w-text_w)/2:` + + `y=${captionY}[final]`); + break; + case "professional-lower": + filters.push(`[${currentStream}]drawbox=y=ih-${Math.round(preset.height * 0.3)}:color=black@0.6:w=iw:h=${Math.round(preset.height * 0.3)}[withBox]`, `[withBox]drawtext=text='${escapedCaption}':` + + `fontsize=${fontSize}:` + + `fontcolor=white:` + + `borderw=1:bordercolor=black@0.4:` + + `x=(w-text_w)/2:` + + `y=${captionY}[final]`); + break; + case "minimal": + filters.push(`[${currentStream}]drawtext=text='${escapedCaption}':` + + `fontsize=${fontSize}:` + + `fontcolor=white:` + + `borderw=2:bordercolor=black:` + + `x=(w-text_w)/2:` + + `y=${captionY}[final]`); + break; + } + currentStream = "final"; + } + return new Promise((resolve, reject) => { + let command = ffmpeg(sourcePath) + .videoCodec("libx264") + .audioCodec("aac") + .videoBitrate(preset.videoBitrate) + .audioBitrate(preset.audioBitrate) + .fps(preset.fps) + .outputOptions([ + "-preset slow", + "-crf 18", + "-profile:v high", + "-level 4.2", + "-movflags +faststart", + "-pix_fmt yuv420p", + "-max_muxing_queue_size 1024", + ]) + .format("mp4") + .save(outputPath) + .on("end", () => resolve()) + .on("error", (err) => reject(err)); + if (filters.length > 0) { + command = command.complexFilter(filters.join("; "), [currentStream, "0:a?"]); + } + }); + } + escapeFfmpegText(text) { + return text + .replace(/[\u{10000}-\u{10FFFF}]/gu, "") + .replace(/\\/g, "\\\\") + .replace(/'/g, "\u2019") + .replace(/:/g, "\\:") + .replace(/%/g, "%%") + .replace(/\n/g, " ") + .substring(0, 200); + } + getHookFontSize(preset) { + return Math.max(24, Math.round(preset.height * 0.04)); + } + getCaptionFontSize(preset) { + return Math.max(20, Math.round(preset.height * 0.035)); + } + async probeDuration(filePath) { + const ffmpeg = await this.loadFfmpeg(); + if (!ffmpeg) + return 0; + return new Promise((resolve, reject) => { + ffmpeg.ffprobe(filePath, (err, metadata) => { + if (err) { + reject(err); + return; + } + resolve(metadata.format?.duration ?? 0); + }); + }); + } +}; +exports.ExportRendererService = ExportRendererService; +exports.ExportRendererService = ExportRendererService = ExportRendererService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [r2_storage_service_1.R2StorageService]) +], ExportRendererService); +//# sourceMappingURL=export-renderer.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/export/export-renderer.service.js.map b/apps/api/dist/common/export/export-renderer.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..9822ca0d05f661d79a4fcdb94c062f4f50a586da --- /dev/null +++ b/apps/api/dist/common/export/export-renderer.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"export-renderer.service.js","sourceRoot":"","sources":["../../../src/common/export/export-renderer.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/export/export.module.js b/apps/api/dist/common/export/export.module.js new file mode 100644 index 0000000000000000000000000000000000000000..19cf6e85053ad10464847544358668d1ece6144b --- /dev/null +++ b/apps/api/dist/common/export/export.module.js @@ -0,0 +1,22 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ExportModule = void 0; +const common_1 = require("@nestjs/common"); +const export_renderer_service_1 = require("./export-renderer.service"); +const r2_storage_service_1 = require("../storage/r2-storage.service"); +let ExportModule = class ExportModule { +}; +exports.ExportModule = ExportModule; +exports.ExportModule = ExportModule = __decorate([ + (0, common_1.Module)({ + providers: [r2_storage_service_1.R2StorageService, export_renderer_service_1.ExportRendererService], + exports: [r2_storage_service_1.R2StorageService, export_renderer_service_1.ExportRendererService], + }) +], ExportModule); +//# sourceMappingURL=export.module.js.map \ No newline at end of file diff --git a/apps/api/dist/common/export/export.module.js.map b/apps/api/dist/common/export/export.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..1651f89ce4d8ff3a42ffab97909815b6223b07c9 --- /dev/null +++ b/apps/api/dist/common/export/export.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"export.module.js","sourceRoot":"","sources":["../../../src/common/export/export.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uEAAkE;AAClE,sEAAiE;AAa1D,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IAJxB,IAAA,eAAM,EAAC;QACJ,SAAS,EAAE,CAAC,qCAAgB,EAAE,+CAAqB,CAAC;QACpD,OAAO,EAAE,CAAC,qCAAgB,EAAE,+CAAqB,CAAC;KACrD,CAAC;GACW,YAAY,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/common/file-security/file-security.module.js b/apps/api/dist/common/file-security/file-security.module.js new file mode 100644 index 0000000000000000000000000000000000000000..a9e9e54dc63c89c92a0f91bd94ef290e76923393 --- /dev/null +++ b/apps/api/dist/common/file-security/file-security.module.js @@ -0,0 +1,22 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FileSecurityModule = void 0; +const common_1 = require("@nestjs/common"); +const file_security_service_1 = require("./file-security.service"); +let FileSecurityModule = class FileSecurityModule { +}; +exports.FileSecurityModule = FileSecurityModule; +exports.FileSecurityModule = FileSecurityModule = __decorate([ + (0, common_1.Global)(), + (0, common_1.Module)({ + providers: [file_security_service_1.FileSecurityService], + exports: [file_security_service_1.FileSecurityService], + }) +], FileSecurityModule); +//# sourceMappingURL=file-security.module.js.map \ No newline at end of file diff --git a/apps/api/dist/common/file-security/file-security.module.js.map b/apps/api/dist/common/file-security/file-security.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..76988a39991da252c81bed5ed8cc0657ef61e101 --- /dev/null +++ b/apps/api/dist/common/file-security/file-security.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"file-security.module.js","sourceRoot":"","sources":["../../../src/common/file-security/file-security.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,mEAA8D;AAWvD,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;CAAG,CAAA;AAArB,gDAAkB;6BAAlB,kBAAkB;IAL9B,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACJ,SAAS,EAAE,CAAC,2CAAmB,CAAC;QAChC,OAAO,EAAE,CAAC,2CAAmB,CAAC;KACjC,CAAC;GACW,kBAAkB,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/common/file-security/file-security.service.js b/apps/api/dist/common/file-security/file-security.service.js new file mode 100644 index 0000000000000000000000000000000000000000..33bd3f10f3e60dd4cac2f3e9fd519ec8d4e0473f --- /dev/null +++ b/apps/api/dist/common/file-security/file-security.service.js @@ -0,0 +1,205 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var FileSecurityService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FileSecurityService = void 0; +const common_1 = require("@nestjs/common"); +const node_crypto_1 = require("node:crypto"); +const path = __importStar(require("node:path")); +const ALLOWED_SIGNATURES = [ + { offset: 0, bytes: [0xff, 0xd8, 0xff], mimeType: "image/jpeg" }, + { offset: 0, bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], mimeType: "image/png" }, + { offset: 0, bytes: [0x52, 0x49, 0x46, 0x46], mimeType: "image/webp" }, + { offset: 0, bytes: [0x47, 0x49, 0x46, 0x38], mimeType: "image/gif" }, + { offset: 0, bytes: [0x25, 0x50, 0x44, 0x46], mimeType: "application/pdf" }, + { offset: 0, bytes: [0xff, 0xfb], mimeType: "audio/mpeg" }, + { offset: 0, bytes: [0xff, 0xf3], mimeType: "audio/mpeg" }, + { offset: 0, bytes: [0xff, 0xf2], mimeType: "audio/mpeg" }, + { offset: 0, bytes: [0x49, 0x44, 0x33], mimeType: "audio/mpeg" }, + { offset: 0, bytes: [0x52, 0x49, 0x46, 0x46], mimeType: "audio/wav" }, + { offset: 0, bytes: [0x4f, 0x67, 0x67, 0x53], mimeType: "audio/ogg" }, + { offset: 4, bytes: [0x66, 0x74, 0x79, 0x70], mimeType: "audio/mp4" }, + { offset: 4, bytes: [0x66, 0x74, 0x79, 0x70], mimeType: "video/mp4" }, +]; +const RIFF_SUB_TYPES = { + "57 45 42 50": "image/webp", + "57 41 56 45": "audio/wav", +}; +const MAX_FILENAME_LENGTH = 255; +const RESERVED_NAMES = new Set([ + "CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", +]); +let FileSecurityService = FileSecurityService_1 = class FileSecurityService { + logger = new common_1.Logger(FileSecurityService_1.name); + validate(file, options = {}) { + const { maxSizeBytes = 25 * 1024 * 1024 } = options; + const defaultAllowedMimes = [ + "image/jpeg", + "image/png", + "image/webp", + "image/gif", + "application/pdf", + "audio/mpeg", + "audio/wav", + "audio/ogg", + "audio/mp4", + "video/mp4", + ]; + const allowedMimeTypes = options.allowedMimeTypes ?? defaultAllowedMimes; + if (!file || !file.buffer) { + throw new common_1.BadRequestException("No file data received."); + } + if (file.size > maxSizeBytes) { + throw new common_1.BadRequestException(`File too large. Maximum allowed size is ${Math.round(maxSizeBytes / 1024 / 1024)} MB.`); + } + if (file.size === 0) { + throw new common_1.BadRequestException("Empty files are not allowed."); + } + const detectedMimeType = this.detectMimeType(file.buffer); + if (!detectedMimeType) { + this.logger.warn(`Rejected upload: unknown magic bytes. Client claimed ${file.mimetype}, ` + + `filename: ${file.originalname}`); + throw new common_1.BadRequestException("File type not recognised. Please upload a supported file format."); + } + if (!allowedMimeTypes.includes(detectedMimeType)) { + this.logger.warn(`Rejected upload: detected type ${detectedMimeType} not in allowlist. ` + + `Client claimed ${file.mimetype}.`); + throw new common_1.BadRequestException(`File type "${detectedMimeType}" is not allowed. ` + + `Accepted formats: ${allowedMimeTypes.join(", ")}.`); + } + if (file.mimetype !== detectedMimeType) { + this.logger.warn(`MIME mismatch: client reported ${file.mimetype} but magic bytes say ` + + `${detectedMimeType}. Filename: ${file.originalname}. Using detected type.`); + } + const safeFilename = this.sanitizeFilename(file.originalname, detectedMimeType); + const sha256 = (0, node_crypto_1.createHash)("sha256").update(file.buffer).digest("hex"); + return { + sha256, + detectedMimeType, + safeFilename, + size: file.size, + }; + } + detectMimeType(buffer) { + if (buffer.length < 4) + return null; + for (const sig of ALLOWED_SIGNATURES) { + if (this.matchesSignature(buffer, sig)) { + if (sig.mimeType === "image/webp" || sig.mimeType === "audio/wav") { + const subType = this.detectRiffSubType(buffer); + if (subType) + return subType; + continue; + } + if (sig.mimeType === "audio/mp4" || sig.mimeType === "video/mp4") { + return this.detectMp4SubType(buffer); + } + return sig.mimeType; + } + } + return null; + } + matchesSignature(buffer, sig) { + if (buffer.length < sig.offset + sig.bytes.length) + return false; + return sig.bytes.every((byte, i) => buffer[sig.offset + i] === byte); + } + detectRiffSubType(buffer) { + if (buffer.length < 12) + return null; + const subKey = Array.from(buffer.slice(8, 12)) + .map((b) => b.toString(16).padStart(2, "0")) + .join(" "); + return RIFF_SUB_TYPES[subKey] ?? null; + } + detectMp4SubType(buffer) { + if (buffer.length >= 12) { + const brand = buffer.slice(8, 12).toString("ascii"); + if (brand === "M4A ") + return "audio/mp4"; + } + return "video/mp4"; + } + sanitizeFilename(originalName, detectedMimeType) { + let name = originalName + .replace(/\0/g, "") + .replace(/[\x00-\x1f\x7f]/g, "") + .replace(/[/\\:*?"<>|]/g, "_") + .trim(); + name = path.basename(name); + if (name.length > MAX_FILENAME_LENGTH) { + const ext = path.extname(name); + name = name.slice(0, MAX_FILENAME_LENGTH - ext.length) + ext; + } + if (!name || name === "." || name === "..") { + name = "upload"; + } + const stem = path.basename(name, path.extname(name)).replace(/\./g, "_"); + const canonicalExt = this.canonicalExtension(detectedMimeType); + const safeStem = stem || "upload"; + if (RESERVED_NAMES.has(safeStem.toUpperCase())) { + return `file${canonicalExt}`; + } + return `${safeStem}${canonicalExt}`; + } + canonicalExtension(mimeType) { + const map = { + "image/jpeg": ".jpg", + "image/png": ".png", + "image/webp": ".webp", + "image/gif": ".gif", + "application/pdf": ".pdf", + "audio/mpeg": ".mp3", + "audio/wav": ".wav", + "audio/ogg": ".ogg", + "audio/mp4": ".m4a", + "video/mp4": ".mp4", + }; + return map[mimeType] ?? ""; + } +}; +exports.FileSecurityService = FileSecurityService; +exports.FileSecurityService = FileSecurityService = FileSecurityService_1 = __decorate([ + (0, common_1.Injectable)() +], FileSecurityService); +//# sourceMappingURL=file-security.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/file-security/file-security.service.js.map b/apps/api/dist/common/file-security/file-security.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..c72b9659256c1a240aedf67e87739a0dc2f16688 --- /dev/null +++ b/apps/api/dist/common/file-security/file-security.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"file-security.service.js","sourceRoot":"","sources":["../../../src/common/file-security/file-security.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/filters/all-exceptions.filter.js b/apps/api/dist/common/filters/all-exceptions.filter.js new file mode 100644 index 0000000000000000000000000000000000000000..2ed4d1c601f6bd94ac4ea136bbab46b52a8da8ff --- /dev/null +++ b/apps/api/dist/common/filters/all-exceptions.filter.js @@ -0,0 +1,193 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var AllExceptionsFilter_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AllExceptionsFilter = void 0; +const common_1 = require("@nestjs/common"); +const client_1 = require("@prisma/client"); +const zod_1 = require("zod"); +const node_crypto_1 = require("node:crypto"); +const shared_types_1 = require("@micprotocol/shared-types"); +let AllExceptionsFilter = AllExceptionsFilter_1 = class AllExceptionsFilter { + logger = new common_1.Logger(AllExceptionsFilter_1.name); + catch(exception, host) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + const requestId = request.headers["x-request-id"] || + request.headers["x-correlation-id"] || + (0, node_crypto_1.randomUUID)(); + let status = common_1.HttpStatus.INTERNAL_SERVER_ERROR; + let errorCode = shared_types_1.ErrorCode.INTERNAL_SERVER_ERROR; + let errorName = "Internal Server Error"; + let message = "An unexpected error occurred. Please try again later."; + let details = undefined; + if (exception instanceof common_1.HttpException) { + status = exception.getStatus(); + const res = exception.getResponse(); + if (typeof res === "string") { + message = res; + } + else if (typeof res === "object" && res !== null) { + const resObj = res; + if ("message" in resObj) { + if (Array.isArray(resObj.message)) { + message = resObj.message.join("; "); + details = resObj.message.map((msg) => ({ message: msg })); + } + else { + message = String(resObj.message); + } + } + if ("error" in resObj && typeof resObj.error === "string") { + errorName = resObj.error; + } + if ("details" in resObj) { + details = resObj.details; + } + if ("code" in resObj && typeof resObj.code === "string") { + errorCode = resObj.code; + } + } + if (errorCode === shared_types_1.ErrorCode.INTERNAL_SERVER_ERROR) { + errorCode = this.mapHttpStatusToErrorCode(status); + } + if (errorName === "Internal Server Error") { + errorName = this.mapHttpStatusToErrorName(status); + } + } + else if (exception instanceof client_1.Prisma.PrismaClientKnownRequestError) { + switch (exception.code) { + case "P2002": { + status = common_1.HttpStatus.CONFLICT; + errorCode = shared_types_1.ErrorCode.CONFLICT; + errorName = "Conflict"; + const target = exception.meta?.target?.join(", ") || "field"; + message = `A record with this ${target} already exists.`; + details = { target: exception.meta?.target }; + break; + } + case "P2025": { + status = common_1.HttpStatus.NOT_FOUND; + errorCode = shared_types_1.ErrorCode.NOT_FOUND; + errorName = "Not Found"; + message = "The requested resource was not found."; + break; + } + case "P2003": { + status = common_1.HttpStatus.BAD_REQUEST; + errorCode = shared_types_1.ErrorCode.BAD_REQUEST; + errorName = "Bad Request"; + message = "Foreign key constraint failed on related resource."; + break; + } + default: { + status = common_1.HttpStatus.BAD_REQUEST; + errorCode = shared_types_1.ErrorCode.BAD_REQUEST; + errorName = "Database Error"; + message = "A database operation failed."; + break; + } + } + } + else if (exception instanceof zod_1.ZodError) { + status = common_1.HttpStatus.BAD_REQUEST; + errorCode = shared_types_1.ErrorCode.VALIDATION_ERROR; + errorName = "Validation Error"; + message = "Input validation failed."; + details = exception.issues.map((issue) => ({ + field: issue.path.join("."), + message: issue.message, + code: issue.code, + })); + } + else if (exception instanceof Error) { + if (process.env.NODE_ENV !== "production") { + message = exception.message; + } + if (exception.message.toLowerCase().includes("rate limit")) { + status = common_1.HttpStatus.TOO_MANY_REQUESTS; + errorCode = shared_types_1.ErrorCode.RATE_LIMITED; + errorName = "Too Many Requests"; + message = "Rate limit exceeded. Please try again shortly."; + } + } + if (status >= common_1.HttpStatus.INTERNAL_SERVER_ERROR) { + this.logger.error(`[${requestId}] ${request.method} ${request.originalUrl} ${status} - ${message}`, exception instanceof Error ? exception.stack : undefined); + } + response.setHeader("x-request-id", requestId); + const errorPayload = { + statusCode: status, + error: errorName, + code: errorCode, + message, + details, + path: request.originalUrl, + timestamp: new Date().toISOString(), + requestId, + }; + response.status(status).json(errorPayload); + } + mapHttpStatusToErrorName(status) { + switch (status) { + case common_1.HttpStatus.BAD_REQUEST: + return "Bad Request"; + case common_1.HttpStatus.UNAUTHORIZED: + return "Unauthorized"; + case common_1.HttpStatus.FORBIDDEN: + return "Forbidden"; + case common_1.HttpStatus.NOT_FOUND: + return "Not Found"; + case common_1.HttpStatus.METHOD_NOT_ALLOWED: + return "Method Not Allowed"; + case common_1.HttpStatus.CONFLICT: + return "Conflict"; + case common_1.HttpStatus.UNPROCESSABLE_ENTITY: + return "Unprocessable Entity"; + case common_1.HttpStatus.TOO_MANY_REQUESTS: + return "Too Many Requests"; + case common_1.HttpStatus.SERVICE_UNAVAILABLE: + return "Service Unavailable"; + case common_1.HttpStatus.GATEWAY_TIMEOUT: + return "Gateway Timeout"; + default: + return "Internal Server Error"; + } + } + mapHttpStatusToErrorCode(status) { + switch (status) { + case common_1.HttpStatus.BAD_REQUEST: + return shared_types_1.ErrorCode.BAD_REQUEST; + case common_1.HttpStatus.UNAUTHORIZED: + return shared_types_1.ErrorCode.UNAUTHORIZED; + case common_1.HttpStatus.FORBIDDEN: + return shared_types_1.ErrorCode.FORBIDDEN; + case common_1.HttpStatus.NOT_FOUND: + return shared_types_1.ErrorCode.NOT_FOUND; + case common_1.HttpStatus.METHOD_NOT_ALLOWED: + return shared_types_1.ErrorCode.METHOD_NOT_ALLOWED; + case common_1.HttpStatus.CONFLICT: + return shared_types_1.ErrorCode.CONFLICT; + case common_1.HttpStatus.UNPROCESSABLE_ENTITY: + return shared_types_1.ErrorCode.UNPROCESSABLE_ENTITY; + case common_1.HttpStatus.TOO_MANY_REQUESTS: + return shared_types_1.ErrorCode.RATE_LIMITED; + case common_1.HttpStatus.SERVICE_UNAVAILABLE: + return shared_types_1.ErrorCode.SERVICE_UNAVAILABLE; + case common_1.HttpStatus.GATEWAY_TIMEOUT: + return shared_types_1.ErrorCode.GATEWAY_TIMEOUT; + default: + return shared_types_1.ErrorCode.INTERNAL_SERVER_ERROR; + } + } +}; +exports.AllExceptionsFilter = AllExceptionsFilter; +exports.AllExceptionsFilter = AllExceptionsFilter = AllExceptionsFilter_1 = __decorate([ + (0, common_1.Catch)() +], AllExceptionsFilter); +//# sourceMappingURL=all-exceptions.filter.js.map \ No newline at end of file diff --git a/apps/api/dist/common/filters/all-exceptions.filter.js.map b/apps/api/dist/common/filters/all-exceptions.filter.js.map new file mode 100644 index 0000000000000000000000000000000000000000..eedafef12d35801aaae703afb7c75e05bec8bf89 --- /dev/null +++ b/apps/api/dist/common/filters/all-exceptions.filter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"all-exceptions.filter.js","sourceRoot":"","sources":["../../../src/common/filters/all-exceptions.filter.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/guards/admin-permission.guard.js b/apps/api/dist/common/guards/admin-permission.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..ea407bf6adf191c19e67ddfc9d7386f085fcab85 --- /dev/null +++ b/apps/api/dist/common/guards/admin-permission.guard.js @@ -0,0 +1,54 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var AdminPermissionGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AdminPermissionGuard = void 0; +const common_1 = require("@nestjs/common"); +const core_1 = require("@nestjs/core"); +const admin_auth_service_1 = require("../../admin/admin-auth.service"); +const admin_permission_decorator_1 = require("../decorators/admin-permission.decorator"); +let AdminPermissionGuard = AdminPermissionGuard_1 = class AdminPermissionGuard { + reflector; + adminAuth; + logger = new common_1.Logger(AdminPermissionGuard_1.name); + constructor(reflector, adminAuth) { + this.reflector = reflector; + this.adminAuth = adminAuth; + } + async canActivate(context) { + const requiredPermission = this.reflector.get(admin_permission_decorator_1.ADMIN_PERMISSION_KEY, context.getHandler()); + if (!requiredPermission) { + return true; + } + const request = context.switchToHttp().getRequest(); + const userId = request.userId ?? + request.headers["x-user-id"]; + if (!userId) { + throw new common_1.ForbiddenException("Authentication required"); + } + const ctx = await this.adminAuth.getAdminContext(userId); + if (!ctx || ctx.suspended) { + throw new common_1.ForbiddenException("Admin privileges required"); + } + if (!ctx.permissions.has(requiredPermission)) { + this.logger.warn(`User ${userId} denied admin permission ${requiredPermission}`); + throw new common_1.ForbiddenException(`Insufficient permissions: ${requiredPermission} required`); + } + return true; + } +}; +exports.AdminPermissionGuard = AdminPermissionGuard; +exports.AdminPermissionGuard = AdminPermissionGuard = AdminPermissionGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [core_1.Reflector, + admin_auth_service_1.AdminAuthService]) +], AdminPermissionGuard); +//# sourceMappingURL=admin-permission.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/common/guards/admin-permission.guard.js.map b/apps/api/dist/common/guards/admin-permission.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..ac5051b86f29f90140a4beaa38f9f6f0a3f5fef1 --- /dev/null +++ b/apps/api/dist/common/guards/admin-permission.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"admin-permission.guard.js","sourceRoot":"","sources":["../../../src/common/guards/admin-permission.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAMwB;AACxB,uCAAyC;AAGzC,uEAAkE;AAClE,yFAAgF;AAczE,IAAM,oBAAoB,4BAA1B,MAAM,oBAAoB;IAIZ;IACA;IAJF,MAAM,GAAG,IAAI,eAAM,CAAC,sBAAoB,CAAC,IAAI,CAAC,CAAC;IAEhE,YACmB,SAAoB,EACpB,SAA2B;QAD3B,cAAS,GAAT,SAAS,CAAW;QACpB,cAAS,GAAT,SAAS,CAAkB;IAC3C,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAC3C,iDAAoB,EACpB,OAAO,CAAC,UAAU,EAAE,CACrB,CAAC;QAGF,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,MAAM,GAAI,OAAO,CAAC,MAA6B;YAClD,OAAO,CAAC,OAAO,CAAC,WAAW,CAAwB,CAAC;QAEvD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,2BAAkB,CAAC,yBAAyB,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACzD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,2BAAkB,CAAC,2BAA2B,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,QAAQ,MAAM,4BAA4B,kBAAkB,EAAE,CAC/D,CAAC;YACF,MAAM,IAAI,2BAAkB,CAC1B,6BAA6B,kBAAkB,WAAW,CAC3D,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA1CY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAKmB,gBAAS;QACT,qCAAgB;GALnC,oBAAoB,CA0ChC"} \ No newline at end of file diff --git a/apps/api/dist/common/guards/podcast-access.guard.js b/apps/api/dist/common/guards/podcast-access.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..cc3c3caeb91298b9797e5b5819c4862866a24de9 --- /dev/null +++ b/apps/api/dist/common/guards/podcast-access.guard.js @@ -0,0 +1,60 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var PodcastAccessGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PodcastAccessGuard = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../prisma/prisma.service"); +let PodcastAccessGuard = PodcastAccessGuard_1 = class PodcastAccessGuard { + prisma; + logger = new common_1.Logger(PodcastAccessGuard_1.name); + constructor(prisma) { + this.prisma = prisma; + } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + const userId = request.userId; + const podcastId = (request.params.podcastId || request.body?.fromPodcastId || request.query?.podcastId); + if (!userId) { + throw new common_1.ForbiddenException("Authentication required"); + } + if (!podcastId) { + throw new common_1.ForbiddenException("Podcast ID is required"); + } + const podcast = await this.prisma.podcast.findUnique({ + where: { id: podcastId }, + select: { workspaceId: true }, + }); + if (!podcast) { + throw new common_1.ForbiddenException("Podcast not found"); + } + const member = await this.prisma.workspaceMember.findUnique({ + where: { + userId_workspaceId: { + userId, + workspaceId: podcast.workspaceId, + }, + }, + }); + if (!member) { + this.logger.warn(`User ${userId} denied access to podcast ${podcastId} (workspace ${podcast.workspaceId})`); + throw new common_1.ForbiddenException("You do not have access to this podcast"); + } + request.workspaceId = podcast.workspaceId; + return true; + } +}; +exports.PodcastAccessGuard = PodcastAccessGuard; +exports.PodcastAccessGuard = PodcastAccessGuard = PodcastAccessGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], PodcastAccessGuard); +//# sourceMappingURL=podcast-access.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/common/guards/podcast-access.guard.js.map b/apps/api/dist/common/guards/podcast-access.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..f41999e5849ac2389dc9d2f169256724a6810949 --- /dev/null +++ b/apps/api/dist/common/guards/podcast-access.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"podcast-access.guard.js","sourceRoot":"","sources":["../../../src/common/guards/podcast-access.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAuG;AACvG,gEAA4D;AAGrD,IAAM,kBAAkB,0BAAxB,MAAM,kBAAkB;IAGE;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,oBAAkB,CAAC,IAAI,CAAC,CAAC;IAE9D,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,WAAW,CAAC,OAAyB;QACvC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,OAAO,CAAC,MAA4B,CAAC;QACpD,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,aAAa,IAAI,OAAO,CAAC,KAAK,EAAE,SAAS,CAAuB,CAAC;QAE9H,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,IAAI,2BAAkB,CAAC,yBAAyB,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,MAAM,IAAI,2BAAkB,CAAC,wBAAwB,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YACjD,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;YACxB,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,2BAAkB,CAAC,mBAAmB,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC;YACxD,KAAK,EAAE;gBACH,kBAAkB,EAAE;oBAChB,MAAM;oBACN,WAAW,EAAE,OAAO,CAAC,WAAW;iBACnC;aACJ;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,MAAM,6BAA6B,SAAS,eAAe,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC;YAC5G,MAAM,IAAI,2BAAkB,CAAC,wCAAwC,CAAC,CAAC;QAC3E,CAAC;QAED,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QAC1C,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ,CAAA;AA5CY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;qCAI4B,8BAAa;GAHzC,kBAAkB,CA4C9B"} \ No newline at end of file diff --git a/apps/api/dist/common/guards/workspace-access.guard.js b/apps/api/dist/common/guards/workspace-access.guard.js new file mode 100644 index 0000000000000000000000000000000000000000..bdf2c1b67e36f457843dbc0301464ec8b8a09b06 --- /dev/null +++ b/apps/api/dist/common/guards/workspace-access.guard.js @@ -0,0 +1,52 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var WorkspaceAccessGuard_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WorkspaceAccessGuard = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../prisma/prisma.service"); +let WorkspaceAccessGuard = WorkspaceAccessGuard_1 = class WorkspaceAccessGuard { + prisma; + logger = new common_1.Logger(WorkspaceAccessGuard_1.name); + constructor(prisma) { + this.prisma = prisma; + } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + const userId = request.userId; + const workspaceId = (request.params.workspaceId || request.query.workspaceId || request.headers["x-workspace-id"]); + if (!userId) { + throw new common_1.ForbiddenException("Authentication required"); + } + if (!workspaceId) { + throw new common_1.ForbiddenException("Workspace ID is required"); + } + const member = await this.prisma.workspaceMember.findUnique({ + where: { + userId_workspaceId: { + userId, + workspaceId: workspaceId, + }, + }, + }); + if (!member) { + this.logger.warn(`User ${userId} denied access to workspace ${workspaceId}`); + throw new common_1.ForbiddenException("You do not have access to this workspace"); + } + return true; + } +}; +exports.WorkspaceAccessGuard = WorkspaceAccessGuard; +exports.WorkspaceAccessGuard = WorkspaceAccessGuard = WorkspaceAccessGuard_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], WorkspaceAccessGuard); +//# sourceMappingURL=workspace-access.guard.js.map \ No newline at end of file diff --git a/apps/api/dist/common/guards/workspace-access.guard.js.map b/apps/api/dist/common/guards/workspace-access.guard.js.map new file mode 100644 index 0000000000000000000000000000000000000000..512f28d260bba6fcab4f988bf4a1ced72ed7153e --- /dev/null +++ b/apps/api/dist/common/guards/workspace-access.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"workspace-access.guard.js","sourceRoot":"","sources":["../../../src/common/guards/workspace-access.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAuG;AACvG,gEAA4D;AAGrD,IAAM,oBAAoB,4BAA1B,MAAM,oBAAoB;IAGA;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,sBAAoB,CAAC,IAAI,CAAC,CAAC;IAEhE,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,WAAW,CAAC,OAAyB;QACvC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,OAAO,CAAC,MAA4B,CAAC;QACpD,MAAM,WAAW,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAuB,CAAC;QAEzI,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,IAAI,2BAAkB,CAAC,yBAAyB,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,WAAW,EAAE,CAAC;YACf,MAAM,IAAI,2BAAkB,CAAC,0BAA0B,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC;YACxD,KAAK,EAAE;gBACH,kBAAkB,EAAE;oBAChB,MAAM;oBACN,WAAW,EAAE,WAAqB;iBACrC;aACJ;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,MAAM,+BAA+B,WAAW,EAAE,CAAC,CAAC;YAC7E,MAAM,IAAI,2BAAkB,CAAC,0CAA0C,CAAC,CAAC;QAC7E,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ,CAAA;AAlCY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAI4B,8BAAa;GAHzC,oBAAoB,CAkChC"} \ No newline at end of file diff --git a/apps/api/dist/common/middleware/raw-body.middleware.js b/apps/api/dist/common/middleware/raw-body.middleware.js new file mode 100644 index 0000000000000000000000000000000000000000..4ee4fee41ba7f7a14347ca5e80e25edea9e71842 --- /dev/null +++ b/apps/api/dist/common/middleware/raw-body.middleware.js @@ -0,0 +1,34 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var RawBodyMiddleware_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RawBodyMiddleware = void 0; +const common_1 = require("@nestjs/common"); +let RawBodyMiddleware = RawBodyMiddleware_1 = class RawBodyMiddleware { + logger = new common_1.Logger(RawBodyMiddleware_1.name); + use(req, res, next) { + const chunks = []; + req.on('data', (chunk) => { + chunks.push(chunk); + }); + req.on('end', () => { + const rawBody = Buffer.concat(chunks).toString('utf8'); + req.rawBody = rawBody; + next(); + }); + req.on('error', (error) => { + this.logger.error('Error reading request body', error); + next(error); + }); + } +}; +exports.RawBodyMiddleware = RawBodyMiddleware; +exports.RawBodyMiddleware = RawBodyMiddleware = RawBodyMiddleware_1 = __decorate([ + (0, common_1.Injectable)() +], RawBodyMiddleware); +//# sourceMappingURL=raw-body.middleware.js.map \ No newline at end of file diff --git a/apps/api/dist/common/middleware/raw-body.middleware.js.map b/apps/api/dist/common/middleware/raw-body.middleware.js.map new file mode 100644 index 0000000000000000000000000000000000000000..7a91e909a1ac48ea9c45b44c125644284ebde5d1 --- /dev/null +++ b/apps/api/dist/common/middleware/raw-body.middleware.js.map @@ -0,0 +1 @@ +{"version":3,"file":"raw-body.middleware.js","sourceRoot":"","sources":["../../../src/common/middleware/raw-body.middleware.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAoE;AAQ7D,IAAM,iBAAiB,yBAAvB,MAAM,iBAAiB;IACX,MAAM,GAAG,IAAI,eAAM,CAAC,mBAAiB,CAAC,IAAI,CAAC,CAAC;IAE7D,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;QACjD,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtD,GAAW,CAAC,OAAO,GAAG,OAAO,CAAC;YAC/B,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACxB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACvD,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AArBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;GACA,iBAAiB,CAqB7B"} \ No newline at end of file diff --git a/apps/api/dist/common/middleware/rls.middleware.js b/apps/api/dist/common/middleware/rls.middleware.js new file mode 100644 index 0000000000000000000000000000000000000000..f83ea0799c1fb7362a0d7b52e26b88150c8471fe --- /dev/null +++ b/apps/api/dist/common/middleware/rls.middleware.js @@ -0,0 +1,41 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var RlsMiddleware_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RlsMiddleware = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../prisma/prisma.service"); +let RlsMiddleware = RlsMiddleware_1 = class RlsMiddleware { + prisma; + logger = new common_1.Logger(RlsMiddleware_1.name); + constructor(prisma) { + this.prisma = prisma; + } + async use(req, res, next) { + const workspaceId = req.headers["x-workspace-id"]; + if (!workspaceId) { + return next(); + } + try { + await this.prisma.$queryRawUnsafe(`SET app.workspace_id = '${workspaceId}'`); + } + catch (err) { + this.logger.warn(`Failed to set app.workspace_id for RLS: ${err instanceof Error ? err.message : String(err)}`); + } + next(); + } +}; +exports.RlsMiddleware = RlsMiddleware; +exports.RlsMiddleware = RlsMiddleware = RlsMiddleware_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], RlsMiddleware); +//# sourceMappingURL=rls.middleware.js.map \ No newline at end of file diff --git a/apps/api/dist/common/middleware/rls.middleware.js.map b/apps/api/dist/common/middleware/rls.middleware.js.map new file mode 100644 index 0000000000000000000000000000000000000000..66845e45de66a49caedf6fd5605679a4961ab042 --- /dev/null +++ b/apps/api/dist/common/middleware/rls.middleware.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rls.middleware.js","sourceRoot":"","sources":["../../../src/common/middleware/rls.middleware.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoE;AAGpE,gEAA4D;AAsBrD,IAAM,aAAa,qBAAnB,MAAM,aAAa;IAGK;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,eAAa,CAAC,IAAI,CAAC,CAAC;IAEzD,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;QACvD,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;QAExE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAC/B,2BAA2B,WAAW,GAAG,CAC1C,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,2CACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;QACJ,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC;CACF,CAAA;AA1BY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;qCAI0B,8BAAa;GAHvC,aAAa,CA0BzB"} \ No newline at end of file diff --git a/apps/api/dist/common/origin-policy.js b/apps/api/dist/common/origin-policy.js new file mode 100644 index 0000000000000000000000000000000000000000..d9edd94751de561e8b73bbc62eac6b656ba04166 --- /dev/null +++ b/apps/api/dist/common/origin-policy.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getAllowedApiOrigins = getAllowedApiOrigins; +exports.isOriginAllowed = isOriginAllowed; +const DEV_ORIGINS = new Set([ + "http://localhost:3000", + "http://localhost:3001", +]); +function normalizeOrigin(origin) { + return origin.replace(/\/+$/, ""); +} +function getAllowedApiOrigins(extra = []) { + const set = new Set(DEV_ORIGINS); + for (const value of [process.env.WEB_URL, process.env.FRONTEND_URL, ...extra]) { + if (typeof value === "string" && value && value !== "*") { + set.add(normalizeOrigin(value)); + } + } + return set; +} +function isOriginAllowed(origin, extra = []) { + if (!origin) + return true; + const normalized = normalizeOrigin(origin); + if (getAllowedApiOrigins(extra).has(normalized)) + return true; + const rootDomain = process.env.ROOT_DOMAIN || process.env.NEXT_PUBLIC_ROOT_DOMAIN; + if (rootDomain) { + try { + const host = new URL(normalized).hostname; + return host === rootDomain || host.endsWith(`.${rootDomain}`); + } + catch { + return false; + } + } + return false; +} +//# sourceMappingURL=origin-policy.js.map \ No newline at end of file diff --git a/apps/api/dist/common/origin-policy.js.map b/apps/api/dist/common/origin-policy.js.map new file mode 100644 index 0000000000000000000000000000000000000000..71a7713076b9639db404175168c69cf7f5521d76 --- /dev/null +++ b/apps/api/dist/common/origin-policy.js.map @@ -0,0 +1 @@ +{"version":3,"file":"origin-policy.js","sourceRoot":"","sources":["../../src/common/origin-policy.ts"],"names":[],"mappings":";;AAwBA,oDAUC;AAQD,0CAsBC;AArDD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,uBAAuB;IACvB,uBAAuB;CACxB,CAAC,CAAC;AAEH,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC;AAMD,SAAgB,oBAAoB,CAClC,QAA0C,EAAE;IAE5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;QAC9E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YACxD,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAQD,SAAgB,eAAe,CAC7B,MAA0B,EAC1B,QAA0C,EAAE;IAE5C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,oBAAoB,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAI7D,MAAM,UAAU,GACd,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IACjE,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC;YAC1C,OAAO,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"} \ No newline at end of file diff --git a/apps/api/dist/common/platform-admin.js b/apps/api/dist/common/platform-admin.js new file mode 100644 index 0000000000000000000000000000000000000000..75a51c49a6840586b025edf4b59be908e27e0911 --- /dev/null +++ b/apps/api/dist/common/platform-admin.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.deriveAdminRole = deriveAdminRole; +exports.assertPlatformAdmin = assertPlatformAdmin; +exports.isPlatformAdmin = isPlatformAdmin; +const common_1 = require("@nestjs/common"); +const shared_types_1 = require("@micprotocol/shared-types"); +function deriveAdminRole(user) { + if (user.adminRole) + return user.adminRole; + if (user.isAdmin) + return shared_types_1.AdminRole.SUPER_ADMIN; + return null; +} +async function assertPlatformAdmin(prisma, userId) { + const user = await prisma.user.findUnique({ + where: { id: userId }, + select: { isAdmin: true, adminRole: true, suspendedAt: true }, + }); + if (!user) { + throw new common_1.ForbiddenException("Admin privileges required"); + } + if (user.suspendedAt) { + throw new common_1.ForbiddenException("Account is suspended"); + } + if (!deriveAdminRole(user)) { + throw new common_1.ForbiddenException("Admin privileges required"); + } +} +async function isPlatformAdmin(prisma, userId) { + try { + await assertPlatformAdmin(prisma, userId); + return true; + } + catch { + return false; + } +} +//# sourceMappingURL=platform-admin.js.map \ No newline at end of file diff --git a/apps/api/dist/common/platform-admin.js.map b/apps/api/dist/common/platform-admin.js.map new file mode 100644 index 0000000000000000000000000000000000000000..72a4a6c9b90d2f9304d4d7664fa70b97f2484e6f --- /dev/null +++ b/apps/api/dist/common/platform-admin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"platform-admin.js","sourceRoot":"","sources":["../../src/common/platform-admin.ts"],"names":[],"mappings":";;AAkBA,0CAOC;AAeD,kDAkBC;AAGD,0CAUC;AAvED,2CAAoD;AACpD,4DAAsD;AAiBtD,SAAgB,eAAe,CAAC,IAG/B;IACC,IAAI,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC,SAAsB,CAAC;IACvD,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,wBAAS,CAAC,WAAW,CAAC;IAC/C,OAAO,IAAI,CAAC;AACd,CAAC;AAeM,KAAK,UAAU,mBAAmB,CACvC,MAAqB,EACrB,MAAc;IAEd,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;QACxC,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACrB,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE;KAC9D,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,2BAAkB,CAAC,2BAA2B,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,MAAM,IAAI,2BAAkB,CAAC,sBAAsB,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,2BAAkB,CAAC,2BAA2B,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;AAGM,KAAK,UAAU,eAAe,CACnC,MAAqB,EACrB,MAAc;IAEd,IAAI,CAAC;QACH,MAAM,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/apps/api/dist/common/rate-limit/rate-limit.module.js b/apps/api/dist/common/rate-limit/rate-limit.module.js new file mode 100644 index 0000000000000000000000000000000000000000..459602f3067df3070ffb13b3458cfa574089f8de --- /dev/null +++ b/apps/api/dist/common/rate-limit/rate-limit.module.js @@ -0,0 +1,22 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RateLimitModule = void 0; +const common_1 = require("@nestjs/common"); +const token_bucket_service_1 = require("./token-bucket.service"); +let RateLimitModule = class RateLimitModule { +}; +exports.RateLimitModule = RateLimitModule; +exports.RateLimitModule = RateLimitModule = __decorate([ + (0, common_1.Global)(), + (0, common_1.Module)({ + providers: [token_bucket_service_1.TokenBucketService], + exports: [token_bucket_service_1.TokenBucketService], + }) +], RateLimitModule); +//# sourceMappingURL=rate-limit.module.js.map \ No newline at end of file diff --git a/apps/api/dist/common/rate-limit/rate-limit.module.js.map b/apps/api/dist/common/rate-limit/rate-limit.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..8520444d2ff51ba264bc80db4d257c4119317375 --- /dev/null +++ b/apps/api/dist/common/rate-limit/rate-limit.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rate-limit.module.js","sourceRoot":"","sources":["../../../src/common/rate-limit/rate-limit.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,iEAA4D;AAerD,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAL3B,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE,CAAC,yCAAkB,CAAC;QAC/B,OAAO,EAAE,CAAC,yCAAkB,CAAC;KAC9B,CAAC;GACW,eAAe,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/common/rate-limit/token-bucket.service.js b/apps/api/dist/common/rate-limit/token-bucket.service.js new file mode 100644 index 0000000000000000000000000000000000000000..7f6f32161af34251128ac2d07ff64761cd027fbd --- /dev/null +++ b/apps/api/dist/common/rate-limit/token-bucket.service.js @@ -0,0 +1,110 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var TokenBucketService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TokenBucketService = void 0; +const common_1 = require("@nestjs/common"); +const redis_constants_1 = require("../redis.constants"); +let TokenBucketService = class TokenBucketService { + static { TokenBucketService_1 = this; } + redis; + logger = new common_1.Logger(TokenBucketService_1.name); + static LUA_SCRIPT = ` + local key = KEYS[1] + local capacity = tonumber(ARGV[1]) + local rate = tonumber(ARGV[2]) + local now = tonumber(ARGV[3]) + local cost = tonumber(ARGV[4] or "1") + + local bucket = redis.call("HMGET", key, "tokens", "lastUpdate") + local tokens = tonumber(bucket[1]) + local lastUpdate = tonumber(bucket[2]) + + if tokens == nil then + tokens = capacity + lastUpdate = now + end + + local delta = math.max(0, now - lastUpdate) + tokens = math.min(capacity, tokens + (delta * rate / 1000)) + + local allowed = 0 + if tokens >= cost then + tokens = tokens - cost + allowed = 1 + end + + redis.call("HMSET", key, "tokens", tokens, "lastUpdate", now) + redis.call("EXPIRE", key, 3600) + + return {allowed, tokens, lastUpdate} + `; + constructor(redis) { + this.redis = redis; + } + async checkBucket(identifier, config) { + const key = `${config.keyPrefix || "tokenbucket"}:${identifier}`; + const now = Date.now(); + const cost = 1; + try { + const result = await this.redis.eval(TokenBucketService_1.LUA_SCRIPT, { + keys: [key], + arguments: [ + String(config.capacity), + String(config.refillRatePerSec), + String(now), + String(cost), + ], + }); + const allowed = Number(result[0]) === 1; + const remainingTokens = Number(result[1]) ?? 0; + const lastUpdate = Number(result[2]) ?? now; + const tokensNeeded = Math.max(0, capacityFor(config) - remainingTokens); + const resetAt = lastUpdate + Math.ceil((tokensNeeded / config.refillRatePerSec) * 1000); + return { + allowed, + remainingTokens, + resetAt, + capacity: config.capacity, + }; + } + catch (err) { + this.logger.warn(`Token bucket check failed for ${key}: ${err.message}`); + return { + allowed: true, + remainingTokens: config.capacity, + resetAt: now, + capacity: config.capacity, + }; + } + } + async resetBucket(identifier, keyPrefix = "tokenbucket") { + try { + await this.redis.del(`${keyPrefix}:${identifier}`); + } + catch (err) { + this.logger.warn(`Token bucket reset failed for ${identifier}: ${err.message}`); + } + } +}; +exports.TokenBucketService = TokenBucketService; +exports.TokenBucketService = TokenBucketService = TokenBucketService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(0, (0, common_1.Inject)(redis_constants_1.REDIS_CLIENT)), + __metadata("design:paramtypes", [Object]) +], TokenBucketService); +function capacityFor(config) { + return config.capacity; +} +//# sourceMappingURL=token-bucket.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/rate-limit/token-bucket.service.js.map b/apps/api/dist/common/rate-limit/token-bucket.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..08a075b5e29bceca36fb8ac39777d6f02fc22aee --- /dev/null +++ b/apps/api/dist/common/rate-limit/token-bucket.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"token-bucket.service.js","sourceRoot":"","sources":["../../../src/common/rate-limit/token-bucket.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAA4D;AAE5D,wDAAkD;AAsC3C,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;;IAoCsB;IAnClC,MAAM,GAAG,IAAI,eAAM,CAAC,oBAAkB,CAAC,IAAI,CAAC,CAAC;IAItD,MAAM,CAAU,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BpC,CAAC;IAEF,YAAmD,KAAsB;QAAtB,UAAK,GAAL,KAAK,CAAiB;IAAG,CAAC;IAM7E,KAAK,CAAC,WAAW,CACf,UAAkB,EAClB,MAAyB;QAEzB,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,SAAS,IAAI,aAAa,IAAI,UAAU,EAAE,CAAC;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,CAAC,CAAC;QAEf,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,oBAAkB,CAAC,UAAU,EAAE;gBAClE,IAAI,EAAE,CAAC,GAAG,CAAC;gBACX,SAAS,EAAE;oBACT,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;oBACvB,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC;oBAC/B,MAAM,CAAC,GAAG,CAAC;oBACX,MAAM,CAAC,IAAI,CAAC;iBACb;aACF,CAAc,CAAC;YAEhB,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACxC,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;YAE5C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC;YACxE,MAAM,OAAO,GACX,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC,CAAC;YAE1E,OAAO;gBACL,OAAO;gBACP,eAAe;gBACf,OAAO;gBACP,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,iCAAiC,GAAG,KAAM,GAAa,CAAC,OAAO,EAAE,CAClE,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,eAAe,EAAE,MAAM,CAAC,QAAQ;gBAChC,OAAO,EAAE,GAAG;gBACZ,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B,CAAC;QACJ,CAAC;IACH,CAAC;IAKD,KAAK,CAAC,WAAW,CAAC,UAAkB,EAAE,YAAoB,aAAa;QACrE,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,IAAI,UAAU,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iCAAiC,UAAU,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;;AAlGU,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;IAqCE,WAAA,IAAA,eAAM,EAAC,8BAAY,CAAC,CAAA;;GApCtB,kBAAkB,CAmG9B;AAED,SAAS,WAAW,CAAC,MAAyB;IAC5C,OAAO,MAAM,CAAC,QAAQ,CAAC;AACzB,CAAC"} \ No newline at end of file diff --git a/apps/api/dist/common/redis-idempotency.service.js b/apps/api/dist/common/redis-idempotency.service.js new file mode 100644 index 0000000000000000000000000000000000000000..73a268f0b44c8acaf960f47a7e0109c1d7365698 --- /dev/null +++ b/apps/api/dist/common/redis-idempotency.service.js @@ -0,0 +1,110 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var RedisIdempotencyService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RedisIdempotencyService = void 0; +const common_1 = require("@nestjs/common"); +const common_2 = require("@nestjs/common"); +const redis_constants_1 = require("./redis.constants"); +let RedisIdempotencyService = RedisIdempotencyService_1 = class RedisIdempotencyService { + redis; + logger = new common_1.Logger(RedisIdempotencyService_1.name); + DEFAULT_TTL = 24 * 60 * 60; + constructor(redis) { + this.redis = redis; + } + async checkAndMark(key, ttlSeconds = this.DEFAULT_TTL) { + const fullKey = `idempotency:${key}`; + try { + const wasSet = await this.redis.set(fullKey, "processing", { + NX: true, + EX: ttlSeconds, + }); + if (wasSet) { + return { isNew: true }; + } + const existingValue = await this.redis.get(fullKey); + if (existingValue === "processing") { + throw new Error(`Request with idempotency key ${key} is currently being processed`); + } + try { + const parsed = JSON.parse(existingValue || "{}"); + return { isNew: false, existingResult: parsed }; + } + catch { + return { isNew: false, existingResult: null }; + } + } + catch (error) { + if (error instanceof Error && error.message.includes("currently being processed")) { + throw error; + } + this.logger.error(`Idempotency check failed for ${key}`, error); + return { isNew: true }; + } + } + async storeResult(key, result, ttlSeconds = this.DEFAULT_TTL) { + const fullKey = `idempotency:${key}`; + try { + await this.redis.set(fullKey, JSON.stringify(result), { + EX: ttlSeconds, + }); + } + catch (error) { + this.logger.error(`Failed to store idempotency result for ${key}`, error); + } + } + async markFailed(key, error, ttlSeconds = this.DEFAULT_TTL) { + const fullKey = `idempotency:${key}`; + try { + await this.redis.set(fullKey, JSON.stringify({ + error: error.message, + failed: true, + }), { + EX: ttlSeconds, + }); + } + catch (err) { + this.logger.error(`Failed to mark idempotency key as failed for ${key}`, err); + } + } + async getResult(key) { + const fullKey = `idempotency:${key}`; + try { + const value = await this.redis.get(fullKey); + if (!value) + return null; + const parsed = JSON.parse(value); + if (parsed === "processing" || parsed.failed) { + return null; + } + return parsed; + } + catch (error) { + this.logger.error(`Failed to get idempotency result for ${key}`, error); + return null; + } + } + async delete(key) { + const fullKey = `idempotency:${key}`; + await this.redis.del(fullKey); + } +}; +exports.RedisIdempotencyService = RedisIdempotencyService; +exports.RedisIdempotencyService = RedisIdempotencyService = RedisIdempotencyService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(0, (0, common_2.Inject)(redis_constants_1.REDIS_CLIENT)), + __metadata("design:paramtypes", [Object]) +], RedisIdempotencyService); +//# sourceMappingURL=redis-idempotency.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/redis-idempotency.service.js.map b/apps/api/dist/common/redis-idempotency.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..69751ee2a70c9a160cb97224fa07c1817103e27b --- /dev/null +++ b/apps/api/dist/common/redis-idempotency.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"redis-idempotency.service.js","sourceRoot":"","sources":["../../src/common/redis-idempotency.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/redis-lock.service.js b/apps/api/dist/common/redis-lock.service.js new file mode 100644 index 0000000000000000000000000000000000000000..4a513c9e98509d875c3b3d93496bc0c19f5d0a94 --- /dev/null +++ b/apps/api/dist/common/redis-lock.service.js @@ -0,0 +1,86 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var RedisLockService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RedisLockService = void 0; +const common_1 = require("@nestjs/common"); +const crypto_1 = require("crypto"); +const redis_constants_1 = require("./redis.constants"); +let RedisLockService = class RedisLockService { + static { RedisLockService_1 = this; } + redis; + logger = new common_1.Logger(RedisLockService_1.name); + static EXTEND_SCRIPT = ` + if redis.call("get", KEYS[1]) == ARGV[1] then + return redis.call("pexpire", KEYS[1], ARGV[2]) + else + return 0 + end + `; + static RELEASE_SCRIPT = ` + if redis.call("get", KEYS[1]) == ARGV[1] then + return redis.call("del", KEYS[1]) + else + return 0 + end + `; + constructor(redis) { + this.redis = redis; + } + async withLock(key, fn, options = {}) { + const ttlMs = options.ttlMs ?? 5000; + const retries = options.retries ?? 0; + const retryDelayMs = options.retryDelayMs ?? 50; + const token = (0, crypto_1.randomUUID)(); + const acquired = await this.acquire(key, token, ttlMs, retries, retryDelayMs); + if (!acquired) { + throw new Error(`Could not acquire lock ${key} after ${retries + 1} attempts`); + } + try { + return await fn(); + } + finally { + await this.release(key, token); + } + } + async acquire(key, token, ttlMs, retries, retryDelayMs) { + for (let attempt = 0; attempt <= retries; attempt++) { + const result = await this.redis.set(key, token, { NX: true, PX: ttlMs }); + if (result === "OK") + return true; + if (attempt < retries) { + await new Promise((resolve) => setTimeout(resolve, retryDelayMs)); + } + } + return false; + } + async release(key, token) { + try { + await this.redis.eval(RedisLockService_1.RELEASE_SCRIPT, { + keys: [key], + arguments: [token], + }); + } + catch (err) { + this.logger.warn(`Failed to release lock ${key}: ${err}`); + } + } +}; +exports.RedisLockService = RedisLockService; +exports.RedisLockService = RedisLockService = RedisLockService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(0, (0, common_1.Inject)(redis_constants_1.REDIS_CLIENT)), + __metadata("design:paramtypes", [Object]) +], RedisLockService); +//# sourceMappingURL=redis-lock.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/redis-lock.service.js.map b/apps/api/dist/common/redis-lock.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..9e3bf465ecd73f81fb67df8662da759655ccb81c --- /dev/null +++ b/apps/api/dist/common/redis-lock.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"redis-lock.service.js","sourceRoot":"","sources":["../../src/common/redis-lock.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAA4D;AAC5D,mCAAoC;AACpC,uDAAiD;AAkB1C,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;;IAqBwB;IApBlC,MAAM,GAAG,IAAI,eAAM,CAAC,kBAAgB,CAAC,IAAI,CAAC,CAAC;IAGpD,MAAM,CAAU,aAAa,GAAG;;;;;;GAMvC,CAAC;IAGM,MAAM,CAAU,cAAc,GAAG;;;;;;GAMxC,CAAC;IAEF,YAAmD,KAAsB;QAAtB,UAAK,GAAL,KAAK,CAAiB;IAAG,CAAC;IAc7E,KAAK,CAAC,QAAQ,CACZ,GAAW,EACX,EAAoB,EACpB,UAAuE,EAAE;QAEzE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;QACpC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;QACrC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,IAAA,mBAAU,GAAE,CAAC;QAE3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QAC9E,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,UAAU,OAAO,GAAG,CAAC,WAAW,CAAC,CAAC;QACjF,CAAC;QAED,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;gBAAS,CAAC;YACT,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,GAAW,EACX,KAAa,EACb,KAAa,EACb,OAAe,EACf,YAAoB;QAEpB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;YACpD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YACzE,IAAI,MAAM,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAC;YACjC,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC;gBACtB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,KAAa;QAC9C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAgB,CAAC,cAAc,EAAE;gBACrD,IAAI,EAAE,CAAC,GAAG,CAAC;gBACX,SAAS,EAAE,CAAC,KAAK,CAAC;aACnB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0BAA0B,GAAG,KAAK,GAAG,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;;AAnFU,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;IAsBE,WAAA,IAAA,eAAM,EAAC,8BAAY,CAAC,CAAA;;GArBtB,gBAAgB,CAoF5B"} \ No newline at end of file diff --git a/apps/api/dist/common/redis-rate-limiter.service.js b/apps/api/dist/common/redis-rate-limiter.service.js new file mode 100644 index 0000000000000000000000000000000000000000..487a3f816cbe84423fb580b281d9808bc1897932 --- /dev/null +++ b/apps/api/dist/common/redis-rate-limiter.service.js @@ -0,0 +1,109 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var RedisRateLimiterService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RedisRateLimiterService = void 0; +const common_1 = require("@nestjs/common"); +const common_2 = require("@nestjs/common"); +const redis_constants_1 = require("./redis.constants"); +let RedisRateLimiterService = RedisRateLimiterService_1 = class RedisRateLimiterService { + redis; + logger = new common_1.Logger(RedisRateLimiterService_1.name); + constructor(redis) { + this.redis = redis; + } + async checkLimit(identifier, config) { + const key = `${config.keyPrefix || "ratelimit"}:${identifier}`; + const now = Date.now(); + const windowStart = now - config.windowMs; + try { + const multi = this.redis.multi(); + multi.zRemRangeByScore(key, 0, windowStart); + multi.zCard(key); + multi.zAdd(key, { score: now, value: `${now}:${Math.random()}` }); + multi.expire(key, Math.ceil(config.windowMs / 1000) + 1); + const results = await multi.exec(); + const execResults = results; + const currentCount = execResults?.[1] ?? 0; + const allowed = currentCount < config.max; + const remaining = Math.max(0, config.max - currentCount - 1); + if (!allowed) { + await this.redis.zRem(key, `${now}:${Math.random()}`); + } + const oldest = await this.redis.zRange(key, 0, 0); + const resetAt = oldest.length > 0 + ? parseInt(oldest[0].split(":")[0]) + config.windowMs + : now + config.windowMs; + return { + allowed, + remaining, + resetAt, + total: config.max, + }; + } + catch (error) { + this.logger.error(`Rate limit check failed for ${identifier}`, error); + return { + allowed: true, + remaining: config.max, + resetAt: now + config.windowMs, + total: config.max, + }; + } + } + async getUsage(identifier, config) { + const key = `${config.keyPrefix || "ratelimit"}:${identifier}`; + const now = Date.now(); + const windowStart = now - config.windowMs; + try { + await this.redis.zRemRangeByScore(key, 0, windowStart); + const currentCount = await this.redis.zCard(key); + const oldest = await this.redis.zRange(key, 0, 0); + const resetAt = oldest.length > 0 + ? parseInt(oldest[0].split(":")[0]) + config.windowMs + : now + config.windowMs; + return { + allowed: currentCount < config.max, + remaining: Math.max(0, config.max - currentCount), + resetAt, + total: config.max, + }; + } + catch (error) { + this.logger.error(`Rate limit usage check failed for ${identifier}`, error); + return { + allowed: true, + remaining: config.max, + resetAt: now + config.windowMs, + total: config.max, + }; + } + } + async resetLimit(identifier, keyPrefix = "ratelimit") { + const key = `${keyPrefix}:${identifier}`; + await this.redis.del(key); + } + createRateLimiter(config) { + return async (identifier) => { + return this.checkLimit(identifier, config); + }; + } +}; +exports.RedisRateLimiterService = RedisRateLimiterService; +exports.RedisRateLimiterService = RedisRateLimiterService = RedisRateLimiterService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(0, (0, common_2.Inject)(redis_constants_1.REDIS_CLIENT)), + __metadata("design:paramtypes", [Object]) +], RedisRateLimiterService); +//# sourceMappingURL=redis-rate-limiter.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/redis-rate-limiter.service.js.map b/apps/api/dist/common/redis-rate-limiter.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..5ed014a4668014ef9d57f7f7eb8a7fce28cabe28 --- /dev/null +++ b/apps/api/dist/common/redis-rate-limiter.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"redis-rate-limiter.service.js","sourceRoot":"","sources":["../../src/common/redis-rate-limiter.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/redis.constants.js b/apps/api/dist/common/redis.constants.js new file mode 100644 index 0000000000000000000000000000000000000000..5cfcdf772931d69cabf901e443e2d231d2b40f1a --- /dev/null +++ b/apps/api/dist/common/redis.constants.js @@ -0,0 +1,5 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.REDIS_CLIENT = void 0; +exports.REDIS_CLIENT = "REDIS_CLIENT"; +//# sourceMappingURL=redis.constants.js.map \ No newline at end of file diff --git a/apps/api/dist/common/redis.constants.js.map b/apps/api/dist/common/redis.constants.js.map new file mode 100644 index 0000000000000000000000000000000000000000..be95bcac1238f8e88497cd19be802fbb15f90e25 --- /dev/null +++ b/apps/api/dist/common/redis.constants.js.map @@ -0,0 +1 @@ +{"version":3,"file":"redis.constants.js","sourceRoot":"","sources":["../../src/common/redis.constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,YAAY,GAAG,cAAc,CAAC"} \ No newline at end of file diff --git a/apps/api/dist/common/route-collision.service.js b/apps/api/dist/common/route-collision.service.js new file mode 100644 index 0000000000000000000000000000000000000000..d81479d8566982273cd06e80246367b7312997c0 --- /dev/null +++ b/apps/api/dist/common/route-collision.service.js @@ -0,0 +1,72 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var RouteCollisionService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RouteCollisionService = void 0; +const common_1 = require("@nestjs/common"); +const core_1 = require("@nestjs/core"); +let RouteCollisionService = RouteCollisionService_1 = class RouteCollisionService { + adapterHost; + logger = new common_1.Logger(RouteCollisionService_1.name); + constructor(adapterHost) { + this.adapterHost = adapterHost; + } + onApplicationBootstrap() { + const instance = this.adapterHost.httpAdapter?.getInstance?.(); + this.assertNoRouteCollisions(instance); + } + assertNoRouteCollisions(expressApp) { + const router = expressApp?.router ?? expressApp?._router; + if (!router || !Array.isArray(router?.stack)) { + this.logger.warn("Could not introspect the Express router — route-collision check skipped"); + return; + } + const counts = new Map(); + const collisions = new Set(); + const scan = (stack, prefix) => { + for (const layer of stack) { + if (!layer) + continue; + if (layer.route) { + const path = ((prefix || "") + layer.route.path).replace(/\/+$/, "") || "/"; + const methods = Object.keys(layer.route.methods).filter((m) => m !== "_all"); + for (const method of methods) { + const key = `${method.toUpperCase()} ${path}`; + const next = (counts.get(key) ?? 0) + 1; + counts.set(key, next); + if (next > 1) + collisions.add(key); + } + } + else if (layer.name === "router" && + layer.handle && + Array.isArray(layer.handle.stack)) { + scan(layer.handle.stack, (prefix || "") + (layer.path || "")); + } + } + }; + scan(router.stack, ""); + if (collisions.size === 0) { + this.logger.log("Route-collision check passed"); + return; + } + const message = "Route collisions detected at startup — one handler silently shadows " + + "the others (this is usually a security bug):\n" + + [...collisions].map((c) => ` ${c}`).join("\n"); + this.logger.error(message); + } +}; +exports.RouteCollisionService = RouteCollisionService; +exports.RouteCollisionService = RouteCollisionService = RouteCollisionService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [core_1.HttpAdapterHost]) +], RouteCollisionService); +//# sourceMappingURL=route-collision.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/route-collision.service.js.map b/apps/api/dist/common/route-collision.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..525273e1e97ec3200bc3f75ace20813ed461d207 --- /dev/null +++ b/apps/api/dist/common/route-collision.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"route-collision.service.js","sourceRoot":"","sources":["../../src/common/route-collision.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/safe-fetch.js b/apps/api/dist/common/safe-fetch.js new file mode 100644 index 0000000000000000000000000000000000000000..39cc5db8b5ae393bd917002f63132b74bc5bc9ec --- /dev/null +++ b/apps/api/dist/common/safe-fetch.js @@ -0,0 +1,102 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.assertSafeUrl = assertSafeUrl; +exports.safeFetch = safeFetch; +const common_1 = require("@nestjs/common"); +const LOOPBACK_HOSTS = new Set([ + "localhost", + "0.0.0.0", + "127.0.0.1", + "::1", + "[::1]", +]); +function isPrivateIPv4(ip) { + const octets = ip.split(".").map(Number); + if (octets.length !== 4 || octets.some((o) => Number.isNaN(o))) + return false; + const [a, b] = octets; + if (a === 10) + return true; + if (a === 172 && b >= 16 && b <= 31) + return true; + if (a === 192 && b === 168) + return true; + if (a === 169 && b === 254) + return true; + if (a === 127) + return true; + if (a === 0) + return true; + if (a === 100 && b >= 64 && b <= 127) + return true; + return false; +} +function isPrivateIPv6(ip) { + const lower = ip.toLowerCase(); + if (lower === "::1" || lower === "0:0:0:0:0:0:0:1") + return true; + if (lower.startsWith("fc") || lower.startsWith("fd")) + return true; + if (lower.startsWith("fe8") || lower.startsWith("fe9") || + lower.startsWith("fea") || lower.startsWith("feb")) + return true; + const mapped = lower.match(/^::ffff:([\da-f.:]+)$/i); + if (mapped) + return isPrivateIPv4(mapped[1].replace(/^0+|:0+:/g, "")); + return false; +} +function isPrivateHost(hostname) { + const lower = hostname.toLowerCase(); + if (LOOPBACK_HOSTS.has(lower)) + return true; + if (lower.endsWith(".localhost") || lower.endsWith(".local")) + return true; + if (/^\d{1,3}(\.\d{1,3}){3}$/.test(lower)) + return isPrivateIPv4(lower); + if (lower.includes(":")) + return isPrivateIPv6(lower); + return false; +} +function assertSafeUrl(rawUrl, opts = {}) { + let parsed; + try { + parsed = new URL(rawUrl); + } + catch { + throw new common_1.BadRequestException(`Invalid URL: ${rawUrl}`); + } + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { + throw new common_1.BadRequestException(`Disallowed URL protocol "${parsed.protocol}" (must be http or https)`); + } + const hostname = parsed.hostname.toLowerCase(); + if (opts.allowedHosts && opts.allowedHosts.length > 0) { + if (!opts.allowedHosts.includes(hostname)) { + throw new common_1.BadRequestException(`Host "${hostname}" is not in the allowlist`); + } + } + else if (opts.allowedHostSuffixes && opts.allowedHostSuffixes.length > 0) { + const matches = opts.allowedHostSuffixes.some((suffix) => hostname === suffix.slice(1) || hostname.endsWith(suffix)); + if (!matches) { + throw new common_1.BadRequestException(`Host "${hostname}" does not match any allowed suffix`); + } + } + else if (isPrivateHost(hostname)) { + throw new common_1.BadRequestException(`URL points at a private/internal address (${hostname})`); + } + return parsed; +} +async function safeFetch(rawUrl, init = {}) { + const parsed = assertSafeUrl(rawUrl); + const maxContentLength = init.maxContentLength ?? 500 * 1024 * 1024; + const response = await fetch(parsed.toString(), init); + if (!response.ok) { + throw new Error(`Fetch failed: ${response.status} ${response.statusText}`); + } + const contentLength = response.headers.get("content-length"); + if (contentLength && Number(contentLength) > maxContentLength) { + response.body?.cancel(); + throw new Error(`Response too large: ${contentLength} bytes exceeds limit of ${maxContentLength}`); + } + return response; +} +//# sourceMappingURL=safe-fetch.js.map \ No newline at end of file diff --git a/apps/api/dist/common/safe-fetch.js.map b/apps/api/dist/common/safe-fetch.js.map new file mode 100644 index 0000000000000000000000000000000000000000..5f02499dab2689f2dc7b405012e4682da34f3136 --- /dev/null +++ b/apps/api/dist/common/safe-fetch.js.map @@ -0,0 +1 @@ +{"version":3,"file":"safe-fetch.js","sourceRoot":"","sources":["../../src/common/safe-fetch.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/storage/r2-storage.service.js b/apps/api/dist/common/storage/r2-storage.service.js new file mode 100644 index 0000000000000000000000000000000000000000..4aabfa09561c64660b47aca4dac44797ad64f549 --- /dev/null +++ b/apps/api/dist/common/storage/r2-storage.service.js @@ -0,0 +1,151 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var R2StorageService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.R2StorageService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const client_s3_1 = require("@aws-sdk/client-s3"); +const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner"); +let R2StorageService = R2StorageService_1 = class R2StorageService { + configService; + logger = new common_1.Logger(R2StorageService_1.name); + s3Client = null; + bucket = ""; + publicBaseUrl = ""; + isConfigured = false; + constructor(configService) { + this.configService = configService; + } + onModuleInit() { + const accessKeyId = this.configService.get("R2_ACCESS_KEY_ID", ""); + const secretAccessKey = this.configService.get("R2_SECRET_ACCESS_KEY", ""); + const endpoint = this.configService.get("R2_ENDPOINT", ""); + this.bucket = this.configService.get("R2_BUCKET", ""); + if (!accessKeyId || !secretAccessKey || !endpoint || !this.bucket) { + this.logger.warn("R2 storage not configured (missing R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_ENDPOINT, or R2_BUCKET). " + + "Export uploads will be skipped; output URLs will be local file paths."); + return; + } + this.s3Client = new client_s3_1.S3Client({ + region: "auto", + endpoint, + credentials: { + accessKeyId, + secretAccessKey, + }, + forcePathStyle: endpoint.includes("localhost") || endpoint.includes("minio"), + }); + const customPublicUrl = this.configService.get("STORAGE_PUBLIC_URL"); + if (customPublicUrl) { + this.publicBaseUrl = customPublicUrl; + } + else if (endpoint.includes("cloudflarestorage.com")) { + this.publicBaseUrl = endpoint.replace(/\.r2\.cloudflarestorage\.com$/, `.r2.dev`); + } + else { + this.publicBaseUrl = `${endpoint}/${this.bucket}`; + } + this.isConfigured = true; + this.logger.log(`Storage configured for bucket: ${this.bucket}`); + } + onModuleDestroy() { + this.s3Client = null; + } + isAvailable() { + return this.isConfigured && this.s3Client !== null; + } + async uploadBuffer(key, body, contentType = "video/mp4") { + if (!this.isAvailable() || !this.s3Client) { + throw new Error("R2 storage is not configured. Cannot upload."); + } + await this.s3Client.send(new client_s3_1.PutObjectCommand({ + Bucket: this.bucket, + Key: key, + Body: body, + ContentType: contentType, + })); + const url = this.getPublicUrl(key); + this.logger.log(`Uploaded ${body.length} bytes to R2: ${key}`); + return { key, url }; + } + async getPresignedUrl(key, expiresIn = 3600) { + if (!this.isAvailable() || !this.s3Client) { + throw new Error("R2 storage is not configured. Cannot generate presigned URL."); + } + const command = new client_s3_1.GetObjectCommand({ + Bucket: this.bucket, + Key: key, + }); + return (0, s3_request_presigner_1.getSignedUrl)(this.s3Client, command, { expiresIn }); + } + getPublicUrl(key) { + return `${this.publicBaseUrl}/${key}`; + } + async getObjectBuffer(key) { + if (!this.isAvailable() || !this.s3Client) { + throw new Error("R2 storage is not configured."); + } + const command = new client_s3_1.GetObjectCommand({ + Bucket: this.bucket, + Key: key, + }); + const response = await this.s3Client.send(command); + const byteArray = await response.Body?.transformToByteArray(); + if (!byteArray) { + throw new Error(`Object ${key} has no body`); + } + return Buffer.from(byteArray); + } + async objectExists(key) { + if (!this.isAvailable() || !this.s3Client) { + return false; + } + try { + const headResult = await this.s3Client.send(new client_s3_1.HeadObjectCommand({ + Bucket: this.bucket, + Key: key, + })); + return headResult.ContentLength !== undefined; + } + catch { + return false; + } + } + async deleteObject(key) { + if (!this.isAvailable() || !this.s3Client) { + this.logger.warn("R2 not configured; skipping delete."); + return; + } + await this.s3Client.send(new client_s3_1.DeleteObjectCommand({ + Bucket: this.bucket, + Key: key, + })); + this.logger.log(`Deleted R2 object: ${key}`); + } + generateExportKey(clipId, platform, extension = "mp4") { + const timestamp = Date.now(); + const random = Math.random().toString(36).substring(2, 8); + return `exports/${clipId}/${platform}/${timestamp}-${random}.${extension}`; + } + generateChunkKey(trackId, chunkIndex) { + return `audio/${trackId}/chunk_${chunkIndex}.raw`; + } + generateMasterTrackKey(trackId, extension = "wav") { + return `audio/${trackId}/master.${extension}`; + } +}; +exports.R2StorageService = R2StorageService; +exports.R2StorageService = R2StorageService = R2StorageService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [config_1.ConfigService]) +], R2StorageService); +//# sourceMappingURL=r2-storage.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/storage/r2-storage.service.js.map b/apps/api/dist/common/storage/r2-storage.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..87a09a64794abb78d17f8b7d0be339035ccad97e --- /dev/null +++ b/apps/api/dist/common/storage/r2-storage.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"r2-storage.service.js","sourceRoot":"","sources":["../../../src/common/storage/r2-storage.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/common/storage/storage.module.js b/apps/api/dist/common/storage/storage.module.js new file mode 100644 index 0000000000000000000000000000000000000000..386b02b468bd5a8d00b18d50a6cbc3b90e53d51a --- /dev/null +++ b/apps/api/dist/common/storage/storage.module.js @@ -0,0 +1,22 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.StorageModule = void 0; +const common_1 = require("@nestjs/common"); +const r2_storage_service_1 = require("./r2-storage.service"); +let StorageModule = class StorageModule { +}; +exports.StorageModule = StorageModule; +exports.StorageModule = StorageModule = __decorate([ + (0, common_1.Global)(), + (0, common_1.Module)({ + providers: [r2_storage_service_1.R2StorageService], + exports: [r2_storage_service_1.R2StorageService], + }) +], StorageModule); +//# sourceMappingURL=storage.module.js.map \ No newline at end of file diff --git a/apps/api/dist/common/storage/storage.module.js.map b/apps/api/dist/common/storage/storage.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..618c42ec011f948eb3aa5d0ed4bc3153e94a518d --- /dev/null +++ b/apps/api/dist/common/storage/storage.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"storage.module.js","sourceRoot":"","sources":["../../../src/common/storage/storage.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,6DAAwD;AAOjD,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IALzB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACJ,SAAS,EAAE,CAAC,qCAAgB,CAAC;QAC7B,OAAO,EAAE,CAAC,qCAAgB,CAAC;KAC9B,CAAC;GACW,aAAa,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/common/subdomain-resolver.service.js b/apps/api/dist/common/subdomain-resolver.service.js new file mode 100644 index 0000000000000000000000000000000000000000..2518a171b3c0a252894e20f050cb9b0a978ba1dc --- /dev/null +++ b/apps/api/dist/common/subdomain-resolver.service.js @@ -0,0 +1,61 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var SubdomainResolverService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SubdomainResolverService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const shared_types_1 = require("@micprotocol/shared-types"); +let SubdomainResolverService = SubdomainResolverService_1 = class SubdomainResolverService { + prisma; + logger = new common_1.Logger(SubdomainResolverService_1.name); + constructor(prisma) { + this.prisma = prisma; + } + async resolveBySubdomain(subdomain, options = {}) { + if (!subdomain) + return null; + const slug = subdomain.toLowerCase().trim(); + if (!slug) + return null; + if ((0, shared_types_1.isReservedSubdomain)(slug)) { + return null; + } + const workspace = await this.prisma.workspace.findUnique({ + where: { slug }, + select: { + id: true, + name: true, + slug: true, + subscriptionPlanId: true, + subscriptionStatus: true, + ...(options.select ?? {}), + }, + }); + if (!workspace) { + this.logger.debug(`No workspace found for subdomain "${slug}"`); + return null; + } + return { + workspaceId: workspace.id, + workspaceName: workspace.name, + slug: workspace.slug, + subscriptionPlanId: workspace.subscriptionPlanId, + subscriptionStatus: workspace.subscriptionStatus, + }; + } +}; +exports.SubdomainResolverService = SubdomainResolverService; +exports.SubdomainResolverService = SubdomainResolverService = SubdomainResolverService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], SubdomainResolverService); +//# sourceMappingURL=subdomain-resolver.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/subdomain-resolver.service.js.map b/apps/api/dist/common/subdomain-resolver.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..b051cae7dd27712717dd42583965f6262c6ec09c --- /dev/null +++ b/apps/api/dist/common/subdomain-resolver.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"subdomain-resolver.service.js","sourceRoot":"","sources":["../../src/common/subdomain-resolver.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AAGpD,6DAAyD;AACzD,4DAAgE;AAiCzD,IAAM,wBAAwB,gCAA9B,MAAM,wBAAwB;IAGJ;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,0BAAwB,CAAC,IAAI,CAAC,CAAC;IAEpE,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAYtD,KAAK,CAAC,kBAAkB,CACpB,SAAoC,EACpC,UAA+C,EAAE;QAEjD,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC;QAE5B,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,IAAI,IAAA,kCAAmB,EAAC,IAAI,CAAC,EAAE,CAAC;YAI5B,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YACrD,KAAK,EAAE,EAAE,IAAI,EAAE;YACf,MAAM,EAAE;gBACJ,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,IAAI;gBACV,IAAI,EAAE,IAAI;gBACV,kBAAkB,EAAE,IAAI;gBACxB,kBAAkB,EAAE,IAAI;gBACxB,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;aAC5B;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,IAAI,GAAG,CAAC,CAAC;YAChE,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,OAAO;YACH,WAAW,EAAE,SAAS,CAAC,EAAE;YACzB,aAAa,EAAE,SAAS,CAAC,IAAI;YAC7B,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,kBAAkB,EAAE,SAAS,CAAC,kBAAkB;YAChD,kBAAkB,EAAE,SAAS,CAAC,kBAAkB;SACnD,CAAC;IACN,CAAC;CACJ,CAAA;AAvDY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAI4B,8BAAa;GAHzC,wBAAwB,CAuDpC"} \ No newline at end of file diff --git a/apps/api/dist/common/tracing/trace.interceptor.js b/apps/api/dist/common/tracing/trace.interceptor.js new file mode 100644 index 0000000000000000000000000000000000000000..77a6f39bcab50cf6e78995505afa6a3370187206 --- /dev/null +++ b/apps/api/dist/common/tracing/trace.interceptor.js @@ -0,0 +1,89 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var TraceInterceptor_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TraceInterceptor = void 0; +const common_1 = require("@nestjs/common"); +const operators_1 = require("rxjs/operators"); +const tracing_service_1 = require("./tracing.service"); +let TraceInterceptor = TraceInterceptor_1 = class TraceInterceptor { + tracingService; + logger = new common_1.Logger(TraceInterceptor_1.name); + constructor(tracingService) { + this.tracingService = tracingService; + } + intercept(context, next) { + const type = context.getType(); + if (type !== "http") { + return next.handle(); + } + const httpContext = context.switchToHttp(); + const req = httpContext.getRequest(); + const res = httpContext.getResponse(); + const traceId = (req.headers["x-trace-id"] || req.headers["x-request-id"] || this.tracingService.generateTraceId()); + const spanId = this.tracingService.generateSpanId(); + const startTime = Date.now(); + const startHr = process.hrtime.bigint(); + res.setHeader("X-Trace-Id", traceId); + const spanName = `${req.method} ${req.route?.path || req.url}`; + const traceCtx = { + traceId, + workspaceId: req.headers["x-workspace-id"], + userId: req.headers["x-user-id"], + }; + return this.tracingService.runInContext(traceCtx, () => { + return next.handle().pipe((0, operators_1.tap)(() => { + const durationNs = process.hrtime.bigint() - startHr; + const durationMs = Math.round(Number(durationNs) / 1e6); + res.setHeader("X-Response-Time", `${durationMs}ms`); + this.tracingService.recordSpan({ + traceId, + spanId, + name: spanName, + type: "http", + startTime, + durationMs, + statusCode: res.statusCode, + metadata: { + path: req.url, + method: req.method, + ip: req.ip, + }, + }); + }), (0, operators_1.catchError)((err) => { + const durationNs = process.hrtime.bigint() - startHr; + const durationMs = Math.round(Number(durationNs) / 1e6); + const statusCode = err?.status || err?.statusCode || 500; + this.tracingService.recordSpan({ + traceId, + spanId, + name: spanName, + type: "http", + startTime, + durationMs, + statusCode, + error: err?.message || String(err), + metadata: { + path: req.url, + method: req.method, + }, + }); + throw err; + })); + }); + } +}; +exports.TraceInterceptor = TraceInterceptor; +exports.TraceInterceptor = TraceInterceptor = TraceInterceptor_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [tracing_service_1.TracingService]) +], TraceInterceptor); +//# sourceMappingURL=trace.interceptor.js.map \ No newline at end of file diff --git a/apps/api/dist/common/tracing/trace.interceptor.js.map b/apps/api/dist/common/tracing/trace.interceptor.js.map new file mode 100644 index 0000000000000000000000000000000000000000..a8178aeafa5117571ef1ed9afc6d9efa7bc6bc02 --- /dev/null +++ b/apps/api/dist/common/tracing/trace.interceptor.js.map @@ -0,0 +1 @@ +{"version":3,"file":"trace.interceptor.js","sourceRoot":"","sources":["../../../src/common/tracing/trace.interceptor.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAMwB;AAExB,8CAAiD;AACjD,uDAAmD;AAG5C,IAAM,gBAAgB,wBAAtB,MAAM,gBAAgB;IAGE;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,kBAAgB,CAAC,IAAI,CAAC,CAAC;IAE5D,YAA6B,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAAG,CAAC;IAE/D,SAAS,CAAC,OAAyB,EAAE,IAAiB;QACpD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QAC/B,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,CAAC;QAED,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;QAC3C,MAAM,GAAG,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;QAEtC,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,eAAe,EAAE,CAAW,CAAC;QAC9H,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,EAAE,CAAC;QACpD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAGxC,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAErC,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,IAAI,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QAC/D,MAAM,QAAQ,GAAG;YACf,OAAO;YACP,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAW;YACpD,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,WAAW,CAAW;SAC3C,CAAC;QAEF,OAAO,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE;YACrD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CACvB,IAAA,eAAG,EAAC,GAAG,EAAE;gBACP,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC;gBACrD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC;gBACxD,GAAG,CAAC,SAAS,CAAC,iBAAiB,EAAE,GAAG,UAAU,IAAI,CAAC,CAAC;gBAEpD,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC;oBAC7B,OAAO;oBACP,MAAM;oBACN,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,MAAM;oBACZ,SAAS;oBACT,UAAU;oBACV,UAAU,EAAE,GAAG,CAAC,UAAU;oBAC1B,QAAQ,EAAE;wBACR,IAAI,EAAE,GAAG,CAAC,GAAG;wBACb,MAAM,EAAE,GAAG,CAAC,MAAM;wBAClB,EAAE,EAAE,GAAG,CAAC,EAAE;qBACX;iBACF,CAAC,CAAC;YACL,CAAC,CAAC,EACF,IAAA,sBAAU,EAAC,CAAC,GAAG,EAAE,EAAE;gBACjB,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC;gBACrD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC;gBACxD,MAAM,UAAU,GAAG,GAAG,EAAE,MAAM,IAAI,GAAG,EAAE,UAAU,IAAI,GAAG,CAAC;gBAEzD,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC;oBAC7B,OAAO;oBACP,MAAM;oBACN,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,MAAM;oBACZ,SAAS;oBACT,UAAU;oBACV,UAAU;oBACV,KAAK,EAAE,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC;oBAClC,QAAQ,EAAE;wBACR,IAAI,EAAE,GAAG,CAAC,GAAG;wBACb,MAAM,EAAE,GAAG,CAAC,MAAM;qBACnB;iBACF,CAAC,CAAC;gBAEH,MAAM,GAAG,CAAC;YACZ,CAAC,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AA7EY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;qCAIkC,gCAAc;GAHhD,gBAAgB,CA6E5B"} \ No newline at end of file diff --git a/apps/api/dist/common/tracing/tracing.controller.js b/apps/api/dist/common/tracing/tracing.controller.js new file mode 100644 index 0000000000000000000000000000000000000000..2053d0f7dbe90f54101429ca1f1ef524f076a1cf --- /dev/null +++ b/apps/api/dist/common/tracing/tracing.controller.js @@ -0,0 +1,54 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TracingController = void 0; +const common_1 = require("@nestjs/common"); +const tracing_service_1 = require("./tracing.service"); +let TracingController = class TracingController { + tracingService; + constructor(tracingService) { + this.tracingService = tracingService; + } + getRecentSpans(limit, minDurationMs) { + const parsedLimit = limit ? parseInt(limit, 10) : 50; + const parsedMinDuration = minDurationMs ? parseInt(minDurationMs, 10) : 0; + return { + spans: this.tracingService.getRecentSpans(parsedLimit, parsedMinDuration), + metrics: this.tracingService.getTraceMetrics(), + }; + } + getMetrics() { + return this.tracingService.getTraceMetrics(); + } +}; +exports.TracingController = TracingController; +__decorate([ + (0, common_1.Get)("spans"), + __param(0, (0, common_1.Query)("limit")), + __param(1, (0, common_1.Query)("minDurationMs")), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, String]), + __metadata("design:returntype", void 0) +], TracingController.prototype, "getRecentSpans", null); +__decorate([ + (0, common_1.Get)("metrics"), + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], TracingController.prototype, "getMetrics", null); +exports.TracingController = TracingController = __decorate([ + (0, common_1.Controller)("tracing"), + __metadata("design:paramtypes", [tracing_service_1.TracingService]) +], TracingController); +//# sourceMappingURL=tracing.controller.js.map \ No newline at end of file diff --git a/apps/api/dist/common/tracing/tracing.controller.js.map b/apps/api/dist/common/tracing/tracing.controller.js.map new file mode 100644 index 0000000000000000000000000000000000000000..bca95779e58c0cfeaa7f29edcbba563d786e3774 --- /dev/null +++ b/apps/api/dist/common/tracing/tracing.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tracing.controller.js","sourceRoot":"","sources":["../../../src/common/tracing/tracing.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAwD;AACxD,uDAAmD;AAG5C,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAAG,CAAC;IAG/D,cAAc,CACI,KAAc,EACN,aAAsB;QAE9C,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,MAAM,iBAAiB,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE1E,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,WAAW,EAAE,iBAAiB,CAAC;YACzE,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,eAAe,EAAE;SAC/C,CAAC;IACJ,CAAC;IAGD,UAAU;QACR,OAAO,IAAI,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC;IAC/C,CAAC;CACF,CAAA;AArBY,8CAAiB;AAI5B;IADC,IAAA,YAAG,EAAC,OAAO,CAAC;IAEV,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;IACd,WAAA,IAAA,cAAK,EAAC,eAAe,CAAC,CAAA;;;;uDASxB;AAGD;IADC,IAAA,YAAG,EAAC,SAAS,CAAC;;;;mDAGd;4BApBU,iBAAiB;IAD7B,IAAA,mBAAU,EAAC,SAAS,CAAC;qCAEyB,gCAAc;GADhD,iBAAiB,CAqB7B"} \ No newline at end of file diff --git a/apps/api/dist/common/tracing/tracing.module.js b/apps/api/dist/common/tracing/tracing.module.js new file mode 100644 index 0000000000000000000000000000000000000000..4ff573a537fd85ce4ed69c84029bcae0be36f699 --- /dev/null +++ b/apps/api/dist/common/tracing/tracing.module.js @@ -0,0 +1,32 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TracingModule = void 0; +const common_1 = require("@nestjs/common"); +const core_1 = require("@nestjs/core"); +const tracing_service_1 = require("./tracing.service"); +const trace_interceptor_1 = require("./trace.interceptor"); +const tracing_controller_1 = require("./tracing.controller"); +let TracingModule = class TracingModule { +}; +exports.TracingModule = TracingModule; +exports.TracingModule = TracingModule = __decorate([ + (0, common_1.Global)(), + (0, common_1.Module)({ + providers: [ + tracing_service_1.TracingService, + { + provide: core_1.APP_INTERCEPTOR, + useClass: trace_interceptor_1.TraceInterceptor, + }, + ], + controllers: [tracing_controller_1.TracingController], + exports: [tracing_service_1.TracingService], + }) +], TracingModule); +//# sourceMappingURL=tracing.module.js.map \ No newline at end of file diff --git a/apps/api/dist/common/tracing/tracing.module.js.map b/apps/api/dist/common/tracing/tracing.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..7ab4a00698fa6b9ae573b9deb4d5ad9b07651fcc --- /dev/null +++ b/apps/api/dist/common/tracing/tracing.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tracing.module.js","sourceRoot":"","sources":["../../../src/common/tracing/tracing.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,uCAA+C;AAC/C,uDAAmD;AACnD,2DAAuD;AACvD,6DAAyD;AAclD,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IAZzB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT,gCAAc;YACd;gBACE,OAAO,EAAE,sBAAe;gBACxB,QAAQ,EAAE,oCAAgB;aAC3B;SACF;QACD,WAAW,EAAE,CAAC,sCAAiB,CAAC;QAChC,OAAO,EAAE,CAAC,gCAAc,CAAC;KAC1B,CAAC;GACW,aAAa,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/common/tracing/tracing.service.js b/apps/api/dist/common/tracing/tracing.service.js new file mode 100644 index 0000000000000000000000000000000000000000..0940cece8b60b4abee3b3c895112a0c566e3c480 --- /dev/null +++ b/apps/api/dist/common/tracing/tracing.service.js @@ -0,0 +1,107 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var TracingService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TracingService = void 0; +const common_1 = require("@nestjs/common"); +const async_hooks_1 = require("async_hooks"); +const crypto = __importStar(require("crypto")); +let TracingService = TracingService_1 = class TracingService { + logger = new common_1.Logger(TracingService_1.name); + asyncLocalStorage = new async_hooks_1.AsyncLocalStorage(); + spanBuffer = []; + maxBufferSize = 1000; + generateTraceId() { + return `tr-${crypto.randomBytes(8).toString("hex")}`; + } + generateSpanId() { + return `sp-${crypto.randomBytes(4).toString("hex")}`; + } + runInContext(context, fn) { + return this.asyncLocalStorage.run(context, fn); + } + getTraceContext() { + return this.asyncLocalStorage.getStore(); + } + recordSpan(span) { + if (this.spanBuffer.length >= this.maxBufferSize) { + this.spanBuffer.shift(); + } + this.spanBuffer.push(span); + if (span.error) { + this.logger.warn(`[Trace: ${span.traceId}] ${span.name} failed after ${span.durationMs ?? 0}ms: ${span.error}`); + } + else if ((span.durationMs ?? 0) > 500) { + this.logger.warn(`[Trace: ${span.traceId}] SLOW SPAN ${span.name} took ${span.durationMs}ms`); + } + } + getRecentSpans(limit = 100, minDurationMs = 0) { + return this.spanBuffer + .filter((s) => (s.durationMs ?? 0) >= minDurationMs) + .slice(-limit) + .reverse(); + } + getTraceMetrics() { + if (this.spanBuffer.length === 0) { + return { totalSpans: 0, avgDurationMs: 0, p95DurationMs: 0, errorRatePercent: 0 }; + } + const durations = this.spanBuffer + .map((s) => s.durationMs ?? 0) + .sort((a, b) => a - b); + const sum = durations.reduce((acc, v) => acc + v, 0); + const avgDurationMs = Math.round(sum / durations.length); + const p95Index = Math.floor(durations.length * 0.95); + const p95DurationMs = durations[p95Index] ?? 0; + const errors = this.spanBuffer.filter((s) => s.error || (s.statusCode && s.statusCode >= 400)).length; + const errorRatePercent = Math.round((errors / durations.length) * 1000) / 10; + return { + totalSpans: this.spanBuffer.length, + avgDurationMs, + p95DurationMs, + errorRatePercent, + }; + } +}; +exports.TracingService = TracingService; +exports.TracingService = TracingService = TracingService_1 = __decorate([ + (0, common_1.Injectable)() +], TracingService); +//# sourceMappingURL=tracing.service.js.map \ No newline at end of file diff --git a/apps/api/dist/common/tracing/tracing.service.js.map b/apps/api/dist/common/tracing/tracing.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..224bd05a7a64b402658ceedfb8ec842fa7025caa --- /dev/null +++ b/apps/api/dist/common/tracing/tracing.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tracing.service.js","sourceRoot":"","sources":["../../../src/common/tracing/tracing.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/content-studio/content-studio.module.js b/apps/api/dist/content-studio/content-studio.module.js new file mode 100644 index 0000000000000000000000000000000000000000..7315642126b581ead7b8e1d1834fbe7d979b096b --- /dev/null +++ b/apps/api/dist/content-studio/content-studio.module.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ContentStudioModule = void 0; +const common_1 = require("@nestjs/common"); +const bullmq_1 = require("@nestjs/bullmq"); +const content_studio_service_1 = require("./content-studio.service"); +const content_studio_router_1 = require("./content-studio.router"); +const prisma_module_1 = require("../prisma/prisma.module"); +let ContentStudioModule = class ContentStudioModule { +}; +exports.ContentStudioModule = ContentStudioModule; +exports.ContentStudioModule = ContentStudioModule = __decorate([ + (0, common_1.Module)({ + imports: [ + prisma_module_1.PrismaModule, + bullmq_1.BullModule.registerQueue({ name: "content" }), + bullmq_1.BullModule.registerQueue({ name: "clip-scoring" }), + ], + providers: [content_studio_service_1.ContentStudioService, content_studio_router_1.ContentStudioRouter], + exports: [content_studio_service_1.ContentStudioService, content_studio_router_1.ContentStudioRouter], + }) +], ContentStudioModule); +//# sourceMappingURL=content-studio.module.js.map \ No newline at end of file diff --git a/apps/api/dist/content-studio/content-studio.module.js.map b/apps/api/dist/content-studio/content-studio.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..4dd4acd4380acbc8bde143ffa8714657777a024e --- /dev/null +++ b/apps/api/dist/content-studio/content-studio.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"content-studio.module.js","sourceRoot":"","sources":["../../src/content-studio/content-studio.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA4C;AAE5C,qEAAgE;AAChE,mEAA8D;AAC9D,2DAAuD;AAWhD,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;CAAG,CAAA;AAAtB,kDAAmB;8BAAnB,mBAAmB;IAT/B,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE;YACL,4BAAY;YACZ,mBAAU,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC7C,mBAAU,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;SACrD;QACD,SAAS,EAAE,CAAC,6CAAoB,EAAE,2CAAmB,CAAC;QACtD,OAAO,EAAE,CAAC,6CAAoB,EAAE,2CAAmB,CAAC;KACvD,CAAC;GACW,mBAAmB,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/content-studio/content-studio.router.js b/apps/api/dist/content-studio/content-studio.router.js new file mode 100644 index 0000000000000000000000000000000000000000..885040b6da12d0eae37e1bda89a0e56ca3366ea6 --- /dev/null +++ b/apps/api/dist/content-studio/content-studio.router.js @@ -0,0 +1,104 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ContentStudioRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const trpc_init_1 = require("../trpc/trpc.init"); +const content_studio_service_1 = require("./content-studio.service"); +let ContentStudioRouter = class ContentStudioRouter { + contentStudioService; + constructor(contentStudioService) { + this.contentStudioService = contentStudioService; + } + get router() { + return (0, trpc_init_1.router)({ + createTranscript: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + episodeId: zod_1.z.string().uuid(), + fullText: zod_1.z.string().min(1), + segments: zod_1.z.array(zod_1.z.object({ + word: zod_1.z.string(), + start: zod_1.z.number(), + end: zod_1.z.number(), + speaker: zod_1.z.string().optional(), + })), + speakerDiarization: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()), + language: zod_1.z.string().default("en"), + confidenceScore: zod_1.z.number().min(0).max(1).optional(), + processingDuration: zod_1.z.number().int().optional(), + })) + .mutation(async ({ input, ctx }) => { + return this.contentStudioService.createTranscript(input.episodeId, ctx.userId, input); + }), + getTranscript: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ episodeId: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.contentStudioService.getTranscript(input.episodeId, ctx.userId); + }), + detectClips: trpc_init_1.studioProcedure + .input(zod_1.z.object({ episodeId: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => { + const moments = await this.contentStudioService.detectViralMoments(input.episodeId, ctx.userId); + return this.contentStudioService.saveDetectedClips(input.episodeId, ctx.userId, moments); + }), + listClips: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ episodeId: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.contentStudioService.getClips(input.episodeId, ctx.userId); + }), + approveClip: trpc_init_1.studioProcedure + .input(zod_1.z.object({ clipId: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => { + return this.contentStudioService.approveClip(input.clipId, ctx.userId); + }), + rejectClip: trpc_init_1.studioProcedure + .input(zod_1.z.object({ clipId: zod_1.z.string().uuid() })) + .mutation(async ({ input, ctx }) => { + return this.contentStudioService.rejectClip(input.clipId, ctx.userId); + }), + exportClip: trpc_init_1.studioProcedure + .input(zod_1.z.object({ + clipId: zod_1.z.string().uuid(), + platform: zod_1.z.enum(["tiktok", "instagram", "youtube", "linkedin", "twitter"]), + renderingParams: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).default({}), + })) + .mutation(async ({ input, ctx }) => { + return this.contentStudioService.createExportJob(input.clipId, ctx.userId, input.platform, input.renderingParams); + }), + bulkExport: trpc_init_1.studioProcedure + .input(zod_1.z.object({ + clipId: zod_1.z.string().uuid(), + platforms: zod_1.z.array(zod_1.z.enum(["tiktok", "instagram", "youtube", "linkedin", "twitter"])), + renderingParams: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).default({}), + })) + .mutation(async ({ input, ctx }) => { + return this.contentStudioService.bulkExport(input.clipId, ctx.userId, input.platforms, input.renderingParams); + }), + getExportJobs: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ clipId: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.contentStudioService.getExportJobs(input.clipId, ctx.userId); + }), + stats: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ podcastId: zod_1.z.string().uuid() })) + .query(async ({ input, ctx }) => { + return this.contentStudioService.getContentStudioStats(input.podcastId, ctx.userId); + }), + }); + } +}; +exports.ContentStudioRouter = ContentStudioRouter; +exports.ContentStudioRouter = ContentStudioRouter = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [content_studio_service_1.ContentStudioService]) +], ContentStudioRouter); +//# sourceMappingURL=content-studio.router.js.map \ No newline at end of file diff --git a/apps/api/dist/content-studio/content-studio.router.js.map b/apps/api/dist/content-studio/content-studio.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..0e68762a57e8cbbf8a316215e6196664e6ec4be8 --- /dev/null +++ b/apps/api/dist/content-studio/content-studio.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"content-studio.router.js","sourceRoot":"","sources":["../../src/content-studio/content-studio.router.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/content-studio/content-studio.service.js b/apps/api/dist/content-studio/content-studio.service.js new file mode 100644 index 0000000000000000000000000000000000000000..c60e120bc2167227cca0f15bcffb5daebe7db4f3 --- /dev/null +++ b/apps/api/dist/content-studio/content-studio.service.js @@ -0,0 +1,867 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var ContentStudioService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ContentStudioService = void 0; +const common_1 = require("@nestjs/common"); +const client_1 = require("@prisma/client"); +const bullmq_1 = require("@nestjs/bullmq"); +const bullmq_2 = require("bullmq"); +const shared_types_1 = require("@micprotocol/shared-types"); +const prisma_service_1 = require("../prisma/prisma.service"); +const authorization_service_1 = require("../common/authorization.service"); +const redis_lock_service_1 = require("../common/redis-lock.service"); +const subscription_features_1 = require("../billing/subscription-features"); +let ContentStudioService = ContentStudioService_1 = class ContentStudioService { + prisma; + authorizationService; + redisLockService; + contentQueue; + clipScoringQueue; + logger = new common_1.Logger(ContentStudioService_1.name); + constructor(prisma, authorizationService, redisLockService, contentQueue, clipScoringQueue) { + this.prisma = prisma; + this.authorizationService = authorizationService; + this.redisLockService = redisLockService; + this.contentQueue = contentQueue; + this.clipScoringQueue = clipScoringQueue; + } + async resolveEpisodePlan(episodeId) { + const episode = await this.prisma.episode.findUnique({ + where: { id: episodeId }, + select: { + podcast: { + select: { + workspaceId: true, + workspace: { + select: { + subscriptionPlanId: true, + subscriptionStatus: true, + }, + }, + }, + }, + }, + }); + if (!episode) { + throw new common_1.NotFoundException(`Episode ${episodeId} not found`); + } + const planId = episode.podcast.workspace.subscriptionPlanId || shared_types_1.SubscriptionPlanId.HOBBY; + const status = episode.podcast.workspace.subscriptionStatus; + const isActive = status === shared_types_1.SubscriptionStatus.ACTIVE || status === shared_types_1.SubscriptionStatus.TRIALING; + return { + planId: isActive ? planId : shared_types_1.SubscriptionPlanId.HOBBY, + workspaceId: episode.podcast.workspaceId, + }; + } + async checkClipLimit(episodeId) { + const { planId, workspaceId } = await this.resolveEpisodePlan(episodeId); + if ((0, subscription_features_1.isUnlimited)(planId, "maxClipsPerMonth")) + return; + const maxClips = (0, subscription_features_1.getPlanLimit)(planId, "maxClipsPerMonth") ?? 0; + if (maxClips === 0) { + throw new common_1.ForbiddenException(`ViralClip Engine is not available on the ${planId} plan. Upgrade to Studio to access AI-powered clip detection.`); + } + const startOfMonth = new Date(); + startOfMonth.setDate(1); + startOfMonth.setHours(0, 0, 0, 0); + const currentCount = await this.prisma.clip.count({ + where: { + episode: { podcast: { workspaceId } }, + createdAt: { gte: startOfMonth }, + }, + }); + if (currentCount >= maxClips) { + throw new common_1.ForbiddenException(`Clip limit reached for ${planId} plan (${maxClips} clips/month). ` + + `You have created ${currentCount} clips this month. Upgrade your plan to add more.`); + } + } + async checkClipLimitForBatch(episodeId, incomingCount) { + if (incomingCount <= 0) + return; + const { planId, workspaceId } = await this.resolveEpisodePlan(episodeId); + if ((0, subscription_features_1.isUnlimited)(planId, "maxClipsPerMonth")) + return; + const maxClips = (0, subscription_features_1.getPlanLimit)(planId, "maxClipsPerMonth") ?? 0; + if (maxClips === 0) { + throw new common_1.ForbiddenException(`ViralClip Engine is not available on the ${planId} plan. Upgrade to Studio to access AI-powered clip detection.`); + } + const startOfMonth = new Date(); + startOfMonth.setDate(1); + startOfMonth.setHours(0, 0, 0, 0); + const currentCount = await this.prisma.clip.count({ + where: { + episode: { podcast: { workspaceId } }, + createdAt: { gte: startOfMonth }, + }, + }); + if (currentCount + incomingCount > maxClips) { + throw new common_1.ForbiddenException(`Clip limit would be exceeded for ${planId} plan (${maxClips} clips/month). ` + + `You have ${currentCount} and are trying to add ${incomingCount}. ` + + `Upgrade your plan to add more.`); + } + } + async checkExportLimit(episodeId, additionalMinutes = 1) { + const { planId, workspaceId } = await this.resolveEpisodePlan(episodeId); + if ((0, subscription_features_1.isUnlimited)(planId, "maxExportMinutesPerMonth")) + return; + const maxMinutes = (0, subscription_features_1.getPlanLimit)(planId, "maxExportMinutesPerMonth") ?? 0; + if (maxMinutes === 0) { + throw new common_1.ForbiddenException(`Multi-platform export is not available on the ${planId} plan. Upgrade to Studio to export clips.`); + } + const startOfMonth = new Date(); + startOfMonth.setDate(1); + startOfMonth.setHours(0, 0, 0, 0); + const currentMinutes = await this.prisma.exportJob.aggregate({ + where: { + clip: { episode: { podcast: { workspaceId } } }, + status: "completed", + updatedAt: { gte: startOfMonth }, + }, + _count: true, + }); + if (currentMinutes._count + additionalMinutes > maxMinutes) { + throw new common_1.ForbiddenException(`Export minute limit reached for ${planId} plan (${maxMinutes} minutes/month). ` + + `You have used ${currentMinutes._count} minutes this month. Upgrade your plan for more.`); + } + } + async createTranscript(episodeId, userId, data) { + await this.authorizationService.checkEpisodeAccess(episodeId, userId); + const MAX_FULLTEXT_CHARS = 2_000_000; + const MAX_SEGMENT_COUNT = 60_000; + const MAX_SEGMENT_WORD_CHARS = 200; + if (typeof data.fullText !== "string") { + throw new common_1.BadRequestException("fullText must be a string"); + } + if (data.fullText.length > MAX_FULLTEXT_CHARS) { + throw new common_1.BadRequestException(`fullText exceeds ${MAX_FULLTEXT_CHARS} characters`); + } + if (!Array.isArray(data.segments)) { + throw new common_1.BadRequestException("segments must be an array"); + } + if (data.segments.length > MAX_SEGMENT_COUNT) { + throw new common_1.BadRequestException(`segments exceeds ${MAX_SEGMENT_COUNT} entries`); + } + for (const seg of data.segments) { + if (typeof seg.word !== "string" || seg.word.length > MAX_SEGMENT_WORD_CHARS) { + throw new common_1.BadRequestException(`segment word exceeds ${MAX_SEGMENT_WORD_CHARS} chars`); + } + if (typeof seg.start !== "number" || typeof seg.end !== "number") { + throw new common_1.BadRequestException("segment start/end must be numbers"); + } + if (seg.start < 0 || seg.end < seg.start) { + throw new common_1.BadRequestException("segment timestamps invalid"); + } + } + this.logger.log(`Creating transcript for episode ${episodeId}`); + return this.prisma.transcript.create({ + data: { + episodeId, + fullText: data.fullText, + segments: data.segments, + speakerDiarization: data.speakerDiarization, + language: data.language || "en", + confidenceScore: data.confidenceScore || null, + processingDuration: data.processingDuration || null, + }, + }); + } + async getTranscript(episodeId, userId) { + await this.authorizationService.checkEpisodeAccess(episodeId, userId); + const transcript = await this.prisma.transcript.findFirst({ + where: { episodeId }, + orderBy: { createdAt: "desc" }, + }); + if (!transcript) { + throw new common_1.NotFoundException(`No transcript found for episode ${episodeId}`); + } + return transcript; + } + async detectViralMoments(episodeId, userId) { + await this.authorizationService.checkEpisodeAccess(episodeId, userId); + this.logger.log(`Detecting viral moments for episode ${episodeId}`); + const transcript = await this.getTranscript(episodeId, userId); + const segments = transcript.segments; + const emotionalPeaks = this.detectEmotionalPeaks(segments); + const contentGold = this.scoreContentSegments(segments); + const naturalBreaks = this.findNaturalBreaks(segments); + const candidates = []; + for (const peak of emotionalPeaks) { + const clipRange = this.resolveClipRangeForPeak(peak.start, peak.end, naturalBreaks, segments[segments.length - 1]?.end ?? peak.end); + if (!clipRange) + continue; + const { startBreak, endBreak } = clipRange; + const duration = endBreak - startBreak; + const sliceWords = segments + .filter((s) => s.start < endBreak && s.end > startBreak) + .map((s) => s.word) + .join(" "); + const contentScore = contentGold.find((c) => c.start < endBreak && c.end > startBreak); + candidates.push({ + startSeconds: Math.floor(startBreak), + endSeconds: Math.ceil(endBreak), + viralityScore: Math.min(100, Math.round(peak.score * 0.4 + + (contentScore?.score || 50) * 0.4 + + Math.min(60, duration) * 0.3)), + type: this.classifyClipType(peak, contentScore), + suggestedHook: this.generateHook(sliceWords), + suggestedCaption: sliceWords.substring(0, 280), + transcriptSlice: sliceWords, + aiTags: this.extractTopics(sliceWords), + }); + } + return candidates + .sort((a, b) => b.viralityScore - a.viralityScore) + .slice(0, 5); + } + async saveDetectedClips(episodeId, userId, moments) { + await this.authorizationService.checkEpisodeAccess(episodeId, userId); + const episode = await this.prisma.episode.findUnique({ + where: { id: episodeId }, + select: { podcast: { select: { workspaceId: true } } }, + }); + if (!episode) + throw new common_1.NotFoundException("Episode not found"); + const workspaceId = episode.podcast.workspaceId; + return this.redisLockService.withLock(`clip-limit:workspace:${workspaceId}`, async () => { + await this.checkClipLimit(episodeId); + return this.saveDetectedClipsUnlocked(episodeId, moments); + }, { ttlMs: 10_000, retries: 3, retryDelayMs: 100 }); + } + async saveDetectedClipsUnlocked(episodeId, moments) { + this.logger.log(`Saving ${moments.length} clips for episode ${episodeId}`); + const existingClips = await this.prisma.clip.findMany({ + where: { episodeId }, + select: { startSeconds: true, endSeconds: true }, + }); + const filtered = moments.filter((moment) => { + const overlaps = existingClips.some((existing) => moment.startSeconds < existing.endSeconds + 5 && + moment.endSeconds > existing.startSeconds - 5); + if (overlaps) { + this.logger.log(`Skipping duplicate clip: ${moment.startSeconds}s-${moment.endSeconds}s (overlaps existing)`); + } + return !overlaps; + }); + if (filtered.length === 0) { + this.logger.log("All detected moments overlap with existing clips, skipping save"); + return []; + } + await this.checkClipLimitForBatch(episodeId, filtered.length); + const clips = await Promise.all(filtered.map((moment) => this.prisma.clip.create({ + data: { + episodeId, + startSeconds: moment.startSeconds, + endSeconds: moment.endSeconds, + type: moment.type, + viralityScore: moment.viralityScore, + suggestedCaption: moment.suggestedCaption, + suggestedHook: moment.suggestedHook, + aiTags: moment.aiTags, + transcriptSlice: moment.transcriptSlice, + isApproved: false, + }, + }))); + try { + const sourceUrl = await this.resolveEpisodeSourceUrl(episodeId); + const transcript = await this.prisma.transcript.findFirst({ + where: { episodeId }, + orderBy: { createdAt: "desc" }, + select: { fullText: true }, + }); + if (sourceUrl && transcript) { + for (const clip of clips) { + await this.clipScoringQueue.add("score", { + clipId: clip.id, + episodeId, + videoUrl: sourceUrl, + transcriptText: transcript.fullText, + }, { + jobId: `score-${clip.id}`, + attempts: 3, + backoff: { type: "exponential", delay: 5000 }, + removeOnComplete: { age: 86400 }, + removeOnFail: { age: 604800 }, + }); + } + this.logger.log(`Enqueued CLIP scoring for ${clips.length} clips`); + } + } + catch (err) { + this.logger.warn(`Failed to enqueue CLIP scoring (non-fatal): ${err instanceof Error ? err.message : String(err)}`); + } + return clips; + } + async getClips(episodeId, userId) { + await this.authorizationService.checkEpisodeAccess(episodeId, userId); + return this.prisma.clip.findMany({ + where: { episodeId }, + include: { + exportJobs: { orderBy: { createdAt: "desc" } }, + metrics: true, + }, + orderBy: { viralityScore: "desc" }, + }); + } + async approveClip(clipId, userId) { + await this.authorizationService.checkClipAccess(clipId, userId); + return this.prisma.clip.update({ + where: { id: clipId }, + data: { isApproved: true }, + }); + } + async rejectClip(clipId, userId) { + await this.authorizationService.checkClipAccess(clipId, userId); + const activeExports = await this.prisma.exportJob.count({ + where: { + clipId, + status: { in: ["queued", "rendering"] }, + }, + }); + if (activeExports > 0) { + throw new common_1.ConflictException(`Cannot delete clip with ${activeExports} active export(s) in progress. ` + + `Wait for them to complete or fail before deleting.`); + } + return this.prisma.clip.delete({ where: { id: clipId } }); + } + async createExportJob(clipId, userId, platform, renderingParams = {}) { + await this.authorizationService.checkClipAccess(clipId, userId); + const clip = await this.prisma.clip.findUnique({ + where: { id: clipId }, + select: { + episodeId: true, + isApproved: true, + startSeconds: true, + endSeconds: true, + suggestedCaption: true, + suggestedHook: true, + }, + }); + if (!clip) { + throw new common_1.NotFoundException(`Clip ${clipId} not found`); + } + if (!clip.isApproved) { + throw new common_1.ForbiddenException("Clip must be approved before exporting. Use the approveClip mutation first."); + } + if (!Object.values(shared_types_1.ExportPlatform).includes(platform)) { + throw new common_1.BadRequestException(`Invalid platform "${platform}". Valid platforms: ${Object.values(shared_types_1.ExportPlatform).join(", ")}`); + } + const platformConfig = shared_types_1.PLATFORM_CONFIGS[platform]; + if (platformConfig) { + const clipDuration = clip.endSeconds - clip.startSeconds; + if (clipDuration > platformConfig.maxDurationSeconds) { + throw new common_1.BadRequestException(`Clip duration (${clipDuration}s) exceeds ${platformConfig.label} maximum of ${platformConfig.maxDurationSeconds}s. Trim the clip or choose a different platform.`); + } + } + await this.checkExportLimit(clip.episodeId, 1); + this.logger.log(`Creating ${platform} export for clip ${clipId}`); + const exportJob = await this.prisma.exportJob.create({ + data: { + clipId, + platform: platform, + status: client_1.$Enums.ExportJobStatus.queued, + renderingParams: renderingParams, + }, + include: { clip: true }, + }); + try { + await this.contentQueue.add("export", { + exportJobId: exportJob.id, + clipId, + platform, + caption: clip?.suggestedCaption || undefined, + hook: clip?.suggestedHook || undefined, + renderingParams, + }, { + jobId: `export-${exportJob.id}-${Date.now()}`, + attempts: 3, + backoff: { + type: "exponential", + delay: 5000, + }, + removeOnComplete: { age: 86400 }, + removeOnFail: { age: 604800 }, + }); + this.logger.log(`Enqueued export job ${exportJob.id} for clip ${clipId} on ${platform}`); + } + catch (err) { + this.logger.error(`Failed to enqueue export for clip ${clipId}: ${err instanceof Error ? err.message : String(err)}`); + } + return exportJob; + } + async bulkExport(clipId, userId, platforms, renderingParams = {}) { + await this.authorizationService.checkClipAccess(clipId, userId); + const clip = await this.prisma.clip.findUnique({ + where: { id: clipId }, + select: { + episodeId: true, + isApproved: true, + startSeconds: true, + endSeconds: true, + suggestedCaption: true, + suggestedHook: true, + }, + }); + if (!clip) { + throw new common_1.NotFoundException(`Clip ${clipId} not found`); + } + if (!clip.isApproved) { + throw new common_1.ForbiddenException("Clip must be approved before exporting. Use the approveClip mutation first."); + } + if (!Array.isArray(platforms) || platforms.length === 0) { + throw new common_1.BadRequestException("At least one platform is required for bulk export"); + } + const validPlatforms = new Set(Object.values(shared_types_1.ExportPlatform)); + const invalidPlatforms = platforms.filter((p) => !validPlatforms.has(p)); + if (invalidPlatforms.length > 0) { + throw new common_1.BadRequestException(`Invalid platform(s): ${invalidPlatforms.join(", ")}. Valid platforms: ${Object.values(shared_types_1.ExportPlatform).join(", ")}`); + } + const clipDuration = clip.endSeconds - clip.startSeconds; + const incompatiblePlatforms = []; + for (const platform of platforms) { + const platformConfig = shared_types_1.PLATFORM_CONFIGS[platform]; + if (platformConfig && clipDuration > platformConfig.maxDurationSeconds) { + incompatiblePlatforms.push(`${platformConfig.label} (max ${platformConfig.maxDurationSeconds}s)`); + } + } + if (incompatiblePlatforms.length > 0) { + throw new common_1.BadRequestException(`Clip duration (${clipDuration}s) exceeds maximum for: ${incompatiblePlatforms.join(", ")}. Trim the clip or remove those platforms.`); + } + await this.checkExportLimit(clip.episodeId, platforms.length); + const existingExports = await this.prisma.exportJob.findMany({ + where: { + clipId, + platform: { in: platforms }, + status: { in: ["queued", "rendering"] }, + }, + select: { platform: true }, + }); + const existingPlatforms = new Set(existingExports.map((e) => e.platform)); + const filteredPlatforms = platforms.filter((p) => !existingPlatforms.has(p)); + if (filteredPlatforms.length === 0) { + throw new common_1.ConflictException(`All requested platforms already have queued or rendering exports for this clip.`); + } + if (filteredPlatforms.length < platforms.length) { + this.logger.warn(`Bulk export: ${platforms.length - filteredPlatforms.length} platforms skipped (already queued/rendering)`); + } + const episode = await this.prisma.episode.findUnique({ + where: { id: clip.episodeId }, + select: { podcast: { select: { workspaceId: true } } }, + }); + const workspaceId = episode?.podcast.workspaceId; + if (!workspaceId) { + throw new common_1.NotFoundException("Workspace for clip not found"); + } + return this.redisLockService.withLock(`export-limit:workspace:${workspaceId}`, async () => { + await this.checkExportLimit(clip.episodeId, filteredPlatforms.length); + return this.bulkExportUnlocked(clipId, clip, filteredPlatforms, renderingParams); + }, { ttlMs: 15_000, retries: 3, retryDelayMs: 100 }); + } + async bulkExportUnlocked(clipId, clip, filteredPlatforms, renderingParams) { + const jobs = []; + for (const platform of filteredPlatforms) { + const exportJob = await this.prisma.exportJob.create({ + data: { + clipId, + platform: platform, + status: client_1.$Enums.ExportJobStatus.queued, + renderingParams: { + ...renderingParams, + aspectRatio: this.getAspectRatio(platform), + }, + }, + }); + jobs.push(exportJob); + try { + await this.contentQueue.add("export", { + exportJobId: exportJob.id, + clipId, + platform, + caption: clip?.suggestedCaption || undefined, + hook: clip?.suggestedHook || undefined, + renderingParams: { + ...renderingParams, + aspectRatio: this.getAspectRatio(platform), + }, + }, { + jobId: `export-${exportJob.id}-${Date.now()}`, + attempts: 3, + backoff: { + type: "exponential", + delay: 5000, + }, + removeOnComplete: { age: 86400 }, + removeOnFail: { age: 604800 }, + }); + } + catch (err) { + this.logger.error(`Failed to enqueue bulk export for clip ${clipId} (${platform}): ${err instanceof Error ? err.message : String(err)}`); + } + } + this.logger.log(`Bulk export: enqueued ${jobs.length} jobs for clip ${clipId}`); + return jobs; + } + async updateExportJobStatus(jobId, status, outputUrl, errorMessage, userId) { + if (userId) { + const job = await this.prisma.exportJob.findUnique({ + where: { id: jobId }, + select: { clip: { select: { episode: { select: { podcast: { select: { workspace: { select: { members: { where: { userId }, select: { id: true } } } } } } } } } } }, + }); + if (!job || !job.clip.episode.podcast.workspace.members.length) { + throw new common_1.ForbiddenException('Access denied'); + } + } + return this.prisma.exportJob.update({ + where: { id: jobId }, + data: { + status: status, + outputUrl: outputUrl || undefined, + errorMessage: errorMessage || undefined, + ...(status === "rendering" ? { startedAt: new Date() } : {}), + ...(status === "completed" ? { completedAt: new Date() } : {}), + }, + }); + } + async getExportJobs(clipId, userId) { + await this.authorizationService.checkClipAccess(clipId, userId); + return this.prisma.exportJob.findMany({ + where: { clipId }, + orderBy: { createdAt: "desc" }, + }); + } + async upsertMetrics(clipId, userId, data) { + await this.authorizationService.checkClipAccess(clipId, userId); + const MAX_METRIC = 10_000_000_000; + const isNonNegInt = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 && Number.isInteger(n); + if (data.viewCount !== undefined && !isNonNegInt(data.viewCount)) { + throw new common_1.BadRequestException("viewCount must be a non-negative integer"); + } + if (data.clickThroughToEpisode !== undefined && !isNonNegInt(data.clickThroughToEpisode)) { + throw new common_1.BadRequestException("clickThroughToEpisode must be a non-negative integer"); + } + if (data.guestProfileVisits !== undefined && !isNonNegInt(data.guestProfileVisits)) { + throw new common_1.BadRequestException("guestProfileVisits must be a non-negative integer"); + } + if (data.viewCount !== undefined && data.viewCount > MAX_METRIC) { + throw new common_1.BadRequestException(`viewCount exceeds max ${MAX_METRIC}`); + } + if (data.clickThroughToEpisode !== undefined && data.clickThroughToEpisode > MAX_METRIC) { + throw new common_1.BadRequestException(`clickThroughToEpisode exceeds max ${MAX_METRIC}`); + } + if (data.guestProfileVisits !== undefined && data.guestProfileVisits > MAX_METRIC) { + throw new common_1.BadRequestException(`guestProfileVisits exceeds max ${MAX_METRIC}`); + } + const existing = await this.prisma.performanceMetric.findFirst({ + where: { clipId }, + }); + if (existing) { + return this.prisma.performanceMetric.update({ + where: { id: existing.id }, + data: { + viewCount: data.viewCount ?? existing.viewCount, + clickThroughToEpisode: data.clickThroughToEpisode ?? existing.clickThroughToEpisode, + guestProfileVisits: data.guestProfileVisits ?? existing.guestProfileVisits, + engagementByPlatform: (data.engagementByPlatform ?? existing.engagementByPlatform ?? {}), + lastSyncedAt: new Date(), + }, + }); + } + return this.prisma.performanceMetric.create({ + data: { + clipId, + viewCount: data.viewCount || 0, + clickThroughToEpisode: data.clickThroughToEpisode || 0, + guestProfileVisits: data.guestProfileVisits || 0, + engagementByPlatform: (data.engagementByPlatform || {}), + lastSyncedAt: new Date(), + }, + }); + } + async getContentStudioStats(podcastId, userId) { + await this.authorizationService.checkPodcastAccess(podcastId, userId); + const episodes = await this.prisma.episode.findMany({ + where: { podcastId }, + select: { id: true }, + }); + const episodeIds = episodes.map((e) => e.id); + if (episodeIds.length === 0) { + return { + totalClips: 0, + approvedClips: 0, + totalExports: 0, + completedExports: 0, + totalViews: 0, + totalClickThroughs: 0, + topPlatform: null, + }; + } + const [totalClips, approvedClips, totalExports, completedExports, metrics] = await Promise.all([ + this.prisma.clip.count({ where: { episodeId: { in: episodeIds } } }), + this.prisma.clip.count({ + where: { episodeId: { in: episodeIds }, isApproved: true }, + }), + this.prisma.exportJob.count({ + where: { clip: { episodeId: { in: episodeIds } } }, + }), + this.prisma.exportJob.count({ + where: { + clip: { episodeId: { in: episodeIds } }, + status: "completed", + }, + }), + this.prisma.performanceMetric.aggregate({ + where: { clip: { episodeId: { in: episodeIds } } }, + _sum: { + viewCount: true, + clickThroughToEpisode: true, + guestProfileVisits: true, + }, + }), + ]); + return { + totalClips, + approvedClips, + totalExports, + completedExports, + totalViews: metrics._sum.viewCount || 0, + totalClickThroughs: metrics._sum.clickThroughToEpisode || 0, + }; + } + detectEmotionalPeaks(segments) { + if (segments.length === 0) + return []; + const sentences = []; + let currentWords = []; + let currentStart = segments[0].start; + for (const seg of segments) { + currentWords.push(seg.word); + if (/[.!?]$/.test(seg.word)) { + sentences.push({ + text: currentWords.join(" "), + start: currentStart, + end: seg.end, + }); + currentWords = []; + currentStart = seg.end; + } + } + if (currentWords.length > 0) { + sentences.push({ + text: currentWords.join(" "), + start: currentStart, + end: segments[segments.length - 1].end, + }); + } + const peaks = []; + for (const sentence of sentences) { + if (sentence.text.length < 20) + continue; + let score = 50; + if (sentence.text.includes("!")) + score += 15; + if (sentence.text.includes("?")) + score += 10; + if (/\b(amazing|incredible|shocking|never|always|best|worst)\b/i.test(sentence.text)) + score += 20; + if (/\b(million|billion|percent|%)\b/i.test(sentence.text)) + score += 15; + if (/\b(secret|mistake|lesson|truth|actually)\b/i.test(sentence.text)) + score += 10; + if (/\b(um|uh|like|you know)\b/i.test(sentence.text)) + score -= 10; + if (score > 55) { + peaks.push({ + start: sentence.start, + end: sentence.end, + score: Math.min(100, score), + emotion: score > 80 ? "high_energy" : "engaging", + }); + } + } + return peaks.sort((a, b) => b.score - a.score).slice(0, 10); + } + scoreContentSegments(segments) { + if (segments.length === 0) + return []; + const results = []; + const windowSeconds = 30; + const totalDuration = segments[segments.length - 1].end; + for (let windowStart = 0; windowStart < totalDuration; windowStart += windowSeconds) { + const windowEnd = windowStart + windowSeconds; + const windowWords = segments + .filter((s) => s.start >= windowStart && s.start < windowEnd) + .map((s) => s.word) + .join(" "); + if (windowWords.trim().length < 30) + continue; + let score = 50; + if (/\d+/.test(windowWords)) + score += 15; + if (/\b(but|however|actually|contrary|myth)\b/i.test(windowWords)) + score += 10; + if (/\b(when I|one time|story|happened|remember)\b/i.test(windowWords)) + score += 15; + if (/\b(step|tip|hack|strategy|framework|how to)\b/i.test(windowWords)) + score += 10; + results.push({ + start: windowStart, + end: Math.min(windowEnd, totalDuration), + score: Math.min(100, score), + }); + } + return results; + } + findNaturalBreaks(segments) { + const breaks = [0]; + let lastEnd = 0; + for (const segment of segments) { + if (segment.start - lastEnd > 0.3) { + breaks.push(segment.start); + } + lastEnd = segment.end; + } + return breaks; + } + resolveClipRangeForPeak(peakStart, peakEnd, naturalBreaks, audioDuration) { + const MIN_CLIP_SECONDS = 15; + const MAX_CLIP_SECONDS = 90; + const startCandidates = naturalBreaks.length > 0 ? naturalBreaks : [peakStart]; + let startBreak = startCandidates[0]; + for (const brk of startCandidates) { + if (Math.abs(brk - peakStart) < Math.abs(startBreak - peakStart)) { + startBreak = brk; + } + } + let endBreak = peakEnd; + for (const brk of naturalBreaks) { + if (brk <= startBreak) + continue; + if (Math.abs(brk - peakEnd) < Math.abs(endBreak - peakEnd) && + brk - startBreak <= MAX_CLIP_SECONDS) { + endBreak = brk; + } + } + let duration = endBreak - startBreak; + if (duration < MIN_CLIP_SECONDS) { + for (const brk of naturalBreaks) { + if (brk <= endBreak) + continue; + if (brk - startBreak > MAX_CLIP_SECONDS) + continue; + if (brk - endBreak > 0) { + endBreak = brk; + break; + } + } + duration = endBreak - startBreak; + } + if (duration < MIN_CLIP_SECONDS) { + for (let i = naturalBreaks.length - 1; i >= 0; i--) { + const brk = naturalBreaks[i]; + if (brk >= startBreak) + continue; + if (endBreak - brk > MAX_CLIP_SECONDS) + continue; + if (startBreak - brk > 0) { + startBreak = brk; + break; + } + } + duration = endBreak - startBreak; + } + if (startBreak < 0) + startBreak = 0; + if (endBreak > audioDuration) + endBreak = audioDuration; + if (endBreak - startBreak > MAX_CLIP_SECONDS) { + endBreak = startBreak + MAX_CLIP_SECONDS; + } + duration = endBreak - startBreak; + if (duration < MIN_CLIP_SECONDS) { + this.logger.warn(`Could not form a 15-90s clip for peak (${peakStart}s-${peakEnd}s); ` + + `natural breaks=${JSON.stringify(naturalBreaks)}`); + return null; + } + return { startBreak, endBreak }; + } + classifyClipType(peak, content) { + if (peak.score > 80 && (content?.score || 0) > 70) + return "viral"; + if (peak.emotion === "high_energy") + return "intro"; + if ((content?.score || 0) > 60) + return "quote"; + return "cta"; + } + generateHook(text) { + const firstSentence = text.split(/[.!?]/)[0]?.trim() || ""; + if (firstSentence.length > 100) { + return firstSentence.substring(0, 97) + "..."; + } + return firstSentence || "Listen to this clip"; + } + extractTopics(text) { + const topicPatterns = [ + /\b(AI|artificial intelligence|machine learning)\b/i, + /\b(startup|founder|entrepreneur)\b/i, + /\b(marketing|growth|audience)\b/i, + /\b(leadership|management|team)\b/i, + /\b(product|engineering|technology)\b/i, + /\b(sales|revenue|business)\b/i, + /\b(podcast|content|media)\b/i, + /\b(career|hiring|remote)\b/i, + ]; + const topics = []; + for (const pattern of topicPatterns) { + const match = text.match(pattern); + if (match) { + topics.push(match[1].toLowerCase()); + } + } + return [...new Set(topics)].slice(0, 5); + } + getAspectRatio(platform) { + const config = shared_types_1.PLATFORM_CONFIGS[platform]; + return config?.aspectRatio ?? "16:9"; + } + async resolveEpisodeSourceUrl(episodeId) { + const episode = await this.prisma.episode.findUnique({ + where: { id: episodeId }, + select: { + recordingSession: { + select: { + tracks: { + where: { status: "completed" }, + orderBy: { createdAt: "desc" }, + take: 1, + select: { trackUrl: true }, + }, + }, + }, + }, + }); + return episode?.recordingSession?.tracks?.[0]?.trackUrl ?? null; + } +}; +exports.ContentStudioService = ContentStudioService; +exports.ContentStudioService = ContentStudioService = ContentStudioService_1 = __decorate([ + (0, common_1.Injectable)(), + __param(3, (0, bullmq_1.InjectQueue)("content")), + __param(4, (0, bullmq_1.InjectQueue)("clip-scoring")), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + authorization_service_1.AuthorizationService, + redis_lock_service_1.RedisLockService, + bullmq_2.Queue, + bullmq_2.Queue]) +], ContentStudioService); +//# sourceMappingURL=content-studio.service.js.map \ No newline at end of file diff --git a/apps/api/dist/content-studio/content-studio.service.js.map b/apps/api/dist/content-studio/content-studio.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..6fdaa8c72f7f2803aab37d501a9bdbe5f3a1a2d9 --- /dev/null +++ b/apps/api/dist/content-studio/content-studio.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"content-studio.service.js","sourceRoot":"","sources":["../../src/content-studio/content-studio.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/crm/crm.module.js b/apps/api/dist/crm/crm.module.js new file mode 100644 index 0000000000000000000000000000000000000000..2df256731ab574d9ce8beca0f98f91e7ead2b20e --- /dev/null +++ b/apps/api/dist/crm/crm.module.js @@ -0,0 +1,24 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CrmModule = void 0; +const common_1 = require("@nestjs/common"); +const prisma_module_1 = require("../prisma/prisma.module"); +const crm_service_1 = require("./crm.service"); +const crm_router_1 = require("./crm.router"); +let CrmModule = class CrmModule { +}; +exports.CrmModule = CrmModule; +exports.CrmModule = CrmModule = __decorate([ + (0, common_1.Module)({ + imports: [prisma_module_1.PrismaModule], + providers: [crm_service_1.CrmService, crm_router_1.CrmRouter], + exports: [crm_service_1.CrmService, crm_router_1.CrmRouter], + }) +], CrmModule); +//# sourceMappingURL=crm.module.js.map \ No newline at end of file diff --git a/apps/api/dist/crm/crm.module.js.map b/apps/api/dist/crm/crm.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..17ceee3a2c9d5af5b0be52cd0410555184195a27 --- /dev/null +++ b/apps/api/dist/crm/crm.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"crm.module.js","sourceRoot":"","sources":["../../src/crm/crm.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,+CAA2C;AAC3C,6CAAyC;AAOlC,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IALrB,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,SAAS,EAAE,CAAC,wBAAU,EAAE,sBAAS,CAAC;QAClC,OAAO,EAAE,CAAC,wBAAU,EAAE,sBAAS,CAAC;KACnC,CAAC;GACW,SAAS,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/crm/crm.router.js b/apps/api/dist/crm/crm.router.js new file mode 100644 index 0000000000000000000000000000000000000000..b51d0c0e21ed17db4d64036045a528000921c774 --- /dev/null +++ b/apps/api/dist/crm/crm.router.js @@ -0,0 +1,79 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CrmRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const trpc_init_1 = require("../trpc/trpc.init"); +const shared_types_1 = require("@micprotocol/shared-types"); +const crm_service_1 = require("./crm.service"); +const shared_types_2 = require("@micprotocol/shared-types"); +let CrmRouter = class CrmRouter { + crmService; + constructor(crmService) { + this.crmService = crmService; + } + get router() { + return (0, trpc_init_1.router)({ + submitInquiry: (0, trpc_init_1.rateLimitedProcedure)("crm:inquiry", 5, 60_000) + .input(shared_types_2.createInquirySchema) + .mutation(async ({ input }) => { + return this.crmService.submitInquiry(input); + }), + listInquiries: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SUPPORT_READ).query(async () => { + return this.crmService.listInquiries(); + }), + updateInquiry: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.SUPPORT_MANAGE) + .input(zod_1.z.object({ + id: zod_1.z.string().uuid(), + data: shared_types_2.updateInquirySchema, + })) + .mutation(async ({ input }) => { + return this.crmService.updateInquiry(input.id, input.data); + }), + getDashboardStats: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.ANALYTICS_READ).query(async () => { + return this.crmService.getDashboardStats(); + }), + listUsers: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_READ).query(async () => { + return this.crmService.listUsers(); + }), + updateUserAdminStatus: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.USERS_GRANT_ADMIN) + .input(shared_types_2.crmUpdateUserSchema) + .mutation(async ({ ctx, input }) => { + return this.crmService.updateUserAdminStatus(ctx.userId, input.userId, input.isAdmin); + }), + listWorkspaces: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_READ).query(async () => { + return this.crmService.listWorkspaces(); + }), + listAllGuests: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_READ) + .input(zod_1.z.object({ + search: zod_1.z.string().optional(), + })) + .query(async ({ input }) => { + return this.crmService.listAllGuests(input.search); + }), + updateGuestCRM: (0, trpc_init_1.adminPermissionProcedure)(shared_types_1.AdminPermission.WORKSPACES_NOTES) + .input(zod_1.z.object({ + id: zod_1.z.string().uuid(), + data: shared_types_2.crmUpdateGuestSchema, + })) + .mutation(async ({ input }) => { + return this.crmService.updateGuestCRM(input.id, input.data); + }), + }); + } +}; +exports.CrmRouter = CrmRouter; +exports.CrmRouter = CrmRouter = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [crm_service_1.CrmService]) +], CrmRouter); +//# sourceMappingURL=crm.router.js.map \ No newline at end of file diff --git a/apps/api/dist/crm/crm.router.js.map b/apps/api/dist/crm/crm.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..243c9e3c8a35dd3fe3005c77e8c71b9b9ce205c8 --- /dev/null +++ b/apps/api/dist/crm/crm.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"crm.router.js","sourceRoot":"","sources":["../../src/crm/crm.router.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/crm/crm.service.js b/apps/api/dist/crm/crm.service.js new file mode 100644 index 0000000000000000000000000000000000000000..e668a28d258c0ac2716f2d8bfd9949a5a354f2a2 --- /dev/null +++ b/apps/api/dist/crm/crm.service.js @@ -0,0 +1,237 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var CrmService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CrmService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +let CrmService = CrmService_1 = class CrmService { + prisma; + logger = new common_1.Logger(CrmService_1.name); + constructor(prisma) { + this.prisma = prisma; + } + async submitInquiry(data) { + this.logger.log(`Submitting new inquiry from: ${data.email}`); + return this.prisma.userInquiry.create({ + data: { + name: data.name, + email: data.email, + subject: data.subject, + message: data.message, + status: "pending", + }, + }); + } + async listInquiries() { + return this.prisma.userInquiry.findMany({ + orderBy: { createdAt: "desc" }, + }); + } + async updateInquiry(id, data) { + const inquiry = await this.prisma.userInquiry.findUnique({ where: { id } }); + if (!inquiry) { + throw new common_1.NotFoundException(`Inquiry with ID ${id} not found`); + } + return this.prisma.userInquiry.update({ + where: { id }, + data: { + status: data.status, + notes: data.notes, + assignedTo: data.assignedTo, + }, + }); + } + async listUsers() { + return this.prisma.user.findMany({ + orderBy: { createdAt: "desc" }, + include: { + memberships: { + include: { + workspace: { + select: { + id: true, + name: true, + subscriptionPlanId: true, + }, + }, + }, + }, + }, + }); + } + async updateUserAdminStatus(actingUserId, userId, isAdmin) { + const user = await this.prisma.user.findUnique({ where: { id: userId } }); + if (!user) { + throw new common_1.NotFoundException(`User with ID ${userId} not found`); + } + if (actingUserId === userId && user.isAdmin && !isAdmin) { + throw new common_1.BadRequestException("You cannot demote your own admin account"); + } + if (user.isAdmin && !isAdmin) { + const adminCount = await this.prisma.user.count({ where: { isAdmin: true } }); + if (adminCount <= 1) { + throw new common_1.BadRequestException("Cannot demote the last remaining administrator"); + } + } + return this.prisma.user.update({ + where: { id: userId }, + data: { isAdmin }, + }); + } + async listWorkspaces() { + return this.prisma.workspace.findMany({ + orderBy: { createdAt: "desc" }, + include: { + _count: { + select: { members: true, podcasts: true }, + }, + }, + }); + } + async listAllGuests(search) { + const whereClause = search + ? { + OR: [ + { fullName: { contains: search, mode: "insensitive" } }, + { email: { contains: search, mode: "insensitive" } }, + { company: { contains: search, mode: "insensitive" } }, + ], + } + : {}; + return this.prisma.guest.findMany({ + where: whereClause, + orderBy: { createdAt: "desc" }, + include: { + podcast: { + select: { + id: true, + title: true, + workspaceId: true, + }, + }, + }, + }); + } + async updateGuestCRM(id, data) { + const guest = await this.prisma.guest.findUnique({ where: { id } }); + if (!guest) { + throw new common_1.NotFoundException(`Guest with ID ${id} not found`); + } + return this.prisma.guest.update({ + where: { id }, + data: { + fullName: data.fullName, + company: data.company, + title: data.title, + bio: data.bio, + priorityScore: data.priorityScore, + status: data.status, + }, + }); + } + async getDashboardStats() { + const [totalUsers, activeUsers, totalWorkspaces, workspacesByPlan, inquiriesByStatus, guestsByStatus, activeSequences, paidInvoices, pendingInvoices, totalTracks, totalIntegrations,] = await Promise.all([ + this.prisma.user.count(), + this.prisma.user.count({ where: { isActive: true } }), + this.prisma.workspace.count(), + this.prisma.workspace.groupBy({ + by: ["subscriptionPlanId"], + _count: true, + }), + this.prisma.userInquiry.groupBy({ + by: ["status"], + _count: true, + }), + this.prisma.guest.groupBy({ + by: ["status"], + _count: true, + }), + this.prisma.outreachSequence.count({ where: { status: "active" } }), + this.prisma.sponsorInvoice.aggregate({ + where: { status: { in: ["paid", "disbursed"] } }, + _sum: { amountKes: true, platformCutKes: true }, + _count: true, + }), + this.prisma.sponsorInvoice.aggregate({ + where: { status: "pending" }, + _sum: { amountKes: true }, + _count: true, + }), + this.prisma.recordingTrack.count(), + this.prisma.workspaceIntegration.count({ where: { status: "connected" } }), + ]); + const plans = { hobby: 0, creator: 0, studio: 0 }; + workspacesByPlan.forEach((group) => { + const planKey = group.subscriptionPlanId.toLowerCase(); + if (planKey in plans) { + plans[planKey] = group._count; + } + }); + const inquiries = { pending: 0, in_progress: 0, resolved: 0, closed: 0 }; + inquiriesByStatus.forEach((group) => { + const statusKey = group.status.toLowerCase(); + if (statusKey in inquiries) { + inquiries[statusKey] = group._count; + } + }); + const guests = { prospect: 0, contacted: 0, scheduled: 0, recorded: 0, published: 0 }; + guestsByStatus.forEach((group) => { + const statusKey = group.status.toLowerCase(); + if (statusKey in guests) { + guests[statusKey] = group._count; + } + }); + const revenueKes = paidInvoices._sum.amountKes || 0; + const platformCutKes = paidInvoices._sum.platformCutKes || 0; + const paidInvoiceCount = paidInvoices._count || 0; + const pendingInvoiceCount = pendingInvoices._count || 0; + const pendingRevenueKes = pendingInvoices._sum.amountKes || 0; + return { + users: { + total: totalUsers, + active: activeUsers, + }, + workspaces: { + total: totalWorkspaces, + byPlan: plans, + }, + inquiries: { + total: Object.values(inquiries).reduce((a, b) => a + b, 0), + byStatus: inquiries, + }, + guests: { + total: Object.values(guests).reduce((a, b) => a + b, 0), + byStatus: guests, + }, + outreach: { + activeSequences, + }, + finance: { + totalRevenueKes: revenueKes, + platformCutKes: platformCutKes, + paidInvoiceCount, + pendingInvoiceCount, + pendingRevenueKes, + }, + infrastructure: { + totalRecordingTracks: totalTracks, + activeIntegrations: totalIntegrations, + }, + }; + } +}; +exports.CrmService = CrmService; +exports.CrmService = CrmService = CrmService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], CrmService); +//# sourceMappingURL=crm.service.js.map \ No newline at end of file diff --git a/apps/api/dist/crm/crm.service.js.map b/apps/api/dist/crm/crm.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..1a65222cd7dba33331094cfb326f292786030419 --- /dev/null +++ b/apps/api/dist/crm/crm.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"crm.service.js","sourceRoot":"","sources":["../../src/crm/crm.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAA4F;AAC5F,6DAAyD;AAIlD,IAAM,UAAU,kBAAhB,MAAM,UAAU;IAGU;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,YAAU,CAAC,IAAI,CAAC,CAAC;IAEtD,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAItD,KAAK,CAAC,aAAa,CAAC,IAAmB;QACnC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,gCAAgC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC;YAClC,IAAI,EAAE;gBACF,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,SAAS;aACpB;SACJ,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,aAAa;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC;YACpC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;SACjC,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,EAAU,EAAE,IAAmB;QAC/C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5E,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,0BAAiB,CAAC,mBAAmB,EAAE,YAAY,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC;YAClC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE;gBACF,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,UAAU;aAC9B;SACJ,CAAC,CAAC;IACP,CAAC;IAID,KAAK,CAAC,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC7B,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC9B,OAAO,EAAE;gBACL,WAAW,EAAE;oBACT,OAAO,EAAE;wBACL,SAAS,EAAE;4BACP,MAAM,EAAE;gCACJ,EAAE,EAAE,IAAI;gCACR,IAAI,EAAE,IAAI;gCACV,kBAAkB,EAAE,IAAI;6BAC3B;yBACJ;qBACJ;iBACJ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,YAAoB,EAAE,MAAc,EAAE,OAAgB;QAC9E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,MAAM,IAAI,0BAAiB,CAAC,gBAAgB,MAAM,YAAY,CAAC,CAAC;QACpE,CAAC;QAKD,IAAI,YAAY,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;YACtD,MAAM,IAAI,4BAAmB,CAAC,0CAA0C,CAAC,CAAC;QAC9E,CAAC;QAID,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;YAC3B,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YAC9E,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;gBAClB,MAAM,IAAI,4BAAmB,CACzB,gDAAgD,CACnD,CAAC;YACN,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;YAC3B,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;YACrB,IAAI,EAAE,EAAE,OAAO,EAAE;SACpB,CAAC,CAAC;IACP,CAAC;IAID,KAAK,CAAC,cAAc;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;YAClC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC9B,OAAO,EAAE;gBACL,MAAM,EAAE;oBACJ,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;iBAC5C;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;IAID,KAAK,CAAC,aAAa,CAAC,MAAe;QAC/B,MAAM,WAAW,GAAG,MAAM;YACtB,CAAC,CAAC;gBACI,EAAE,EAAE;oBACA,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,aAAsB,EAAE,EAAE;oBAChE,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,aAAsB,EAAE,EAAE;oBAC7D,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,aAAsB,EAAE,EAAE;iBAClE;aACJ;YACH,CAAC,CAAC,EAAE,CAAC;QAET,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC9B,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;YAC9B,OAAO,EAAE;gBACL,OAAO,EAAE;oBACL,MAAM,EAAE;wBACJ,EAAE,EAAE,IAAI;wBACR,KAAK,EAAE,IAAI;wBACX,WAAW,EAAE,IAAI;qBACpB;iBACJ;aACJ;SACJ,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,EAAU,EAAE,IAAoB;QACjD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;YAC5B,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE;gBACF,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,MAAM,EAAE,IAAI,CAAC,MAAM;aACtB;SACJ,CAAC,CAAC;IACP,CAAC;IAID,KAAK,CAAC,iBAAiB;QACnB,MAAM,CACF,UAAU,EACV,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,eAAe,EACf,WAAW,EACX,iBAAiB,EACpB,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAElB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE;YAExB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;YAErD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE;YAE7B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC1B,EAAE,EAAE,CAAC,oBAAoB,CAAC;gBAC1B,MAAM,EAAE,IAAI;aACf,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC;gBAC5B,EAAE,EAAE,CAAC,QAAQ,CAAC;gBACd,MAAM,EAAE,IAAI;aACf,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;gBACtB,EAAE,EAAE,CAAC,QAAQ,CAAC;gBACd,MAAM,EAAE,IAAI;aACf,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC;YAEnE,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC;gBACjC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,EAAE;gBAChD,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;gBAC/C,MAAM,EAAE,IAAI;aACf,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC;gBACjC,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;gBAC5B,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;gBACzB,MAAM,EAAE,IAAI;aACf,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE;YAElC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC;SAC7E,CAAC,CAAC;QAGH,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAClD,gBAAgB,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,kBAAkB,CAAC,WAAW,EAAwB,CAAC;YAC7E,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;gBACnB,KAAK,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;YAClC,CAAC;QACL,CAAC,CAAC,CAAC;QAGH,MAAM,SAAS,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACzE,iBAAiB,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAChC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,EAA4B,CAAC;YACvE,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;gBACzB,SAAS,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;YACxC,CAAC;QACL,CAAC,CAAC,CAAC;QAGH,MAAM,MAAM,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;QACtF,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,EAAyB,CAAC;YACpE,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;gBACtB,MAAM,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;YACrC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QACpD,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,CAAC;QAC7D,MAAM,gBAAgB,GAAG,YAAY,CAAC,MAAM,IAAI,CAAC,CAAC;QAClD,MAAM,mBAAmB,GAAG,eAAe,CAAC,MAAM,IAAI,CAAC,CAAC;QACxD,MAAM,iBAAiB,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QAE9D,OAAO;YACH,KAAK,EAAE;gBACH,KAAK,EAAE,UAAU;gBACjB,MAAM,EAAE,WAAW;aACtB;YACD,UAAU,EAAE;gBACR,KAAK,EAAE,eAAe;gBACtB,MAAM,EAAE,KAAK;aAChB;YACD,SAAS,EAAE;gBACP,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC1D,QAAQ,EAAE,SAAS;aACtB;YACD,MAAM,EAAE;gBACJ,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACvD,QAAQ,EAAE,MAAM;aACnB;YACD,QAAQ,EAAE;gBACN,eAAe;aAClB;YACD,OAAO,EAAE;gBACL,eAAe,EAAE,UAAU;gBAC3B,cAAc,EAAE,cAAc;gBAC9B,gBAAgB;gBAChB,mBAAmB;gBACnB,iBAAiB;aACpB;YACD,cAAc,EAAE;gBACZ,oBAAoB,EAAE,WAAW;gBACjC,kBAAkB,EAAE,iBAAiB;aACxC;SACJ,CAAC;IACN,CAAC;CACJ,CAAA;AApRY,gCAAU;qBAAV,UAAU;IADtB,IAAA,mBAAU,GAAE;qCAI4B,8BAAa;GAHzC,UAAU,CAoRtB"} \ No newline at end of file diff --git a/apps/api/dist/diary/diary.module.js b/apps/api/dist/diary/diary.module.js new file mode 100644 index 0000000000000000000000000000000000000000..b78bca30511de15dd7bf2ad53a884bf07c4d9411 --- /dev/null +++ b/apps/api/dist/diary/diary.module.js @@ -0,0 +1,22 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DiaryModule = void 0; +const common_1 = require("@nestjs/common"); +const diary_service_1 = require("./diary.service"); +const diary_router_1 = require("./diary.router"); +let DiaryModule = class DiaryModule { +}; +exports.DiaryModule = DiaryModule; +exports.DiaryModule = DiaryModule = __decorate([ + (0, common_1.Module)({ + providers: [diary_service_1.DiaryService, diary_router_1.DiaryRouter], + exports: [diary_service_1.DiaryService, diary_router_1.DiaryRouter], + }) +], DiaryModule); +//# sourceMappingURL=diary.module.js.map \ No newline at end of file diff --git a/apps/api/dist/diary/diary.module.js.map b/apps/api/dist/diary/diary.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..30e7638c20c757436da7c29854f5c49d9a9a41e7 --- /dev/null +++ b/apps/api/dist/diary/diary.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"diary.module.js","sourceRoot":"","sources":["../../src/diary/diary.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AAExC,mDAA+C;AAC/C,iDAA6C;AAMtC,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAI,CAAA;AAAf,kCAAW;sBAAX,WAAW;IAJvB,IAAA,eAAM,EAAC;QACJ,SAAS,EAAE,CAAC,4BAAY,EAAE,0BAAW,CAAC;QACtC,OAAO,EAAE,CAAC,4BAAY,EAAE,0BAAW,CAAC;KACvC,CAAC;GACW,WAAW,CAAI"} \ No newline at end of file diff --git a/apps/api/dist/diary/diary.router.js b/apps/api/dist/diary/diary.router.js new file mode 100644 index 0000000000000000000000000000000000000000..df9604a6102d82989c919d6869e29f7e26c2202b --- /dev/null +++ b/apps/api/dist/diary/diary.router.js @@ -0,0 +1,62 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DiaryRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const trpc_init_1 = require("../trpc/trpc.init"); +const diary_service_1 = require("./diary.service"); +const getStateSchema = zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), +}); +const saveStateSchema = zod_1.z.object({ + workspaceId: zod_1.z.string().uuid(), + state: zod_1.z.object({ + title: zod_1.z.string().max(160).optional(), + activeEntryId: zod_1.z.string().nullable(), + entries: zod_1.z.array(zod_1.z.object({ + id: zod_1.z.string(), + title: zod_1.z.string(), + body: zod_1.z.string(), + createdAt: zod_1.z.string(), + updatedAt: zod_1.z.string(), + })), + nodes: zod_1.z.array(zod_1.z.any()), + edges: zod_1.z.array(zod_1.z.any()), + notepad: zod_1.z.string(), + }), +}); +let DiaryRouter = class DiaryRouter { + diaryService; + constructor(diaryService) { + this.diaryService = diaryService; + } + get router() { + return (0, trpc_init_1.router)({ + getState: trpc_init_1.workspaceProcedure + .input(getStateSchema) + .query(async ({ ctx, input }) => { + return this.diaryService.getState(ctx.userId, input.workspaceId); + }), + saveState: trpc_init_1.workspaceProcedure + .input(saveStateSchema) + .mutation(async ({ ctx, input }) => { + return this.diaryService.saveState(ctx.userId, input.workspaceId, input.state); + }), + }); + } +}; +exports.DiaryRouter = DiaryRouter; +exports.DiaryRouter = DiaryRouter = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [diary_service_1.DiaryService]) +], DiaryRouter); +//# sourceMappingURL=diary.router.js.map \ No newline at end of file diff --git a/apps/api/dist/diary/diary.router.js.map b/apps/api/dist/diary/diary.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..9262d42eb8ea071040ac47cfc6ea17b51275dba4 --- /dev/null +++ b/apps/api/dist/diary/diary.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"diary.router.js","sourceRoot":"","sources":["../../src/diary/diary.router.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6BAAwB;AAExB,iDAA+D;AAC/D,mDAA+C;AAE/C,MAAM,cAAc,GAAG,OAAC,CAAC,MAAM,CAAC;IAC5B,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;CACjC,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IAC7B,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;IAC9B,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC;QACZ,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;QACrC,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACpC,OAAO,EAAE,OAAC,CAAC,KAAK,CACZ,OAAC,CAAC,MAAM,CAAC;YACL,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE;YACd,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE;YACjB,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE;YAChB,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE;YACrB,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE;SACxB,CAAC,CACL;QACD,KAAK,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,GAAG,EAAE,CAAC;QACvB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE;KACtB,CAAC;CACL,CAAC,CAAC;AAGI,IAAM,WAAW,GAAjB,MAAM,WAAW;IACS;IAA7B,YAA6B,YAA0B;QAA1B,iBAAY,GAAZ,YAAY,CAAc;IAAI,CAAC;IAE5D,IAAI,MAAM;QACN,OAAO,IAAA,kBAAM,EAAC;YAClB,QAAQ,EAAE,8BAAkB;iBACzB,KAAK,CAAC,cAAc,CAAC;iBACrB,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC9B,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAO,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;YACpE,CAAC,CAAC;YAEJ,SAAS,EAAE,8BAAkB;iBAC1B,KAAK,CAAC,eAAe,CAAC;iBACtB,QAAQ,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;gBACjC,OAAO,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,MAAO,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAClF,CAAC,CAAC;SACC,CAAC,CAAC;IACP,CAAC;CACJ,CAAA;AAlBY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;qCAEkC,4BAAY;GAD9C,WAAW,CAkBvB"} \ No newline at end of file diff --git a/apps/api/dist/diary/diary.service.js b/apps/api/dist/diary/diary.service.js new file mode 100644 index 0000000000000000000000000000000000000000..81fe32f46cde579f7f5c9e55b9bbec6b003958f3 --- /dev/null +++ b/apps/api/dist/diary/diary.service.js @@ -0,0 +1,151 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var DiaryService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DiaryService = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const prisma_service_1 = require("../prisma/prisma.service"); +const diaryEntrySchema = zod_1.z.object({ + id: zod_1.z.string(), + title: zod_1.z.string(), + body: zod_1.z.string(), + createdAt: zod_1.z.string(), + updatedAt: zod_1.z.string(), +}); +const workflowNodeSchema = zod_1.z.object({ + id: zod_1.z.string(), + position: zod_1.z.object({ + x: zod_1.z.number(), + y: zod_1.z.number(), + }), + data: zod_1.z.object({ + label: zod_1.z.string(), + }), + style: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(), + type: zod_1.z.string().optional(), +}); +const workflowEdgeSchema = zod_1.z.object({ + id: zod_1.z.string(), + source: zod_1.z.string(), + target: zod_1.z.string(), + type: zod_1.z.string().optional(), + animated: zod_1.z.boolean().optional(), + markerEnd: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(), +}); +const diaryStateSchema = zod_1.z.object({ + title: zod_1.z.string().max(160).optional(), + activeEntryId: zod_1.z.string().nullable(), + entries: zod_1.z.array(diaryEntrySchema), + nodes: zod_1.z.array(workflowNodeSchema), + edges: zod_1.z.array(workflowEdgeSchema), + notepad: zod_1.z.string(), +}); +let DiaryService = DiaryService_1 = class DiaryService { + prisma; + logger = new common_1.Logger(DiaryService_1.name); + constructor(prisma) { + this.prisma = prisma; + } + async ensureMembership(userId, workspaceId) { + const membership = await this.prisma.workspaceMember.findUnique({ + where: { + userId_workspaceId: { + userId, + workspaceId, + }, + }, + }); + if (!membership) { + throw new common_1.ForbiddenException("You do not have access to this workspace"); + } + } + async getState(userId, workspaceId) { + await this.ensureMembership(userId, workspaceId); + const state = await this.prisma.diary.upsert({ + where: { + userId_workspaceId: { + userId, + workspaceId, + }, + }, + create: { + userId, + workspaceId, + title: "Workspace Diary", + activeEntryId: null, + entries: [], + workflowNodes: [], + workflowEdges: [], + notepad: "", + }, + update: {}, + }); + this.logger.debug(`Loaded diary state for user=${userId} workspace=${workspaceId}`); + return { + id: state.id, + title: state.title, + activeEntryId: state.activeEntryId, + entries: state.entries, + nodes: state.workflowNodes, + edges: state.workflowEdges, + notepad: state.notepad, + updatedAt: state.updatedAt, + }; + } + async saveState(userId, workspaceId, rawState) { + await this.ensureMembership(userId, workspaceId); + const state = diaryStateSchema.parse(rawState); + const updated = await this.prisma.diary.upsert({ + where: { + userId_workspaceId: { + userId, + workspaceId, + }, + }, + create: { + userId, + workspaceId, + title: state.title || "Workspace Diary", + activeEntryId: state.activeEntryId, + entries: state.entries, + workflowNodes: state.nodes, + workflowEdges: state.edges, + notepad: state.notepad, + }, + update: { + title: state.title || "Workspace Diary", + activeEntryId: state.activeEntryId, + entries: state.entries, + workflowNodes: state.nodes, + workflowEdges: state.edges, + notepad: state.notepad, + }, + }); + this.logger.debug(`Saved diary state for user=${userId} workspace=${workspaceId} entries=${state.entries.length} nodes=${state.nodes.length} edges=${state.edges.length}`); + return { + id: updated.id, + title: updated.title, + activeEntryId: updated.activeEntryId, + entries: updated.entries, + nodes: updated.workflowNodes, + edges: updated.workflowEdges, + notepad: updated.notepad, + updatedAt: updated.updatedAt, + }; + } +}; +exports.DiaryService = DiaryService; +exports.DiaryService = DiaryService = DiaryService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], DiaryService); +//# sourceMappingURL=diary.service.js.map \ No newline at end of file diff --git a/apps/api/dist/diary/diary.service.js.map b/apps/api/dist/diary/diary.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..03990d783ac12f7f6f3386f3386150cf491b205d --- /dev/null +++ b/apps/api/dist/diary/diary.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"diary.service.js","sourceRoot":"","sources":["../../src/diary/diary.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/documents/documents.module.js b/apps/api/dist/documents/documents.module.js new file mode 100644 index 0000000000000000000000000000000000000000..6168bd3abd873af1288460c7f921d2fd29347afb --- /dev/null +++ b/apps/api/dist/documents/documents.module.js @@ -0,0 +1,28 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DocumentsModule = void 0; +const common_1 = require("@nestjs/common"); +const prisma_module_1 = require("../prisma/prisma.module"); +const file_security_module_1 = require("../common/file-security/file-security.module"); +const storage_module_1 = require("../common/storage/storage.module"); +const audit_module_1 = require("../audit/audit.module"); +const websocket_module_1 = require("../websocket/websocket.module"); +const documents_service_1 = require("./documents.service"); +const documents_router_1 = require("./documents.router"); +let DocumentsModule = class DocumentsModule { +}; +exports.DocumentsModule = DocumentsModule; +exports.DocumentsModule = DocumentsModule = __decorate([ + (0, common_1.Module)({ + imports: [prisma_module_1.PrismaModule, file_security_module_1.FileSecurityModule, storage_module_1.StorageModule, audit_module_1.AuditModule, websocket_module_1.WebsocketModule], + providers: [documents_service_1.DocumentsService, documents_router_1.DocumentsRouter], + exports: [documents_service_1.DocumentsService, documents_router_1.DocumentsRouter], + }) +], DocumentsModule); +//# sourceMappingURL=documents.module.js.map \ No newline at end of file diff --git a/apps/api/dist/documents/documents.module.js.map b/apps/api/dist/documents/documents.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..6b311c06a19e562386915e957da61aa4733110d8 --- /dev/null +++ b/apps/api/dist/documents/documents.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"documents.module.js","sourceRoot":"","sources":["../../src/documents/documents.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,uFAAkF;AAClF,qEAAiE;AACjE,wDAAoD;AACpD,oEAAgE;AAEhE,2DAAuD;AACvD,yDAAqD;AAO9C,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAL3B,IAAA,eAAM,EAAC;QACJ,OAAO,EAAE,CAAC,4BAAY,EAAE,yCAAkB,EAAE,8BAAa,EAAE,0BAAW,EAAE,kCAAe,CAAC;QACxF,SAAS,EAAE,CAAC,oCAAgB,EAAE,kCAAe,CAAC;QAC9C,OAAO,EAAE,CAAC,oCAAgB,EAAE,kCAAe,CAAC;KAC/C,CAAC;GACW,eAAe,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/documents/documents.router.js b/apps/api/dist/documents/documents.router.js new file mode 100644 index 0000000000000000000000000000000000000000..776346002c8b20a6832b39475fc7ff4ffe450a50 --- /dev/null +++ b/apps/api/dist/documents/documents.router.js @@ -0,0 +1,138 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var DocumentsRouter_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DocumentsRouter = void 0; +const common_1 = require("@nestjs/common"); +const zod_1 = require("zod"); +const trpc_init_1 = require("../trpc/trpc.init"); +const subscription_features_1 = require("../billing/subscription-features"); +const shared_types_1 = require("@micprotocol/shared-types"); +const documents_service_1 = require("./documents.service"); +let DocumentsRouter = DocumentsRouter_1 = class DocumentsRouter { + documentsService; + logger = new common_1.Logger(DocumentsRouter_1.name); + constructor(documentsService) { + this.documentsService = documentsService; + } + get router() { + return (0, trpc_init_1.router)({ + upload: (0, trpc_init_1.requireFeature)(subscription_features_1.SubscriptionFeature.DOCUMENT_MANAGEMENT) + .input(shared_types_1.uploadDocumentSchema) + .mutation(async ({ ctx, input }) => { + if (!ctx.userId) + throw new Error("Unauthorized"); + if (!ctx.workspaceId) + throw new Error("Workspace context required"); + return this.documentsService.uploadDocument({ + workspaceId: input.workspaceId, + userId: ctx.userId, + title: input.title, + description: input.description, + file: { + buffer: Buffer.from(input.storageKey, "base64"), + originalname: input.storageKey, + mimetype: input.mimeType, + size: input.sizeBytes, + }, + }); + }), + list: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + status: zod_1.z.enum(["draft", "active", "revoked", "flagged", "quarantined"]).optional(), + search: zod_1.z.string().optional(), + page: zod_1.z.number().int().min(1).optional(), + limit: zod_1.z.number().int().min(1).max(100).optional(), + }).optional()) + .query(async ({ ctx, input }) => { + if (!ctx.workspaceId) + throw new Error("Workspace context required"); + return this.documentsService.listDocuments(ctx.workspaceId, { + status: input?.status, + search: input?.search, + page: input?.page, + limit: input?.limit, + }); + }), + get: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ documentId: zod_1.z.string().uuid() })) + .query(async ({ ctx, input }) => { + if (!ctx.workspaceId) + throw new Error("Workspace context required"); + return this.documentsService.getDocument(input.documentId, ctx.workspaceId); + }), + createShare: (0, trpc_init_1.requireFeature)(subscription_features_1.SubscriptionFeature.DOCUMENT_MANAGEMENT) + .input(shared_types_1.createDocumentShareSchema) + .mutation(async ({ ctx, input }) => { + if (!ctx.workspaceId) + throw new Error("Workspace context required"); + return this.documentsService.createShare({ + documentId: input.documentId, + workspaceId: input.workspaceId, + shareType: input.shareType, + accessCode: input.accessCode, + expiresAt: input.expiresAt, + maxDownloads: input.maxDownloads, + }); + }), + revokeShare: (0, trpc_init_1.requireFeature)(subscription_features_1.SubscriptionFeature.DOCUMENT_MANAGEMENT) + .input(shared_types_1.revokeDocumentShareSchema) + .mutation(async ({ ctx, input }) => { + if (!ctx.workspaceId) + throw new Error("Workspace context required"); + return this.documentsService.revokeShare(input.shareId, ctx.workspaceId); + }), + download: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + documentId: zod_1.z.string().uuid(), + shareId: zod_1.z.string().uuid().optional(), + })) + .query(async ({ ctx, input }) => { + if (!ctx.workspaceId) + throw new Error("Workspace context required"); + return this.documentsService.getDownloadUrl(input.documentId, ctx.workspaceId, input.shareId ?? null, ctx.userId ?? null, null, null); + }), + verifyIntegrity: (0, trpc_init_1.requireFeature)(subscription_features_1.SubscriptionFeature.DOCUMENT_MANAGEMENT) + .input(shared_types_1.verifyDocumentIntegritySchema) + .query(async ({ ctx, input }) => { + if (!ctx.workspaceId) + throw new Error("Workspace context required"); + return this.documentsService.verifyIntegrity(input.documentId, ctx.workspaceId); + }), + accessLogs: trpc_init_1.workspaceProcedure + .input(zod_1.z.object({ + documentId: zod_1.z.string().uuid(), + page: zod_1.z.number().int().min(1).optional(), + limit: zod_1.z.number().int().min(1).max(100).optional(), + })) + .query(async ({ ctx, input }) => { + if (!ctx.workspaceId) + throw new Error("Workspace context required"); + return this.documentsService.listAccessLogs(input.documentId, ctx.workspaceId, { page: input.page, limit: input.limit }); + }), + delete: (0, trpc_init_1.requireFeature)(subscription_features_1.SubscriptionFeature.DOCUMENT_MANAGEMENT) + .input(zod_1.z.object({ documentId: zod_1.z.string().uuid() })) + .mutation(async ({ ctx, input }) => { + if (!ctx.userId) + throw new Error("Unauthorized"); + if (!ctx.workspaceId) + throw new Error("Workspace context required"); + return this.documentsService.deleteDocument(input.documentId, ctx.workspaceId, ctx.userId); + }), + }); + } +}; +exports.DocumentsRouter = DocumentsRouter; +exports.DocumentsRouter = DocumentsRouter = DocumentsRouter_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [documents_service_1.DocumentsService]) +], DocumentsRouter); +//# sourceMappingURL=documents.router.js.map \ No newline at end of file diff --git a/apps/api/dist/documents/documents.router.js.map b/apps/api/dist/documents/documents.router.js.map new file mode 100644 index 0000000000000000000000000000000000000000..b9d3d51fc367349620a1b859811ba927cd525fce --- /dev/null +++ b/apps/api/dist/documents/documents.router.js.map @@ -0,0 +1 @@ +{"version":3,"file":"documents.router.js","sourceRoot":"","sources":["../../src/documents/documents.router.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/documents/documents.service.js b/apps/api/dist/documents/documents.service.js new file mode 100644 index 0000000000000000000000000000000000000000..71519a6ad724705c71b4996c7af549bb5356792d --- /dev/null +++ b/apps/api/dist/documents/documents.service.js @@ -0,0 +1,477 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var DocumentsService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DocumentsService = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../prisma/prisma.service"); +const file_security_service_1 = require("../common/file-security/file-security.service"); +const r2_storage_service_1 = require("../common/storage/r2-storage.service"); +const audit_service_1 = require("../audit/audit.service"); +const events_gateway_1 = require("../websocket/events.gateway"); +const shared_types_1 = require("@micprotocol/shared-types"); +let DocumentsService = DocumentsService_1 = class DocumentsService { + prisma; + fileSecurity; + storage; + audit; + eventsGateway; + logger = new common_1.Logger(DocumentsService_1.name); + constructor(prisma, fileSecurity, storage, audit, eventsGateway) { + this.prisma = prisma; + this.fileSecurity = fileSecurity; + this.storage = storage; + this.audit = audit; + this.eventsGateway = eventsGateway; + } + async uploadDocument(input) { + const validation = this.fileSecurity.validate({ + buffer: input.file.buffer, + originalname: input.file.originalname, + mimetype: input.file.mimetype, + size: input.file.size, + }, { + maxSizeBytes: 50 * 1024 * 1024, + allowedMimeTypes: [ + "application/pdf", + "image/jpeg", + "image/png", + "image/webp", + "image/gif", + ], + }); + const existing = await this.prisma.document.findFirst({ + where: { + workspaceId: input.workspaceId, + sha256: validation.sha256, + }, + select: { id: true, title: true, createdAt: true }, + }); + const safeKey = `documents/${input.workspaceId}/${validation.sha256}/${validation.safeFilename}`; + await this.storage.uploadBuffer(safeKey, input.file.buffer, validation.detectedMimeType); + const result = await this.prisma.$transaction(async (tx) => { + const document = await tx.document.create({ + data: { + workspaceId: input.workspaceId, + ownerId: input.userId, + title: input.title, + description: input.description ?? null, + mimeType: validation.detectedMimeType, + sizeBytes: validation.size, + sha256: validation.sha256, + storageKey: safeKey, + status: shared_types_1.DocumentStatus.ACTIVE, + }, + }); + const version = await tx.documentVersion.create({ + data: { + documentId: document.id, + versionNo: 1, + mimeType: validation.detectedMimeType, + sizeBytes: validation.size, + sha256: validation.sha256, + storageKey: safeKey, + uploadedById: input.userId, + }, + }); + await tx.documentAccessLog.create({ + data: { + documentId: document.id, + workspaceId: input.workspaceId, + userId: input.userId, + action: shared_types_1.DocumentAction.UPLOAD, + }, + }); + return { document, version }; + }); + await this.audit.log({ + userId: input.userId, + workspaceId: input.workspaceId, + action: "document.upload", + entityType: "Document", + entityId: result.document.id, + metadata: { + title: input.title, + mimeType: validation.detectedMimeType, + sizeBytes: validation.size, + sha256: validation.sha256, + duplicate: !!existing, + }, + }).catch(() => { }); + this.eventsGateway.notifyDocumentUploaded(input.workspaceId, { + id: result.document.id, + title: result.document.title, + mimeType: result.document.mimeType, + sizeBytes: result.document.sizeBytes, + createdAt: result.document.createdAt, + }); + return this.mapDocument(result.document, result.version); + } + async listDocuments(workspaceId, options = {}) { + const page = Math.max(1, options.page ?? 1); + const limit = Math.min(100, Math.max(1, options.limit ?? 20)); + const skip = (page - 1) * limit; + const where = { workspaceId }; + if (options.status) + where.status = options.status; + if (options.search) { + where.OR = [ + { title: { contains: options.search, mode: "insensitive" } }, + { description: { contains: options.search, mode: "insensitive" } }, + ]; + } + const [items, total] = await Promise.all([ + this.prisma.document.findMany({ + where, + include: { + versions: { + orderBy: { versionNo: "desc" }, + take: 1, + include: { + uploadedBy: { select: { id: true, name: true } }, + }, + }, + _count: { select: { versions: true, shares: true } }, + }, + orderBy: { updatedAt: "desc" }, + skip, + take: limit, + }), + this.prisma.document.count({ where }), + ]); + return { + items: items.map((d) => this.mapDocument(d, d.versions[0] ?? undefined)), + total, + page, + limit, + }; + } + async getDocument(documentId, workspaceId) { + const document = await this.prisma.document.findFirst({ + where: { id: documentId, workspaceId }, + include: { + versions: { + orderBy: { versionNo: "desc" }, + }, + shares: { + where: { isRevoked: false }, + }, + _count: { select: { versions: true, accessLogs: true } }, + }, + }); + if (!document) + return null; + return this.mapDocumentWithShares(document); + } + async createShare(input) { + const document = await this.prisma.document.findFirst({ + where: { id: input.documentId, workspaceId: input.workspaceId }, + select: { id: true, title: true, status: true }, + }); + if (!document) { + throw new common_1.NotFoundException(`Document ${input.documentId} not found`); + } + if (document.status === shared_types_1.DocumentStatus.REVOKED || document.status === shared_types_1.DocumentStatus.QUARANTINED) { + throw new common_1.ForbiddenException("Cannot share a revoked or quarantined document"); + } + const share = await this.prisma.documentShare.create({ + data: { + documentId: input.documentId, + workspaceId: input.workspaceId, + shareType: input.shareType ?? shared_types_1.ShareType.PUBLIC, + accessCode: input.accessCode ?? null, + expiresAt: input.expiresAt ? new Date(input.expiresAt) : null, + maxDownloads: input.maxDownloads ?? null, + }, + }); + await this.audit.log({ + workspaceId: input.workspaceId, + action: "document.share.create", + entityType: "DocumentShare", + entityId: share.id, + metadata: { + documentId: input.documentId, + shareType: input.shareType, + expiresAt: input.expiresAt, + maxDownloads: input.maxDownloads, + }, + }).catch(() => { }); + this.eventsGateway.notifyDocumentShared(input.workspaceId, { + shareId: share.id, + documentId: input.documentId, + documentTitle: document.title, + shareType: share.shareType, + }); + return this.mapShare(share); + } + async revokeShare(shareId, workspaceId) { + const share = await this.prisma.documentShare.findFirst({ + where: { id: shareId, workspaceId }, + include: { document: { select: { id: true, title: true } } }, + }); + if (!share) { + throw new common_1.NotFoundException(`Share ${shareId} not found`); + } + await this.prisma.documentShare.update({ + where: { id: shareId }, + data: { isRevoked: true }, + }); + await this.audit.log({ + workspaceId, + action: "document.share.revoke", + entityType: "DocumentShare", + entityId: shareId, + metadata: { + documentId: share.document.id, + documentTitle: share.document.title, + }, + }).catch(() => { }); + this.eventsGateway.notifyDocumentRevoked(workspaceId, shareId); + } + async getDownloadUrl(documentId, workspaceId, shareId, userId, ipAddress, userAgent) { + const document = await this.prisma.document.findFirst({ + where: { id: documentId, workspaceId }, + }); + if (!document) { + throw new common_1.NotFoundException(`Document ${documentId} not found`); + } + if (document.status !== shared_types_1.DocumentStatus.ACTIVE) { + throw new common_1.ForbiddenException(`Document is ${document.status}`); + } + if (shareId) { + const share = await this.prisma.documentShare.findFirst({ + where: { id: shareId, documentId, isRevoked: false }, + }); + if (!share) { + throw new common_1.NotFoundException("Share link not found or revoked"); + } + if (share.expiresAt && share.expiresAt < new Date()) { + throw new common_1.ForbiddenException("Share link has expired"); + } + if (share.maxDownloads !== null && share.downloadCount >= share.maxDownloads) { + throw new common_1.ForbiddenException("Share link download limit reached"); + } + } + const url = await this.storage.getPresignedUrl(document.storageKey, 900); + await this.prisma.documentAccessLog.create({ + data: { + documentId, + shareId, + userId, + workspaceId, + action: shared_types_1.DocumentAction.DOWNLOAD, + ipAddress, + userAgent, + }, + }); + if (shareId) { + await this.prisma.documentShare.update({ + where: { id: shareId }, + data: { downloadCount: { increment: 1 } }, + }); + } + return { + url, + document: this.mapDocument(document), + }; + } + async verifyIntegrity(documentId, workspaceId) { + const document = await this.prisma.document.findFirst({ + where: { id: documentId, workspaceId }, + }); + if (!document) { + throw new common_1.NotFoundException(`Document ${documentId} not found`); + } + let computedSha256; + try { + const buffer = await this.storage.getObjectBuffer(document.storageKey); + const crypto = require("crypto"); + computedSha256 = crypto.createHash("sha256").update(buffer).digest("hex"); + } + catch (err) { + throw new common_1.BadRequestException(`Unable to retrieve document from storage: ${err instanceof Error ? err.message : String(err)}`); + } + const integrityValid = computedSha256 === document.sha256; + const tampered = !integrityValid; + if (tampered) { + await this.prisma.document.update({ + where: { id: documentId }, + data: { isForgeryFlagged: true, status: shared_types_1.DocumentStatus.FLAGGED }, + }); + await this.audit.log({ + workspaceId, + action: "document.tampering_detected", + entityType: "Document", + entityId: documentId, + metadata: { + storedSha256: document.sha256, + computedSha256, + }, + }).catch(() => { }); + this.eventsGateway.notifyDocumentTamperingDetected(workspaceId, documentId); + } + return { + documentId, + storedSha256: document.sha256, + computedSha256, + integrityValid, + tampered, + }; + } + async listAccessLogs(documentId, workspaceId, options = {}) { + const page = Math.max(1, options.page ?? 1); + const limit = Math.min(100, Math.max(1, options.limit ?? 50)); + const skip = (page - 1) * limit; + const document = await this.prisma.document.findFirst({ + where: { id: documentId, workspaceId }, + select: { id: true }, + }); + if (!document) { + throw new common_1.NotFoundException(`Document ${documentId} not found`); + } + const [items, total] = await Promise.all([ + this.prisma.documentAccessLog.findMany({ + where: { documentId }, + include: { + user: { select: { id: true, name: true, email: true } }, + }, + orderBy: { createdAt: "desc" }, + skip, + take: limit, + }), + this.prisma.documentAccessLog.count({ where: { documentId } }), + ]); + return { + items: items.map((l) => ({ + id: l.id, + documentId: l.documentId, + shareId: l.shareId, + userId: l.userId, + workspaceId: l.workspaceId, + action: l.action, + ipAddress: l.ipAddress, + userAgent: l.userAgent, + createdAt: l.createdAt, + userName: l.user ? (l.user.name ?? l.user.email) : null, + })), + total, + }; + } + async deleteDocument(documentId, workspaceId, userId) { + const document = await this.prisma.document.findFirst({ + where: { id: documentId, workspaceId }, + include: { shares: true }, + }); + if (!document) { + throw new common_1.NotFoundException(`Document ${documentId} not found`); + } + await this.prisma.documentShare.updateMany({ + where: { documentId }, + data: { isRevoked: true }, + }); + try { + await this.storage.deleteObject(document.storageKey); + } + catch { + } + await this.prisma.document.delete({ + where: { id: documentId }, + }); + await this.audit.log({ + userId, + workspaceId, + action: "document.delete", + entityType: "Document", + entityId: documentId, + metadata: { title: document.title }, + }).catch(() => { }); + } + mapDocument(document, latestVersion) { + return { + id: document.id, + workspaceId: document.workspaceId, + ownerId: document.ownerId, + title: document.title, + description: document.description, + mimeType: document.mimeType, + sizeBytes: document.sizeBytes, + sha256: document.sha256, + storageKey: document.storageKey, + status: document.status, + isForgeryFlagged: document.isForgeryFlagged, + isSanctionBlocked: document.isSanctionBlocked, + createdAt: document.createdAt, + updatedAt: document.updatedAt, + versionCount: document._count?.versions ?? (latestVersion ? 1 : 0), + latestVersion: latestVersion ? this.mapVersion(latestVersion) : undefined, + }; + } + mapDocumentWithShares(document) { + return { + id: document.id, + workspaceId: document.workspaceId, + ownerId: document.ownerId, + title: document.title, + description: document.description, + mimeType: document.mimeType, + sizeBytes: document.sizeBytes, + sha256: document.sha256, + storageKey: document.storageKey, + status: document.status, + isForgeryFlagged: document.isForgeryFlagged, + isSanctionBlocked: document.isSanctionBlocked, + createdAt: document.createdAt, + updatedAt: document.updatedAt, + versionCount: document.versions.length, + latestVersion: document.versions[0] ? this.mapVersion(document.versions[0]) : undefined, + }; + } + mapVersion(version) { + return { + id: version.id, + documentId: version.documentId, + versionNo: version.versionNo, + mimeType: version.mimeType, + sizeBytes: version.sizeBytes, + sha256: version.sha256, + storageKey: version.storageKey, + uploadedAt: version.uploadedAt, + uploadedById: version.uploadedById, + uploadedByName: version.uploadedBy?.name ?? null, + }; + } + mapShare(share) { + return { + id: share.id, + documentId: share.documentId, + workspaceId: share.workspaceId, + shareType: share.shareType, + accessCode: share.accessCode, + expiresAt: share.expiresAt, + maxDownloads: share.maxDownloads, + downloadCount: share.downloadCount, + isRevoked: share.isRevoked, + createdAt: share.createdAt, + updatedAt: share.updatedAt, + shareUrl: `/documents/shared/${share.id}`, + }; + } +}; +exports.DocumentsService = DocumentsService; +exports.DocumentsService = DocumentsService = DocumentsService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService, + file_security_service_1.FileSecurityService, + r2_storage_service_1.R2StorageService, + audit_service_1.AuditService, + events_gateway_1.EventsGateway]) +], DocumentsService); +//# sourceMappingURL=documents.service.js.map \ No newline at end of file diff --git a/apps/api/dist/documents/documents.service.js.map b/apps/api/dist/documents/documents.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..30799f2d5c1993c2c0be8f78797f38521c3429a8 --- /dev/null +++ b/apps/api/dist/documents/documents.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"documents.service.js","sourceRoot":"","sources":["../../src/documents/documents.service.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/apps/api/dist/email/email-templates.service.js b/apps/api/dist/email/email-templates.service.js new file mode 100644 index 0000000000000000000000000000000000000000..f2fb0ae253cef7a350e6904b982915b0f2731a08 --- /dev/null +++ b/apps/api/dist/email/email-templates.service.js @@ -0,0 +1,103 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var EmailTemplatesService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EmailTemplatesService = void 0; +const common_1 = require("@nestjs/common"); +const fs_1 = require("fs"); +const path_1 = require("path"); +const Handlebars = __importStar(require("handlebars")); +let EmailTemplatesService = EmailTemplatesService_1 = class EmailTemplatesService { + logger = new common_1.Logger(EmailTemplatesService_1.name); + compiled = new Map(); + TEMPLATES_DIR = (0, path_1.join)(__dirname, "templates"); + constructor() { + this.loadTemplates(); + } + loadTemplates() { + const names = [ + "booking-confirmation", + "recording-reminder", + "asset-reminder", + "outreach", + ]; + for (const name of names) { + const filePath = (0, path_1.join)(this.TEMPLATES_DIR, `${name}.hbs`); + if (!(0, fs_1.existsSync)(filePath)) { + this.logger.warn(`Template file not found: ${filePath}`); + continue; + } + try { + const source = (0, fs_1.readFileSync)(filePath, "utf-8"); + this.compiled.set(name, Handlebars.compile(source)); + this.logger.log(`Loaded template: ${name}`); + } + catch (err) { + this.logger.error(`Failed to load template ${name}: ${err}`); + } + } + } + render(name, data) { + const template = this.compiled.get(name); + if (!template) { + this.logger.warn(`Template "${name}" not loaded, using fallback`); + return { html: this.fallbackHtml(name, data), text: this.fallbackText(name, data) }; + } + const html = template(data); + const text = html.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim(); + return { html, text }; + } + fallbackHtml(name, data) { + return `
${data.guestName || ""}
`; + } + fallbackText(name, data) { + return `${name}: ${data.guestName || ""}`; + } +}; +exports.EmailTemplatesService = EmailTemplatesService; +exports.EmailTemplatesService = EmailTemplatesService = EmailTemplatesService_1 = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", []) +], EmailTemplatesService); +//# sourceMappingURL=email-templates.service.js.map \ No newline at end of file diff --git a/apps/api/dist/email/email-templates.service.js.map b/apps/api/dist/email/email-templates.service.js.map new file mode 100644 index 0000000000000000000000000000000000000000..5d50f195067a64de58cbcbe55705848aeb294036 --- /dev/null +++ b/apps/api/dist/email/email-templates.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"email-templates.service.js","sourceRoot":"","sources":["../../src/email/email-templates.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,2BAA8C;AAC9C,+BAA4B;AAC5B,uDAAyC;AA0ClC,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IACf,MAAM,GAAG,IAAI,eAAM,CAAC,uBAAqB,CAAC,IAAI,CAAC,CAAC;IAChD,QAAQ,GAAG,IAAI,GAAG,EAA4C,CAAC;IAE/D,aAAa,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAE9D;QACE,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAEO,aAAa;QACnB,MAAM,KAAK,GAAmB;YAC5B,sBAAsB;YACtB,oBAAoB;YACpB,gBAAgB;YAChB,UAAU;SACX,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,IAAI,CAAC,aAAa,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC;YACzD,IAAI,CAAC,IAAA,eAAU,EAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC;gBACzD,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAA,iBAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAC/C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBACpD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAC;YAC9C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,CAAyB,IAAkB,EAAE,IAAO;QACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,IAAI,8BAA8B,CAAC,CAAC;YAClE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;QACtF,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACtE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,CAAC;IAEO,YAAY,CAAC,IAAY,EAAE,IAAS;QAC1C,OAAO,OAAO,IAAI,WAAW,IAAI,CAAC,SAAS,IAAI,EAAE,MAAM,CAAC;IAC1D,CAAC;IAEO,YAAY,CAAC,IAAY,EAAE,IAAS;QAC1C,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;IAC5C,CAAC;CACF,CAAA;AApDY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;;GACA,qBAAqB,CAoDjC"} \ No newline at end of file diff --git a/apps/api/dist/email/email.module.js b/apps/api/dist/email/email.module.js new file mode 100644 index 0000000000000000000000000000000000000000..83760e609ecf2bc47b2eed753b937fd314e65d15 --- /dev/null +++ b/apps/api/dist/email/email.module.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EmailModule = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const bullmq_1 = require("@nestjs/bullmq"); +const email_service_1 = require("./email.service"); +const email_templates_service_1 = require("./email-templates.service"); +const listmonk_service_1 = require("./listmonk.service"); +let EmailModule = class EmailModule { +}; +exports.EmailModule = EmailModule; +exports.EmailModule = EmailModule = __decorate([ + (0, common_1.Module)({ + imports: [ + config_1.ConfigModule, + bullmq_1.BullModule.registerQueue({ name: "outreach" }), + ], + providers: [email_service_1.EmailService, email_templates_service_1.EmailTemplatesService, listmonk_service_1.ListmonkService], + exports: [email_service_1.EmailService, email_templates_service_1.EmailTemplatesService, listmonk_service_1.ListmonkService], + }) +], EmailModule); +//# sourceMappingURL=email.module.js.map \ No newline at end of file diff --git a/apps/api/dist/email/email.module.js.map b/apps/api/dist/email/email.module.js.map new file mode 100644 index 0000000000000000000000000000000000000000..7c72ea4130de9b486121ee1190da036ac6f4bfc6 --- /dev/null +++ b/apps/api/dist/email/email.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"email.module.js","sourceRoot":"","sources":["../../src/email/email.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA8C;AAC9C,2CAA4C;AAC5C,mDAA+C;AAC/C,uEAAkE;AAClE,yDAAqD;AAU9C,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IARvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY;YACZ,mBAAU,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;SAC/C;QACD,SAAS,EAAE,CAAC,4BAAY,EAAE,+CAAqB,EAAE,kCAAe,CAAC;QACjE,OAAO,EAAE,CAAC,4BAAY,EAAE,+CAAqB,EAAE,kCAAe,CAAC;KAChE,CAAC;GACW,WAAW,CAAG"} \ No newline at end of file diff --git a/apps/api/dist/email/email.service.js b/apps/api/dist/email/email.service.js new file mode 100644 index 0000000000000000000000000000000000000000..d30eba99709e4ed0784447251e54c4905962892e --- /dev/null +++ b/apps/api/dist/email/email.service.js @@ -0,0 +1,186 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var EmailService_1; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EmailService = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const bullmq_1 = require("@nestjs/bullmq"); +const bullmq_2 = require("bullmq"); +const email_templates_service_1 = require("./email-templates.service"); +const nodemailer = __importStar(require("nodemailer")); +let EmailService = EmailService_1 = class EmailService { + configService; + templatesService; + outreachQueue; + logger = new common_1.Logger(EmailService_1.name); + resendApiKey = null; + smtpTransporter = null; + constructor(configService, templatesService, outreachQueue) { + this.configService = configService; + this.templatesService = templatesService; + this.outreachQueue = outreachQueue; + this.resendApiKey = this.configService.get("RESEND_API_KEY") || null; + const smtpHost = this.configService.get("SMTP_HOST"); + if (smtpHost) { + this.smtpTransporter = nodemailer.createTransport({ + host: smtpHost, + port: this.configService.get("SMTP_PORT", 1025), + secure: this.configService.get("SMTP_SECURE", false), + ignoreTLS: true, + }); + } + } + async sendTemplatedEmail(template, to, subject, data) { + const { html, text } = this.templatesService.render(template, data); + return this.sendEmail({ to, subject, bodyHtml: html, bodyText: text }); + } + sanitizeSubject(subject) { + return subject.replace(/[\r\n]+/g, " ").trim(); + } + async sendEmail(options) { + const subject = this.sanitizeSubject(options.subject); + this.logger.log(`Sending email to ${options.to}: ${subject}`); + if (this.resendApiKey) { + try { + const { Resend } = await Promise.resolve().then(() => __importStar(require("resend"))); + const resend = new Resend(this.resendApiKey); + const result = await resend.emails.send({ + from: 'MicProtocol