Add composite index on usage_records for user_id, record_type, and timestamp columns; add individual indexes on record_type, timestamp, period_start, and status fields in billing models. Implement lazy Stripe API key initialization via _ensure_api_key method called before each Stripe operation. Fix monthly usage query to use date range filtering instead of year/month decomposition. Refactor auth login endpoint to replace OAuth2PasswordRequestForm dependency with custom LoginRequest model. Add error handling wrapper around current usage endpoint and introduce loading/error CSS classes for billing UI. Convert code editor view from absolute positioning to fixed overlay with centered modal container. Include Stripe.js script tag in index.html and add new APIContract class for response validation against schemas.
72 lines
1.9 KiB
JavaScript
72 lines
1.9 KiB
JavaScript
export default class PerformanceMonitor {
|
|
constructor(logger = null) {
|
|
this.logger = logger;
|
|
this.metrics = new Map();
|
|
}
|
|
|
|
measureOperation(name, fn) {
|
|
const start = performance.now();
|
|
try {
|
|
const result = fn();
|
|
const duration = performance.now() - start;
|
|
this._recordMetric(name, duration);
|
|
return result;
|
|
} catch (error) {
|
|
this.logger?.error(`Operation ${name} failed`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async measureAsync(name, fn) {
|
|
const start = performance.now();
|
|
try {
|
|
const result = await fn();
|
|
const duration = performance.now() - start;
|
|
this._recordMetric(name, duration);
|
|
return result;
|
|
} catch (error) {
|
|
this.logger?.error(`Async operation ${name} failed`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
_recordMetric(name, duration) {
|
|
if (!this.metrics.has(name)) {
|
|
this.metrics.set(name, []);
|
|
}
|
|
|
|
this.metrics.get(name).push(duration);
|
|
this.logger?.debug(`[PERF] ${name}: ${duration.toFixed(2)}ms`);
|
|
}
|
|
|
|
getMetrics(name) {
|
|
const values = this.metrics.get(name) || [];
|
|
return {
|
|
count: values.length,
|
|
min: Math.min(...values),
|
|
max: Math.max(...values),
|
|
avg: values.reduce((a, b) => a + b, 0) / values.length,
|
|
values
|
|
};
|
|
}
|
|
|
|
reportWebVitals() {
|
|
if ('web-vital' in window) {
|
|
const vitals = window['web-vital'];
|
|
this.logger?.info('Core Web Vitals', vitals);
|
|
}
|
|
}
|
|
|
|
getAllMetrics() {
|
|
const result = {};
|
|
for (const [name, values] of this.metrics.entries()) {
|
|
result[name] = this.getMetrics(name);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
reset() {
|
|
this.metrics.clear();
|
|
}
|
|
}
|