picpocket / client_source.py
chawin.chen
fix: isolate rankings and image records by client source
d2ae3d7
Raw History Blame Contribute Delete
1.76 kB
"""Server-owned request provenance, inherited by async output-recording tasks."""
from contextvars import ContextVar
from typing import Awaitable, Callable
from fastapi import Request, Response
from fastapi.routing import APIRoute
CLIENT_SOURCES = frozenset({'android', 'wechat', 'ios', 'web', 'unknown'})
current_client_source: ContextVar[str] = ContextVar('client_source', default='unknown')
def validate_client_source(source: str) -> str:
if source not in CLIENT_SOURCES:
raise ValueError('Unsupported client source')
return source
def resolve_client_source(request: Request) -> str:
"""Explicit routes win; legacy shared Android routes carry x-device-id."""
path = request.url.path.rstrip('/')
for prefix, source in (('/facescore/app/', 'android'),
('/facescore/wechat/', 'wechat'),
('/facescore/ios/', 'ios'), ('/facescore/web/', 'web')):
if path.startswith(prefix):
return source
# Original analyze is the mini-program entry; app/analyze delegates internally.
if path == '/facescore/analyze':
return 'wechat'
if path.startswith('/facescore/'):
return 'android' if request.headers.get('x-device-id', '').strip() else 'wechat'
return 'unknown'
class ClientSourceRoute(APIRoute):
def get_route_handler(self) -> Callable[[Request], Awaitable[Response]]:
original_handler = super().get_route_handler()
async def handle(request: Request) -> Response:
token = current_client_source.set(resolve_client_source(request))
try:
return await original_handler(request)
finally:
current_client_source.reset(token)
return handle