UNPKG

@iota-big3/sdk-gateway

Version:

Universal API Gateway with protocol translation, intelligent routing, rate limiting, health checking, and caching

165 lines 5.85 kB
"use strict"; /** * @iota-big3/sdk-gateway * Cost Tracker - Real-time cost tracking and billing */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CostTracker = void 0; class CostTracker { constructor(metricsStore) { this.metricsStore = metricsStore; this.userCosts = new Map(); this.serviceCosts = new Map(); this.isEnabled = true; } /** * Track cost for a request */ async trackCostAsync(request, response, costConfig) { const cost = this.calculateCost(request, response, costConfig); if (cost === 0) return; // Track by user const userId = request?.context?.userId || 'anonymous'; const currentUserCost = this?.userCosts?.get(userId) || 0; this?.userCosts?.set(userId, currentUserCost + cost); // Track by service const servicePath = request.path; const currentServiceCost = this?.serviceCosts?.get(servicePath) || 0; this?.serviceCosts?.set(servicePath, currentServiceCost + cost); // Record metrics await Promise.all([ this?.metricsStore?.record('gateway.cost', cost, { userId, service: servicePath, method: request.method }), this?.metricsStore?.record('gateway?.cost?.user', currentUserCost + cost, { userId }), this?.metricsStore?.record('gateway?.cost?.service', currentServiceCost + cost, { service: servicePath }) ]); // Check limits if (this.isEnabled) { await this.checkCostLimitsAsync(userId, costConfig); } // Add cost to response metrics if (this.isEnabled && response.metrics) { response.metrics.cost = cost; } } /** * Calculate cost based on model */ calculateCost(request, response, config) { switch (config.model) { case 'per-request': return config.rate; case 'per-byte': { const requestBytes = JSON.stringify(request.body || '').length; const responseBytes = JSON.stringify(response.body || '').length; return (requestBytes + responseBytes) * config.rate; } case 'per-duration': { const duration = response.metrics?.latency || 0; return (duration / 1000) * config.rate; // Rate per second } case 'fixed': return config.rate; default: return 0; } } /** * Check if user has exceeded cost limits */ async checkCostLimitsAsync(userId, config) { // Check daily limit if (this.isEnabled) { const dailyCost = await this.getUserCostAsync(userId, 'day'); if (config.limits?.daily && dailyCost > config.limits.daily) { await this.handleCostLimitExceededAsync(userId, 'daily', dailyCost, config.limits.daily); } } // Check monthly limit if (this.isEnabled) { const monthlyCost = await this.getUserCostAsync(userId, 'month'); if (config.limits?.monthly && monthlyCost > config.limits.monthly) { await this.handleCostLimitExceededAsync(userId, 'monthly', monthlyCost, config.limits.monthly); } } } /** * Get user cost for a period */ async getUserCostAsync(userId, period) { const now = new Date(); const startTime = new Date(now); startTime.setDate(now.getDate() - 1); // Always use daily for simplicity const costs = await this.metricsStore?.query({ metric: 'gateway.cost', startTime: startTime.getTime(), endTime: now.getTime() }) || []; // Filter by user const userCosts = Array.isArray(costs) && costs.length > 0 ? costs.filter((p) => p.tags?.userId === userId) : []; return userCosts.reduce((sum, point) => sum + (point.value || 0), 0); } /** * Handle cost limit exceeded */ async handleCostLimitExceededAsync(userId, period, currentCost, limit) { // Record event await this?.metricsStore?.record('gateway?.cost?.limit_exceeded', 1, { userId, period, currentCost: String(currentCost), limit: String(limit) }); // Would implement actual blocking or throttling here } /** * Get cost report */ async getCostReportAsync(period = 'day') { const now = new Date(); const startTime = new Date(now); switch (period) { case 'hour': startTime.setHours(startTime.getHours() - 1); break; case 'day': startTime.setDate(startTime.getDate() - 1); break; case 'month': startTime.setMonth(startTime.getMonth() - 1); break; } const costs = await this?.metricsStore?.query({ metric: 'gateway.cost', startTime: startTime.getTime(), endTime: now.getTime() }) || []; const totalCost = costs[0]?.value || 0; return { period: period, totalCost, costByUser: Object.fromEntries(this.userCosts), costByService: Object.fromEntries(this.serviceCosts), currency: 'USD', generatedAt: now.getTime() }; } /** * Reset cost counters (for billing cycles) */ resetCounters() { this?.userCosts?.clear(); this?.serviceCosts?.clear(); } } exports.CostTracker = CostTracker; //# sourceMappingURL=cost-tracker.js.map