"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 UsageService_1; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.UsageService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("@nestjs/typeorm"); const typeorm_2 = require("typeorm"); const usage_entity_1 = require("./usage.entity"); const redis_service_1 = require("../common/redis.service"); let UsageService = UsageService_1 = class UsageService { usageRepository; redisService; logger = new common_1.Logger(UsageService_1.name); constructor(usageRepository, redisService) { this.usageRepository = usageRepository; this.redisService = redisService; } async recordRequest(data) { this.usageRepository.save(data).catch((err) => { this.logger.error(`Failed to save usage log to DB: ${err.message}`); }); const monthKey = this.getMonthlyKey(data.tenantId); try { await this.redisService.getClient().incr(monthKey); } catch (err) { this.logger.error(`Failed to increment Redis usage counter: ${err.message}`); } } async getMonthlyUsage(tenantId) { const key = this.getMonthlyKey(tenantId); const val = await this.redisService.getClient().get(key); return val ? parseInt(val, 10) : 0; } async checkQuota(tenantId, limit) { const used = await this.getMonthlyUsage(tenantId); return { allowed: used < limit, used, }; } async checkRateLimit(tenantId, limit) { const key = this.getRateLimitKey(tenantId); try { const client = this.redisService.getClient(); const usedRaw = await client.get(key); const used = usedRaw ? parseInt(usedRaw, 10) : 0; if (used >= limit) { return { allowed: false, used }; } const multi = client.multi(); multi.incr(key); if (!usedRaw) { multi.expire(key, 60); } await multi.exec(); return { allowed: true, used: used + 1 }; } catch (err) { this.logger.error(`Rate limit check failed for ${tenantId}: ${err.message}`); return { allowed: true, used: 0 }; } } async getUsageHistory(tenantId, days = 30) { return this.usageRepository .createQueryBuilder('usage') .select("DATE_TRUNC('day', usage.createdAt)", 'date') .addSelect('COUNT(*)', 'count') .where('usage.tenantId = :tenantId', { tenantId }) .andWhere("usage.createdAt >= NOW() - (:days || ' days')::INTERVAL", { days: days.toString() }) .groupBy("DATE_TRUNC('day', usage.createdAt)") .orderBy('date', 'ASC') .getRawMany(); } async getUsageSummary(tenantId, plan = 'FREE') { const monthlyUsage = await this.getMonthlyUsage(tenantId); const QUOTA_LIMITS = { 'FREE': 8000, 'PRO': 50000, 'ENTERPRISE': 1000000 }; const monthlyLimit = QUOTA_LIMITS[plan] || 8000; const stats = await this.usageRepository .createQueryBuilder('usage') .select('COUNT(*)', 'totalToday') .addSelect('AVG(usage.responseTimeMs)', 'avgLatency') .addSelect('COUNT(CASE WHEN usage.statusCode >= 200 AND usage.statusCode < 300 THEN 1 END)', 'successCount') .where('usage.tenantId = :tenantId', { tenantId }) .andWhere('usage.createdAt >= CURRENT_DATE') .getRawOne(); const totalToday = parseInt(stats.totalToday || '0', 10); const avgLatency = Math.round(parseFloat(stats.avgLatency || '0')); const successRate = totalToday > 0 ? Math.round((parseInt(stats.successCount || '0', 10) / totalToday) * 100) : 100; return { monthlyUsage, monthlyLimit, totalToday, avgLatency, successRate, lastUpdated: new Date() }; } getMonthlyKey(tenantId) { const now = new Date(); const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; return `usage:${tenantId}:${yearMonth}`; } getRateLimitKey(tenantId) { const now = new Date(); const window = `${now.getFullYear()}${now.getMonth()}${now.getDate()}${now.getHours()}${now.getMinutes()}`; return `ratelimit:${tenantId}:${window}`; } }; exports.UsageService = UsageService; exports.UsageService = UsageService = UsageService_1 = __decorate([ (0, common_1.Injectable)(), __param(0, (0, typeorm_1.InjectRepository)(usage_entity_1.UsageLog)), __metadata("design:paramtypes", [typeof (_a = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _a : Object, redis_service_1.RedisService]) ], UsageService); //# sourceMappingURL=usage.service.js.map