Skip to content

Commit 6329166

Browse files
committed
Phase 1: Final stabilization. Purged legacy auth (auth_data, verify_token) and resolved all IDE diagnostic errors.
1 parent 9ee430a commit 6329166

6 files changed

Lines changed: 16 additions & 118 deletions

File tree

api/auth.py

Lines changed: 0 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -16,53 +16,6 @@
1616

1717
logger = structlog.get_logger(__name__)
1818

19-
_PRO_STATE_CACHE: "OrderedDict[str, tuple[bool, float]]" = OrderedDict()
20-
_PRO_STATE_CACHE_LOCK = threading.Lock()
21-
22-
23-
def _pro_cache_ttl_seconds() -> int:
24-
try:
25-
configured_ttl = int(getattr(get_settings(), "pro_state_cache_ttl_seconds", 30) or 30)
26-
except (ValueError, TypeError):
27-
configured_ttl = 30
28-
return min(max(configured_ttl, 1), 30)
29-
30-
31-
def _pro_cache_max_entries() -> int:
32-
try:
33-
configured_max = int(getattr(get_settings(), "pro_state_cache_max_entries", 1000) or 1000)
34-
except (ValueError, TypeError):
35-
configured_max = 1000
36-
return min(max(configured_max, 1), 10000)
37-
38-
39-
def _prune_pro_cache_locked(now: float) -> None:
40-
expired_keys = [user_id for user_id, (_is_pro, expires_at) in _PRO_STATE_CACHE.items() if expires_at <= now]
41-
for user_id in expired_keys:
42-
_PRO_STATE_CACHE.pop(user_id, None)
43-
44-
max_entries = _pro_cache_max_entries()
45-
while len(_PRO_STATE_CACHE) > max_entries:
46-
_PRO_STATE_CACHE.popitem(last=False)
47-
48-
49-
def invalidate_pro_cache(user_id: str) -> None:
50-
if not user_id:
51-
return
52-
with _PRO_STATE_CACHE_LOCK:
53-
_PRO_STATE_CACHE.pop(user_id, None)
54-
async def _clear() -> None:
55-
redis = await safe_redis_call(get_redis, operation="connect")
56-
if redis is None:
57-
return
58-
await safe_redis_call(redis.delete, f"depthapi:user:is_pro:{user_id}", operation="delete")
59-
60-
try:
61-
asyncio.create_task(_clear())
62-
except Exception as exc:
63-
logger.debug("auth_invalidate_pro_cache_failed", user_id_hash=anonymize_user_id(user_id), error=str(exc))
64-
65-
6619
@lru_cache(maxsize=1)
6720
def get_supabase() -> Client | None:
6821
settings = get_settings()
@@ -82,51 +35,3 @@ def get_supabase_admin() -> Client | None:
8235
if hasattr(secret_key, "get_secret_value"):
8336
secret_key = secret_key.get_secret_value()
8437
return create_client(settings.supabase_url, secret_key) # type: ignore
85-
86-
87-
async def check_is_pro(user_id: str, force_refresh: bool = False) -> bool:
88-
"""Check if a user has pro status in the database (deprecated for v1)."""
89-
if not user_id:
90-
return False
91-
92-
now = time.time()
93-
if not force_refresh:
94-
with _PRO_STATE_CACHE_LOCK:
95-
_prune_pro_cache_locked(now)
96-
cached = _PRO_STATE_CACHE.get(user_id)
97-
if cached and cached[1] > now:
98-
_PRO_STATE_CACHE.move_to_end(user_id)
99-
return cached[0]
100-
try:
101-
redis = await safe_redis_call(get_redis, operation="connect")
102-
raw = await safe_redis_call(redis.get, f"depthapi:user:is_pro:{user_id}", operation="get") if redis else None
103-
if raw is not None:
104-
value = raw.decode("utf-8") if isinstance(raw, (bytes, bytearray)) else str(raw)
105-
is_pro = value == "1"
106-
with _PRO_STATE_CACHE_LOCK:
107-
_prune_pro_cache_locked(now)
108-
_PRO_STATE_CACHE[user_id] = (is_pro, now + _pro_cache_ttl_seconds())
109-
_PRO_STATE_CACHE.move_to_end(user_id)
110-
return is_pro
111-
except Exception as exc:
112-
logger.debug("auth_pro_cache_redis_read_failed", user_id_hash=anonymize_user_id(user_id), error=str(exc))
113-
114-
supabase = get_supabase_admin()
115-
if not supabase:
116-
return False
117-
118-
try:
119-
response = await asyncio.to_thread(
120-
lambda: supabase.table("users").select("is_pro").eq("id", user_id).single().execute()
121-
)
122-
data = getattr(response, "data", None)
123-
is_pro = bool(data.get("is_pro", False)) if isinstance(data, dict) else False
124-
125-
with _PRO_STATE_CACHE_LOCK:
126-
_prune_pro_cache_locked(now)
127-
_PRO_STATE_CACHE[user_id] = (is_pro, now + _pro_cache_ttl_seconds())
128-
_PRO_STATE_CACHE.move_to_end(user_id)
129-
return is_pro
130-
except Exception as e:
131-
logger.error("auth_check_is_pro_failed", user_id_hash=anonymize_user_id(user_id), error=str(e))
132-
return False

