@iota-big3/sdk-gateway
Version:
Universal API Gateway with protocol translation, intelligent routing, rate limiting, health checking, and caching
165 lines • 5.85 kB
JavaScript
;
/**
* @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