Build 2901262240: cache users
This commit is contained in:
@@ -39,6 +39,14 @@ from ..clients.radarr import RadarrClient
|
||||
from ..clients.jellyfin import JellyfinClient
|
||||
from ..clients.jellyseerr import JellyseerrClient
|
||||
from ..services.jellyfin_sync import sync_jellyfin_users
|
||||
from ..services.user_cache import (
|
||||
build_jellyseerr_candidate_map,
|
||||
get_cached_jellyfin_users,
|
||||
get_cached_jellyseerr_users,
|
||||
match_jellyseerr_user_id,
|
||||
save_jellyfin_users_cache,
|
||||
save_jellyseerr_users_cache,
|
||||
)
|
||||
import logging
|
||||
from ..logging_config import configure_logging
|
||||
from ..routers import requests as requests_router
|
||||
@@ -235,6 +243,9 @@ async def radarr_options() -> Dict[str, Any]:
|
||||
|
||||
@router.get("/jellyfin/users")
|
||||
async def jellyfin_users() -> Dict[str, Any]:
|
||||
cached = get_cached_jellyfin_users()
|
||||
if cached is not None:
|
||||
return {"users": cached}
|
||||
runtime = get_runtime_settings()
|
||||
client = JellyfinClient(runtime.jellyfin_base_url, runtime.jellyfin_api_key)
|
||||
if not client.configured():
|
||||
@@ -242,18 +253,7 @@ async def jellyfin_users() -> Dict[str, Any]:
|
||||
users = await client.get_users()
|
||||
if not isinstance(users, list):
|
||||
return {"users": []}
|
||||
results = []
|
||||
for user in users:
|
||||
if not isinstance(user, dict):
|
||||
continue
|
||||
results.append(
|
||||
{
|
||||
"id": user.get("Id"),
|
||||
"name": user.get("Name"),
|
||||
"hasPassword": user.get("HasPassword"),
|
||||
"lastLoginDate": user.get("LastLoginDate"),
|
||||
}
|
||||
)
|
||||
results = save_jellyfin_users_cache(users)
|
||||
return {"users": results}
|
||||
|
||||
|
||||
@@ -262,24 +262,13 @@ async def jellyfin_users_sync() -> Dict[str, Any]:
|
||||
imported = await sync_jellyfin_users()
|
||||
return {"status": "ok", "imported": imported}
|
||||
|
||||
def _normalized_handles(value: Any) -> List[str]:
|
||||
if not isinstance(value, str):
|
||||
return []
|
||||
normalized = value.strip().lower()
|
||||
if not normalized:
|
||||
return []
|
||||
handles = [normalized]
|
||||
if "@" in normalized:
|
||||
handles.append(normalized.split("@", 1)[0])
|
||||
return handles
|
||||
|
||||
def _extract_user_candidates(user: Dict[str, Any]) -> List[str]:
|
||||
candidates: List[str] = []
|
||||
for key in ("username", "email", "displayName", "name"):
|
||||
candidates.extend(_normalized_handles(user.get(key)))
|
||||
return list(dict.fromkeys(candidates))
|
||||
|
||||
async def _fetch_all_jellyseerr_users(client: JellyseerrClient) -> List[Dict[str, Any]]:
|
||||
async def _fetch_all_jellyseerr_users(
|
||||
client: JellyseerrClient, use_cache: bool = True
|
||||
) -> List[Dict[str, Any]]:
|
||||
if use_cache:
|
||||
cached = get_cached_jellyseerr_users()
|
||||
if cached is not None:
|
||||
return cached
|
||||
users: List[Dict[str, Any]] = []
|
||||
take = 100
|
||||
skip = 0
|
||||
@@ -299,6 +288,8 @@ async def _fetch_all_jellyseerr_users(client: JellyseerrClient) -> List[Dict[str
|
||||
if len(batch) < take:
|
||||
break
|
||||
skip += take
|
||||
if users:
|
||||
return save_jellyseerr_users_cache(users)
|
||||
return users
|
||||
|
||||
@router.post("/jellyseerr/users/sync")
|
||||
@@ -307,19 +298,11 @@ async def jellyseerr_users_sync() -> Dict[str, Any]:
|
||||
client = JellyseerrClient(runtime.jellyseerr_base_url, runtime.jellyseerr_api_key)
|
||||
if not client.configured():
|
||||
raise HTTPException(status_code=400, detail="Jellyseerr not configured")
|
||||
jellyseerr_users = await _fetch_all_jellyseerr_users(client)
|
||||
jellyseerr_users = await _fetch_all_jellyseerr_users(client, use_cache=False)
|
||||
if not jellyseerr_users:
|
||||
return {"status": "ok", "matched": 0, "skipped": 0, "total": 0}
|
||||
|
||||
candidate_to_id: Dict[str, int] = {}
|
||||
for user in jellyseerr_users:
|
||||
user_id = user.get("id") or user.get("userId") or user.get("Id")
|
||||
try:
|
||||
user_id = int(user_id)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
for candidate in _extract_user_candidates(user):
|
||||
candidate_to_id.setdefault(candidate, user_id)
|
||||
candidate_to_id = build_jellyseerr_candidate_map(jellyseerr_users)
|
||||
|
||||
updated = 0
|
||||
skipped = 0
|
||||
@@ -329,11 +312,7 @@ async def jellyseerr_users_sync() -> Dict[str, Any]:
|
||||
skipped += 1
|
||||
continue
|
||||
username = user.get("username") or ""
|
||||
matched_id = None
|
||||
for handle in _normalized_handles(username):
|
||||
matched_id = candidate_to_id.get(handle)
|
||||
if matched_id is not None:
|
||||
break
|
||||
matched_id = match_jellyseerr_user_id(username, candidate_to_id)
|
||||
if matched_id is not None:
|
||||
set_user_jellyseerr_id(username, matched_id)
|
||||
updated += 1
|
||||
@@ -356,7 +335,7 @@ async def jellyseerr_users_resync() -> Dict[str, Any]:
|
||||
client = JellyseerrClient(runtime.jellyseerr_base_url, runtime.jellyseerr_api_key)
|
||||
if not client.configured():
|
||||
raise HTTPException(status_code=400, detail="Jellyseerr not configured")
|
||||
jellyseerr_users = await _fetch_all_jellyseerr_users(client)
|
||||
jellyseerr_users = await _fetch_all_jellyseerr_users(client, use_cache=False)
|
||||
if not jellyseerr_users:
|
||||
return {"status": "ok", "imported": 0, "cleared": 0}
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ from ..clients.jellyfin import JellyfinClient
|
||||
from ..clients.jellyseerr import JellyseerrClient
|
||||
from ..security import create_access_token, verify_password
|
||||
from ..auth import get_current_user
|
||||
from ..services.user_cache import (
|
||||
build_jellyseerr_candidate_map,
|
||||
get_cached_jellyseerr_users,
|
||||
match_jellyseerr_user_id,
|
||||
save_jellyfin_users_cache,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||
|
||||
@@ -96,6 +102,8 @@ async def jellyfin_login(form_data: OAuth2PasswordRequestForm = Depends()) -> di
|
||||
client = JellyfinClient(runtime.jellyfin_base_url, runtime.jellyfin_api_key)
|
||||
if not client.configured():
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Jellyfin not configured")
|
||||
jellyseerr_users = get_cached_jellyseerr_users()
|
||||
candidate_map = build_jellyseerr_candidate_map(jellyseerr_users or [])
|
||||
username = form_data.username
|
||||
password = form_data.password
|
||||
user = get_user_by_username(username)
|
||||
@@ -118,15 +126,20 @@ async def jellyfin_login(form_data: OAuth2PasswordRequestForm = Depends()) -> di
|
||||
try:
|
||||
users = await client.get_users()
|
||||
if isinstance(users, list):
|
||||
for user in users:
|
||||
if not isinstance(user, dict):
|
||||
save_jellyfin_users_cache(users)
|
||||
for jellyfin_user in users:
|
||||
if not isinstance(jellyfin_user, dict):
|
||||
continue
|
||||
name = user.get("Name")
|
||||
name = jellyfin_user.get("Name")
|
||||
if isinstance(name, str) and name:
|
||||
create_user_if_missing(name, "jellyfin-user", role="user", auth_provider="jellyfin")
|
||||
except Exception:
|
||||
pass
|
||||
set_jellyfin_auth_cache(username, password)
|
||||
if user and user.get("jellyseerr_user_id") is None and candidate_map:
|
||||
matched_id = match_jellyseerr_user_id(username, candidate_map)
|
||||
if matched_id is not None:
|
||||
set_user_jellyseerr_id(username, matched_id)
|
||||
token = create_access_token(username, "user")
|
||||
set_last_login(username)
|
||||
return {"access_token": token, "token_type": "bearer", "user": {"username": username, "role": "user"}}
|
||||
|
||||
Reference in New Issue
Block a user