import time from config import Config from http_client import request_json from error_handling import AppServiceError _CMS_TOKEN_CACHE = {"token": "", "expires_at": 0} def _resolve_login_url(): if Config.CMS_LOGIN_URL: return Config.CMS_LOGIN_URL if Config.CMS_BASE_URL: return f"{Config.CMS_BASE_URL.rstrip('/')}/api/auth/login" raise AppServiceError( "CMS login url is not configured", category="validation", code="CMS_CONFIG_MISSING", provider="cms", ) def _resolve_add_share_url(): if Config.CMS_ADD_SHARE_URL: return Config.CMS_ADD_SHARE_URL if Config.CMS_BASE_URL: return f"{Config.CMS_BASE_URL.rstrip('/')}/api/cloud/add_share_down" raise AppServiceError( "CMS add share url is not configured", category="validation", code="CMS_CONFIG_MISSING", provider="cms", ) def _headers(token): headers = {"Content-Type": "application/json"} if token: headers["Authorization"] = f"Bearer {token}" return headers def _extract_cms_token(login_result): data = login_result.get("data") or {} token = (data.get("data") or {}).get("token") if not token: raise AppServiceError( "CMS login succeeded but token missing", category="upstream", code="CMS_TOKEN_MISSING", provider="cms", detail={"response": data}, ) return token def _login_and_get_token(): if Config.CMS_TOKEN: return Config.CMS_TOKEN if not Config.CMS_USERNAME or not Config.CMS_PASSWORD: raise AppServiceError( "CMS username/password is required when CMS_TOKEN is not provided", category="validation", code="CMS_CONFIG_MISSING", provider="cms", ) login_result = request_json( _resolve_login_url(), method="POST", payload={"username": Config.CMS_USERNAME, "password": Config.CMS_PASSWORD}, headers={"Content-Type": "application/json"}, max_retry=Config.MAX_RETRY, retry_delay_ms=Config.RETRY_DELAY_MS, provider="cms", ) token = _extract_cms_token(login_result) _CMS_TOKEN_CACHE["token"] = token _CMS_TOKEN_CACHE["expires_at"] = int(time.time()) + 3500 return token def _get_cached_token(): if Config.CMS_TOKEN: return Config.CMS_TOKEN if _CMS_TOKEN_CACHE["token"] and _CMS_TOKEN_CACHE["expires_at"] > int(time.time()): return _CMS_TOKEN_CACHE["token"] return _login_and_get_token() def _should_refresh_token(response_data): code = (response_data or {}).get("code") msg = (response_data or {}).get("msg") or (response_data or {}).get("message") or "" return code != 200 and msg != "提取分享链接失败" def _add_share(url_value, token): return request_json( _resolve_add_share_url(), method="POST", payload={"url": url_value}, headers=_headers(token), max_retry=Config.MAX_RETRY, retry_delay_ms=Config.RETRY_DELAY_MS, provider="cms", ) def create_resource(payload): resource = payload.get("resource") or {} share_url = resource.get("unlockUrl") or payload.get("url") if not share_url: raise AppServiceError( "CMS ingest requires unlockUrl", category="validation", code="CMS_INPUT_INVALID", provider="cms", ) token = _get_cached_token() first_result = _add_share(share_url, token) first_data = first_result.get("data") or {} if _should_refresh_token(first_data): refreshed_token = _login_and_get_token() second_result = _add_share(share_url, refreshed_token) second_data = second_result.get("data") or {} if (second_data.get("code") or 0) != 200: raise AppServiceError( second_data.get("msg") or second_data.get("message") or "CMS ingest failed", category="upstream", code=str(second_data.get("code") or "CMS_INGEST_FAILED"), provider="cms", detail={"response": second_data}, ) return second_result if (first_data.get("code") or 0) != 200: raise AppServiceError( first_data.get("msg") or first_data.get("message") or "CMS ingest failed", category="business_rule" if (first_data.get("msg") == "提取分享链接失败") else "upstream", code=str(first_data.get("code") or "CMS_INGEST_FAILED"), provider="cms", detail={"response": first_data}, ) return first_result