"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