Files
ServerKit/backend/app/models/api_usage.py
T
Juan Denis c5710e88b9 Add API keys, analytics, webhooks, and middleware
Introduce programmatic API features and observability: add API Key management, API usage analytics, webhook event subscriptions, and OpenAPI docs. New models (ApiKey, ApiUsageLog, ApiUsageSummary, EventSubscription, EventDelivery), services (api_analytics, api_key, event, openapi), and blueprints (/api-keys, /api-analytics, /event-subscriptions, /docs) were added and registered in create_app. Middleware for API key authentication, buffered analytics logging (with a flush thread), and enhanced rate limiting (custom key function and response headers) were implemented; RBAC was updated to accept API key auth via an auth_required decorator. Background threads for hourly aggregation and event retry were added; security CSP updated to allow resources for Swagger UI. Frontend settings UI and styles were added/updated for API keys and webhooks, and requirements were updated.
2026-03-04 01:53:46 -05:00

69 lines
2.9 KiB
Python

"""API usage tracking models."""
from datetime import datetime
from app import db
class ApiUsageLog(db.Model):
"""Raw API usage log for every request."""
__tablename__ = 'api_usage_logs'
id = db.Column(db.Integer, primary_key=True)
api_key_id = db.Column(db.Integer, db.ForeignKey('api_keys.id'), nullable=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
method = db.Column(db.String(10), nullable=False)
endpoint = db.Column(db.String(500), nullable=False)
blueprint = db.Column(db.String(100), nullable=True)
status_code = db.Column(db.Integer, nullable=False)
response_time_ms = db.Column(db.Float, nullable=True)
ip_address = db.Column(db.String(45), nullable=True)
user_agent = db.Column(db.String(500), nullable=True)
request_size = db.Column(db.Integer, nullable=True)
response_size = db.Column(db.Integer, nullable=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow, index=True)
def to_dict(self):
return {
'id': self.id,
'api_key_id': self.api_key_id,
'user_id': self.user_id,
'method': self.method,
'endpoint': self.endpoint,
'blueprint': self.blueprint,
'status_code': self.status_code,
'response_time_ms': self.response_time_ms,
'ip_address': self.ip_address,
'created_at': self.created_at.isoformat() if self.created_at else None,
}
class ApiUsageSummary(db.Model):
"""Aggregated API usage summary per hour."""
__tablename__ = 'api_usage_summaries'
id = db.Column(db.Integer, primary_key=True)
period_start = db.Column(db.DateTime, nullable=False, index=True)
api_key_id = db.Column(db.Integer, db.ForeignKey('api_keys.id'), nullable=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
endpoint = db.Column(db.String(500), nullable=True)
total_requests = db.Column(db.Integer, default=0)
success_count = db.Column(db.Integer, default=0)
client_error_count = db.Column(db.Integer, default=0)
server_error_count = db.Column(db.Integer, default=0)
avg_response_time_ms = db.Column(db.Float, nullable=True)
max_response_time_ms = db.Column(db.Float, nullable=True)
def to_dict(self):
return {
'id': self.id,
'period_start': self.period_start.isoformat() if self.period_start else None,
'api_key_id': self.api_key_id,
'user_id': self.user_id,
'endpoint': self.endpoint,
'total_requests': self.total_requests,
'success_count': self.success_count,
'client_error_count': self.client_error_count,
'server_error_count': self.server_error_count,
'avg_response_time_ms': self.avg_response_time_ms,
'max_response_time_ms': self.max_response_time_ms,
}