api/routers/messages.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,16 @@ async def _send_message_handler(
5252
_core.log_sampled_success = log_sampled_success
5353
_core.cache_set_value = cache_set_value
5454
_core.logger = logger
55-
# Bridge: wrap ApiKeyRecord into the shape messages_core expects
56-
auth_data = {"api_key": api_key, "is_pro": api_key.is_pro}
57-
return await _core._send_message_handler(request=request, auth_data=auth_data)
55+
return await _core._send_message_handler(request=request, api_key=api_key)
5856

5957

6058
@router.post("/messages")
6159
async def send_message(
6260
request: Request,
6361
api_key: ApiKeyRecord = Depends(verify_api_key),
6462
) -> StreamingResponse:
65-
auth_data = {"api_key": api_key, "is_pro": api_key.is_pro}
6663
return await _core._message_workflow.process_message(
6764
request=request,
68-
auth_data=auth_data,
65+
api_key=api_key,
6966
handler=_send_message_handler,
7067
)

api/routers/messages_core.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,10 +1644,10 @@ async def get_next_chunk() -> str:
16441644
@router.post("/messages")
16451645
async def send_message(
16461646
request: Request,
1647-
auth_data: dict = Depends(verify_token),
1647+
api_key: ApiKeyRecord = Depends(verify_api_key),
16481648
) -> StreamingResponse:
16491649
return await _message_workflow.process_message(
16501650
request=request,
1651-
auth_data=auth_data,
1651+
api_key=api_key,
16521652
handler=_send_message_handler,
16531653
)

api/routers/query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ async def query_topic_stream(
407407
user_id_raw=api_key.id,
408408
user_id_hash=user_id_hash,
409409
topic_hash=topic_hash,
410-
auth_data={"api_key": api_key},
410+
api_key=api_key,
411411
cache_get=cache_get,
412412
cache_set=cache_set,
413413
cache_key_value=cache_key(topic, level, mode),

api/services/message_workflow.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from fastapi import Request
1010
from fastapi.responses import StreamingResponse
1111

12-
from logging_config import logger
12+
from services.api_key_auth import ApiKeyRecord
1313

1414
T = TypeVar("T")
1515

@@ -35,10 +35,10 @@ async def process_message(
3535
self,
3636
*,
3737
request: Request,
38-
auth_data: dict,
39-
handler: Callable[[Request, dict], Awaitable[StreamingResponse]],
38+
api_key: ApiKeyRecord,
39+
handler: Callable[[Request, ApiKeyRecord], Awaitable[StreamingResponse]],
4040
) -> StreamingResponse:
4141
async def _execute() -> StreamingResponse:
42-
return await handler(request, auth_data)
42+
return await handler(request, api_key)
4343

4444
return await self.run_stage("process_message", _execute)

api/services/query_streaming.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import time
1313
from typing import Any
1414
from collections.abc import AsyncIterable, AsyncIterator, Iterable
15+
from services.api_key_auth import ApiKeyRecord
1516

1617
from fastapi.responses import StreamingResponse
1718

@@ -107,7 +108,7 @@ def build_query_stream_response(
107108
user_id_raw: str | None,
108109
user_id_hash: str | None,
109110
topic_hash: str,
110-
auth_data,
111+
api_key: ApiKeyRecord,
111112
cache_get,
112113
cache_set,
113114
cache_key_value: str,
@@ -207,8 +208,7 @@ def emit(event: str, payload: dict[str, Any] | str) -> str:
207208
for index in range(0, len(content), chunk_size):
208209
yield emit("chunk", {"chunk": content[index : index + chunk_size]})
209210
yield emit("done", "[DONE]")
210-
if auth_data:
211-
await persist_history(auth_data["user"], topic, [level], mode)
211+
await persist_history(api_key.id, topic, [level], mode)
212212
return
213213

214214
stream = generate_stream_explanation(
@@ -319,17 +319,15 @@ async def get_next_chunk():
319319
yield emit("done", "[DONE]")
320320
if full_content.strip():
321321
await cache_set(cache_key_value, {"text": full_content})
322-
if auth_data:
323-
await persist_history(auth_data["user"], topic, [level], mode)
322+
await persist_history(api_key.id, topic, [level], mode)
324323
return
325324
if timed_out:
326325
cutoff_message = "\n\n[Response truncated to stay within serverless limits. Retry to continue.]"
327326
yield emit("chunk", {"chunk": cutoff_message})
328327

329328
if full_content.strip():
330329
await cache_set(cache_key_value, {"text": full_content})
331-
if auth_data:
332-
await persist_history(auth_data["user"], topic, [level], mode)
330+
await persist_history(api_key.id, topic, [level], mode)
333331

334332
yield emit("done", "[DONE]")
335333
except Exception as exc:
@@ -380,8 +378,7 @@ async def get_next_chunk():
380378
yield emit("done", "[DONE]")
381379
if full_content.strip():
382380
await cache_set(cache_key_value, {"text": full_content})
383-
if auth_data:
384-
await persist_history(auth_data["user"], topic, [level], mode)
381+
await persist_history(api_key.id, topic, [level], mode)
385382
return
386383
except Exception as fallback_exc:
387384
logger.error(
@@ -401,8 +398,7 @@ async def get_next_chunk():
401398
yield emit("done", "[DONE]")
402399
if full_content.strip():
403400
await cache_set(cache_key_value, {"text": full_content})
404-
if auth_data:
405-
await persist_history(auth_data["user"], topic, [level], mode)
401+
await persist_history(api_key.id, topic, [level], mode)
406402
return
407403
yield emit("error", {"error": "An error occurred while streaming. Please try again."})
408404
yield emit("done", "[DONE]")

0 commit comments

Comments
 (0)