feat: support gated/private Hugging Face repos via access token

Add a huggingface_api_key setting (Settings UI, HF_TOKEN /
HUGGING_FACE_HUB_TOKEN env override) and attach it as a Bearer token
to Hugging Face file listing, model card fetching and downloads, so
gated and private repositories can be downloaded once the user has
accepted the repo terms.

- fetch_json/fetch_text accept custom headers; ModelSource gains an
  auth_headers() hook so handlers stay platform-agnostic
- 401/403 from the tree API now explain how to fix (configure token /
  accept gated terms)
- aria2 pre-resolves huggingface.co redirects and strips credentials
  before handing the signed CDN URL to aria2, mirroring the CivitAI
  handling so the token never leaks to the CDN
- settings API exposes huggingface_api_key_set only; the raw key joins
  _NO_SYNC_KEYS
This commit is contained in:
Will Miao
2026-09-25 18:44:06 +08:00
parent 067e605e75
commit 8b7ba59263
23 changed files with 446 additions and 23 deletions
+3
View File
@@ -1506,6 +1506,7 @@ class SettingsHandler:
# Sensitive — never expose the actual value to the frontend;
# frontend receives a boolean instead (*_set).
"civitai_api_key",
"huggingface_api_key",
"llm_api_key",
}
)
@@ -1564,6 +1565,8 @@ class SettingsHandler:
# Sensitive fields: only expose a boolean indicating whether set
raw_key = self._settings.get("civitai_api_key")
response_data["civitai_api_key_set"] = bool(raw_key)
raw_hf_key = self._settings.get("huggingface_api_key")
response_data["huggingface_api_key_set"] = bool(raw_hf_key)
raw_llm_key = self._settings.get("llm_api_key")
response_data["llm_api_key_set"] = bool(raw_llm_key)
# Derived capability flag (not persisted): whether the host exposes
@@ -580,6 +580,10 @@ class ModelSourceHandler:
get_settings_manager().get("download_backend", "default")
)
# Site-specific credentials (e.g. a Hugging Face access token for
# gated/private repositories); empty for anonymous downloads.
auth_headers = source.auth_headers()
if download_backend == "aria2":
aria2 = await Aria2Downloader.get_instance()
aid = download_id or f"{source.platform}_{repo}_{filename}"
@@ -589,6 +593,7 @@ class ModelSourceHandler:
save_path=dest_path,
download_id=aid,
progress_callback=progress_callback,
headers=auth_headers or None,
)
if ok:
await _save_source_metadata(
@@ -618,6 +623,7 @@ class ModelSourceHandler:
use_auth=False,
allow_resume=True,
progress_callback=progress_callback,
custom_headers=auth_headers or None,
)
if success:
await _save_source_metadata(