Coverage for src/alprina_cli/api/middleware/usage_check.py: 0%
35 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-14 11:27 +0100
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-14 11:27 +0100
1"""
2Usage Check Middleware
4Enforces usage limits for scans and API requests.
5"""
7from fastapi import Request, HTTPException, Depends
8from typing import Dict, Any, Optional
9from loguru import logger
11from ..middleware.auth import get_current_user
12from ..services.usage_service import usage_service
13from ..services.neon_service import neon_service
16async def check_usage_limits(
17 request: Request,
18 user: Dict[str, Any] = Depends(get_current_user)
19) -> Dict[str, Any]:
20 """
21 Check if user is within usage limits.
23 Args:
24 request: FastAPI request object
25 user: Current authenticated user
27 Returns:
28 User object with usage info
30 Raises:
31 HTTPException: If usage limits exceeded
32 """
33 user_id = user["id"]
34 tier = user["tier"]
36 # Check API rate limit
37 within_limit, error = await usage_service.enforce_rate_limit(
38 user_id,
39 tier,
40 neon_service
41 )
43 if not within_limit:
44 logger.warning(f"Rate limit exceeded for user {user_id}")
45 raise HTTPException(
46 status_code=429,
47 detail={
48 "error": "rate_limit_exceeded",
49 "message": error,
50 "tier": tier,
51 "upgrade_url": "https://platform.alprina.ai/upgrade"
52 }
53 )
55 # Add usage info to user object
56 user["usage_checked"] = True
57 return user
60async def check_scan_permission(
61 workflow_mode: str = "single",
62 user: Dict[str, Any] = Depends(get_current_user)
63) -> Dict[str, Any]:
64 """
65 Check if user has permission to perform scan.
67 Args:
68 workflow_mode: Workflow mode (single, parallel, sequential, coordinated)
69 user: Current authenticated user
71 Returns:
72 User object
74 Raises:
75 HTTPException: If scan not permitted
76 """
77 user_id = user["id"]
78 tier = user["tier"]
80 # Check scan limit
81 can_scan, error, usage_info = await usage_service.check_scan_limit(
82 user_id,
83 tier,
84 neon_service
85 )
87 if not can_scan:
88 logger.warning(f"Scan limit exceeded for user {user_id}")
89 raise HTTPException(
90 status_code=403,
91 detail={
92 "error": "scan_limit_exceeded",
93 "message": error,
94 "usage": usage_info,
95 "tier": tier,
96 "upgrade_url": "https://platform.alprina.ai/upgrade"
97 }
98 )
100 # Check workflow access
101 has_access, error = await usage_service.check_workflow_access(tier, workflow_mode)
103 if not has_access:
104 logger.warning(
105 f"User {user_id} attempted {workflow_mode} workflow "
106 f"without access (tier: {tier})"
107 )
108 raise HTTPException(
109 status_code=403,
110 detail={
111 "error": "workflow_not_available",
112 "message": error,
113 "tier": tier,
114 "required_tier": "pro",
115 "upgrade_url": "https://platform.alprina.ai/upgrade"
116 }
117 )
119 # Add usage info to user
120 user["usage_info"] = usage_info
121 return user
124async def record_scan_usage(
125 user_id: str,
126 tier: str,
127 workflow_mode: str,
128 file_count: int,
129 scan_data: Dict[str, Any]
130):
131 """
132 Record scan usage after successful scan.
134 Args:
135 user_id: User ID
136 tier: User tier
137 workflow_mode: Workflow mode used
138 file_count: Number of files scanned
139 scan_data: Full scan data
140 """
141 try:
142 # Increment scan count
143 await usage_service.increment_scan_count(
144 user_id,
145 tier,
146 workflow_mode,
147 file_count,
148 neon_service
149 )
151 # Record in history
152 await usage_service.record_scan(
153 user_id,
154 scan_data,
155 neon_service
156 )
158 logger.info(f"Recorded scan usage for user {user_id}")
160 except Exception as e:
161 logger.error(f"Failed to record scan usage: {e}")
162 # Don't fail the scan if usage recording fails