Skip to content

Commit d3eaedb

Browse files
committed
refactor: improve code quality, add architecture docs and tests
Major refactoring: - Split main.py (1349→307 lines) into modular route handlers - Create app/dependencies.py with shared state and auth helpers - Create app/routes/proxy.py for HTTP/LLM proxy endpoints - Create app/routes/rapidapi.py for RapidAPI integration - Update app/routes/health.py with all health check endpoints Improved typing: - Add Pydantic enums (HTTPMethod, MessageRole, CostPolicy) - Add typed response models (TokenUsage, LLMResponseData) - Add field validators with constraints (ge, le, min_length) - Use Literal types for success/error flags Code quality fixes: - Replace print() with logger.warning() in analytics.py - Remove unused imports - Improve import organization Documentation: - Add docs/ARCHITECTURE.md with system overview - Document request flow, design decisions, deployment Testing: - Add tests/test_routes_business.py for business routes - Cover onboarding, analytics, health, and schema validation
1 parent 4b2576b commit d3eaedb

File tree

10 files changed

+2840
-1332
lines changed

10 files changed

+2840
-1332
lines changed

app/dependencies.py

Lines changed: 477 additions & 0 deletions
Large diffs are not rendered by default.

app/main.py

Lines changed: 211 additions & 1254 deletions
Large diffs are not rendered by default.

app/routes/__init__.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,19 @@
1-
"""ReliAPI Routes Package."""
1+
"""ReliAPI Routes Package.
22
3+
This package contains all route handlers organized by domain:
4+
5+
Core routes:
6+
- health: Health check and monitoring endpoints
7+
- proxy: HTTP and LLM proxy endpoints
8+
- rapidapi: RapidAPI integration endpoints
9+
10+
Business routes:
11+
- paddle: Paddle payment integration
12+
- onboarding: Self-service API key generation
13+
- analytics: Usage analytics tracking
14+
- calculators: ROI/pricing calculators
15+
- dashboard: Admin dashboard
16+
"""
17+
from reliapi.app.routes import health, proxy, rapidapi
18+
19+
__all__ = ["health", "proxy", "rapidapi"]

app/routes/analytics.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@
33
This module provides analytics endpoints for tracking user behavior,
44
conversion funnels, and events. All tracking is automated through APIs.
55
"""
6-
6+
import base64
7+
import json
8+
import logging
79
import os
8-
from typing import Dict, Any, Optional, List
910
from datetime import datetime, timedelta
10-
from fastapi import APIRouter, Request, Header, Body
11-
from pydantic import BaseModel, Field
11+
from typing import Any, Dict, Optional
12+
1213
import httpx
14+
from fastapi import APIRouter, Request
15+
from pydantic import BaseModel, Field
16+
17+
logger = logging.getLogger(__name__)
1318

1419
router = APIRouter(prefix="/analytics", tags=["analytics"])
1520

@@ -174,16 +179,12 @@ async def _track_google_analytics(event_data: Dict[str, Any], ga_id: str) -> Non
174179
timeout=5.0,
175180
)
176181
except Exception as e:
177-
# Log error but don't fail the request
178-
print(f"Google Analytics tracking error: {e}")
182+
logger.warning(f"Google Analytics tracking error: {e}")
179183

180184

181185
async def _track_mixpanel(event_data: Dict[str, Any], token: str) -> None:
182186
"""Track event in Mixpanel."""
183187
try:
184-
import base64
185-
import json
186-
187188
# Mixpanel uses base64 encoded JSON
188189
event_payload = {
189190
"event": event_data["event_name"],
@@ -203,8 +204,7 @@ async def _track_mixpanel(event_data: Dict[str, Any], token: str) -> None:
203204
timeout=5.0,
204205
)
205206
except Exception as e:
206-
# Log error but don't fail the request
207-
print(f"Mixpanel tracking error: {e}")
207+
logger.warning(f"Mixpanel tracking error: {e}")
208208

209209

210210
async def _track_posthog(event_data: Dict[str, Any], api_key: str, host: str) -> None:
@@ -222,6 +222,4 @@ async def _track_posthog(event_data: Dict[str, Any], api_key: str, host: str) ->
222222
timeout=5.0,
223223
)
224224
except Exception as e:
225-
# Log error but don't fail the request
226-
print(f"PostHog tracking error: {e}")
227-
225+
logger.warning(f"PostHog tracking error: {e}")

app/routes/health.py

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,104 @@
1-
"""Health check endpoints.
1+
"""Health check and monitoring endpoints.
22
3-
This module provides health check endpoints for load balancers and monitoring.
4-
The actual health endpoints are defined in reliapi.app.main, this module
5-
is provided for api-template compatibility.
3+
This module provides:
4+
- GET /health - Basic health check
5+
- GET /healthz - Kubernetes-style health check
6+
- GET /readyz - Readiness check
7+
- GET /livez - Liveness check
8+
- GET /metrics - Prometheus metrics
69
"""
10+
import logging
711

