Dieser Commit ist enthalten in:
Claude Project Manager
2025-07-05 17:51:16 +02:00
Commit 0d7d888502
1594 geänderte Dateien mit 122839 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,46 @@
from fastapi import HTTPException, Request, Depends
from sqlalchemy.orm import Session
from sqlalchemy import text
from datetime import datetime
import logging
from app.db.database import get_db
logger = logging.getLogger(__name__)
async def validate_api_key(request: Request, db: Session = Depends(get_db)):
"""Validate API key from X-API-Key header against system_api_key table"""
api_key = request.headers.get("X-API-Key")
if not api_key:
logger.warning("API request without API key")
raise HTTPException(
status_code=401,
detail="API key required",
headers={"WWW-Authenticate": "ApiKey"}
)
# Query the system API key
cursor = db.execute(
text("SELECT api_key FROM system_api_key WHERE id = 1")
)
result = cursor.fetchone()
if not result or result[0] != api_key:
logger.warning(f"Invalid API key attempt: {api_key[:8]}...")
raise HTTPException(
status_code=401,
detail="Invalid API key"
)
# Update usage statistics
db.execute(text("""
UPDATE system_api_key
SET last_used_at = CURRENT_TIMESTAMP,
usage_count = usage_count + 1
WHERE id = 1
"""))
db.commit()
return api_key

Datei anzeigen

@ -0,0 +1,32 @@
from pydantic_settings import BaseSettings
from typing import List
class Settings(BaseSettings):
PROJECT_NAME: str = "License Server"
VERSION: str = "1.0.0"
API_PREFIX: str = "/api"
SECRET_KEY: str = "your-secret-key-change-this-in-production"
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
DATABASE_URL: str = "postgresql://license_user:license_password@db:5432/license_db"
ALLOWED_ORIGINS: List[str] = [
"https://api-software-undso.z5m7q9dk3ah2v1plx6ju.com",
"https://admin-panel-undso.z5m7q9dk3ah2v1plx6ju.com"
]
DEBUG: bool = False
MAX_ACTIVATIONS_PER_LICENSE: int = 5
HEARTBEAT_INTERVAL_MINUTES: int = 15
OFFLINE_GRACE_PERIOD_DAYS: int = 7
class Config:
env_file = ".env"
case_sensitive = True
extra = "ignore" # Ignore extra environment variables
settings = Settings()

Datei anzeigen

@ -0,0 +1,175 @@
from prometheus_client import Counter, Histogram, Gauge, Info
from functools import wraps
import time
# License validation metrics
license_validation_total = Counter(
'license_validation_total',
'Total number of license validations',
['result', 'license_type']
)
license_validation_errors_total = Counter(
'license_validation_errors_total',
'Total number of license validation errors',
['error_type']
)
license_validation_duration_seconds = Histogram(
'license_validation_duration_seconds',
'License validation duration in seconds',
buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
)
# Active licenses gauge
active_licenses_total = Gauge(
'active_licenses_total',
'Total number of active licenses',
['license_type']
)
# Heartbeat metrics
license_heartbeat_total = Counter(
'license_heartbeat_total',
'Total number of license heartbeats received'
)
# Activation metrics
license_activation_total = Counter(
'license_activation_total',
'Total number of license activations',
['result']
)
# Anomaly detection metrics
anomaly_detections_total = Counter(
'anomaly_detections_total',
'Total number of anomalies detected',
['anomaly_type', 'severity']
)
# Concurrent sessions gauge
concurrent_sessions_total = Gauge(
'concurrent_sessions_total',
'Total number of concurrent active sessions'
)
# Database connection pool metrics
db_connection_pool_size = Gauge(
'db_connection_pool_size',
'Database connection pool size'
)
db_connection_pool_used = Gauge(
'db_connection_pool_used',
'Database connections currently in use'
)
# API client metrics
api_requests_total = Counter(
'api_requests_total',
'Total number of API requests',
['method', 'endpoint', 'status']
)
api_request_duration_seconds = Histogram(
'api_request_duration_seconds',
'API request duration in seconds',
['method', 'endpoint'],
buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
)
# Cache metrics
cache_hits_total = Counter(
'cache_hits_total',
'Total number of cache hits',
['cache_type']
)
cache_misses_total = Counter(
'cache_misses_total',
'Total number of cache misses',
['cache_type']
)
# System info
system_info = Info(
'license_server_info',
'License server information'
)
def track_request_metrics(method: str, endpoint: str):
"""Decorator to track API request metrics"""
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
start_time = time.time()
status = "success"
try:
result = await func(*args, **kwargs)
return result
except Exception as e:
status = "error"
raise
finally:
duration = time.time() - start_time
api_requests_total.labels(
method=method,
endpoint=endpoint,
status=status
).inc()
api_request_duration_seconds.labels(
method=method,
endpoint=endpoint
).observe(duration)
return wrapper
return decorator
def track_validation_metrics():
"""Decorator to track license validation metrics"""
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
start_time = time.time()
try:
result = await func(*args, **kwargs)
# Extract result type from the validation result
if result.get('valid'):
result_type = 'success'
elif result.get('error') == 'expired':
result_type = 'expired'
elif result.get('error') == 'invalid':
result_type = 'invalid'
else:
result_type = 'error'
license_type = result.get('license_type', 'unknown')
license_validation_total.labels(
result=result_type,
license_type=license_type
).inc()
return result
except Exception as e:
license_validation_errors_total.labels(
error_type=type(e).__name__
).inc()
raise
finally:
duration = time.time() - start_time
license_validation_duration_seconds.observe(duration)
return wrapper
return decorator
# Initialize system info
def init_metrics(version: str = "1.0.0"):
"""Initialize system metrics"""
system_info.info({
'version': version,
'service': 'license-server'
})

Datei anzeigen

@ -0,0 +1,52 @@
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import HTTPException, Security, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from app.db.database import get_db
from app.models.models import ApiKey
from app.core.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
security = HTTPBearer()
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt
def verify_token(credentials: HTTPAuthorizationCredentials = Security(security)):
token = credentials.credentials
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
return payload
except JWTError:
raise HTTPException(status_code=403, detail="Invalid token")
def verify_api_key(api_key: str, db: Session):
key = db.query(ApiKey).filter(
ApiKey.key == api_key,
ApiKey.is_active == True
).first()
if not key:
raise HTTPException(status_code=401, detail="Invalid API key")
key.last_used = datetime.utcnow()
db.commit()
return key
def get_api_key(
credentials: HTTPAuthorizationCredentials = Security(security),
db: Session = Depends(get_db)
):
api_key = credentials.credentials
return verify_api_key(api_key, db)