8-
from fastapi import APIRouter
12+
from fastapi import APIRouter, HTTPException, Request
13+
from fastapi.responses import Response
14+
from prometheus_client import CONTENT_TYPE_LATEST, generate_latest
915
from pydantic import BaseModel
1016

11-
router = APIRouter()
17+
from reliapi.app.dependencies import get_app_state
18+
19+
logger = logging.getLogger(__name__)
20+
21+
router = APIRouter(tags=["Health"])
1222

1323

1424
class HealthResponse(BaseModel):
1525
"""Health check response model."""
1626

1727
status: str
18-
version: str
28+
version: str = "1.0.7"
29+
30+
31+
class StatusResponse(BaseModel):
32+
"""Simple status response model."""
33+
34+
status: str
35+
36+
37+
def _check_health_rate_limit(request: Request, prefix: str) -> None:
38+
"""Check rate limit for health endpoints.
39+
40+
Args:
41+
request: FastAPI request
42+
prefix: Rate limit prefix (e.g., 'healthz', 'metrics')
43+
44+
Raises:
45+
HTTPException: If rate limit exceeded
46+
"""
47+
state = get_app_state()
48+
49+
if not state.rate_limiter:
50+
return
51+
52+
client_ip = request.client.host if request.client else "unknown"
53+
limit = 10 if prefix == "metrics" else 20
54+
55+
allowed, error = state.rate_limiter.check_ip_rate_limit(
56+
client_ip, limit_per_minute=limit, prefix=prefix
57+
)
58+
59+
if not allowed:
60+
if prefix == "metrics":
61+
logger.warning(f"Rate limit exceeded for /metrics endpoint: IP={client_ip}")
62+
63+
raise HTTPException(
64+
status_code=429,
65+
detail={
66+
"type": "rate_limit_error",
67+
"code": error,
68+
"message": f"Rate limit exceeded for {prefix} endpoint.",
69+
},
70+
)
1971

2072

2173
@router.get("/health", response_model=HealthResponse)
2274
async def health_check() -> HealthResponse:
23-
"""Health check endpoint for load balancers and monitoring."""
24-
return HealthResponse(status="ok", version="1.0.7")
75+
"""Basic health check endpoint for load balancers and monitoring."""
76+
return HealthResponse(status="ok")
77+
78+
79+
@router.get("/healthz", response_model=StatusResponse)
80+
async def healthz(request: Request) -> StatusResponse:
81+
"""Kubernetes-style health check endpoint with optional rate limiting."""
82+
_check_health_rate_limit(request, "healthz")
83+
return StatusResponse(status="healthy")
84+
85+
86+
@router.get("/readyz", response_model=StatusResponse)
87+
async def readyz(request: Request) -> StatusResponse:
88+
"""Readiness check endpoint with optional rate limiting."""
89+
_check_health_rate_limit(request, "readyz")
90+
return StatusResponse(status="ready")
91+
2592

93+
@router.get("/livez", response_model=StatusResponse)
94+
async def livez(request: Request) -> StatusResponse:
95+
"""Liveness check endpoint with optional rate limiting."""
96+
_check_health_rate_limit(request, "livez")
97+
return StatusResponse(status="alive")
2698

27-
@router.get("/healthz", response_model=HealthResponse)
28-
async def healthz() -> HealthResponse:
29-
"""Kubernetes-style health check endpoint."""
30-
return HealthResponse(status="ok", version="1.0.7")
3199

100+
@router.get("/metrics")
101+
async def metrics(request: Request) -> Response:
102+
"""Prometheus metrics endpoint with rate limiting."""
103+
_check_health_rate_limit(request, "metrics")
104+
return Response(content=generate_latest(), media_type=CONTENT_TYPE_LATEST)

0 commit comments

Comments
 (0)