feat: live remote key list with auto-upsert and safe group name extraction
- list_generated_keys now fetches live keys from upstream API, merges with local DB: remote keys with plaintext values are auto-upserted (by group_id+managed_prefix), remote-only keys shown as unimportable - Use _fetch_remote_managed_prefixes to support custom key prefixes - Group remote keys by (group_id, prefix), pick latest by key_id - Extract _remote_group_name helper for safe group name parsing (handles dict group field from Meow upstream) - Frontend excludes orphaned keys from importable list - Backend import endpoint reconciles upstream before importing
This commit is contained in:
@@ -418,10 +418,10 @@ def sync_account_priorities_for_upstream(db: Session, upstream_id: int) -> list[
|
||||
return all_results
|
||||
|
||||
|
||||
def _fetch_remote_managed_key_ids(db: Session, client, upstream_id: int) -> set[str]:
|
||||
"""查询本地 distinct managed_prefix,分别拉远端活跃 Key ID 集合。
|
||||
def _fetch_remote_managed_prefixes(db: Session, upstream_id: int) -> list[str]:
|
||||
"""查询本地 distinct managed_prefix。
|
||||
|
||||
返回全部找到的远端 Key ID(合并多个 prefix 的结果)。
|
||||
返回该上游所有已使用的 prefix 列表。空时回退 ["SmartUp"] 兼容旧数据。
|
||||
"""
|
||||
prefixes = [
|
||||
row[0] for row in
|
||||
@@ -433,10 +433,16 @@ def _fetch_remote_managed_key_ids(db: Session, client, upstream_id: int) -> set[
|
||||
.distinct()
|
||||
.all()
|
||||
]
|
||||
if not prefixes:
|
||||
prefixes = ["SmartUp"]
|
||||
return prefixes if prefixes else ["SmartUp"]
|
||||
|
||||
|
||||
def _fetch_remote_managed_key_ids(db: Session, client, upstream_id: int) -> set[str]:
|
||||
"""查询本地 distinct managed_prefix,分别拉远端活跃 Key ID 集合。
|
||||
|
||||
返回全部找到的远端 Key ID(合并多个 prefix 的结果)。
|
||||
"""
|
||||
all_ids: set[str] = set()
|
||||
for prefix in prefixes:
|
||||
for prefix in _fetch_remote_managed_prefixes(db, upstream_id):
|
||||
remote_keys = client.list_api_keys(search=prefix, status="active")
|
||||
all_ids.update(str(k["id"]) for k in remote_keys if k.get("id"))
|
||||
return all_ids
|
||||
@@ -490,12 +496,13 @@ def reconcile_upstream_keys(
|
||||
|
||||
|
||||
def reconcile_upstream_keys_full(db: Session, upstream_id: int) -> bool:
|
||||
"""完整的 Key 对账:登录上游、拉取分组和远端 Key 列表、调用 reconcile_upstream_keys。
|
||||
"""完整的 Key 对账:拉取最新快照的分组 + 登录上游查远端 Key 列表 → 调用 reconcile_upstream_keys。
|
||||
|
||||
活跃分组 ID 从最新快照获取(与调度器一致),而非调用 live API 避免格式不一致。
|
||||
安全规则:
|
||||
- 分组拉取成功 → 才允许分组级清理。
|
||||
- 快照存在 → 才允许分组级清理。
|
||||
- 远端 Key 列表拉取成功 → 才允许 key_id 级清理。
|
||||
- 登录/分组拉取失败 → 不做任何删除/标记。
|
||||
- 两者均失败 → 不做任何删除/标记。
|
||||
|
||||
支持自定义 managed_prefix:查询本地 distinct prefix,分别查远端。
|
||||
"""
|
||||
@@ -512,6 +519,12 @@ def reconcile_upstream_keys_full(db: Session, upstream_id: int) -> bool:
|
||||
remote_key_ids: set[str] | None = None
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# 从最新快照获取活跃分组 ID(与调度器 _sync_upstream_keys 一致)
|
||||
groups = latest_rate_map(db, upstream_id)
|
||||
if groups:
|
||||
active_group_ids = set(groups.keys())
|
||||
groups_fetched = True
|
||||
|
||||
try:
|
||||
with UpstreamClient(
|
||||
base_url=upstream.base_url,
|
||||
@@ -521,11 +534,6 @@ def reconcile_upstream_keys_full(db: Session, upstream_id: int) -> bool:
|
||||
timeout=float(upstream.timeout_seconds),
|
||||
) as client:
|
||||
client.login()
|
||||
# 获取当前分组 ID 集合
|
||||
groups = client.get_available_groups(upstream.groups_endpoint)
|
||||
active_group_ids = {g.get("group_id") or g.get("id") or "" for g in groups if isinstance(g, dict)}
|
||||
active_group_ids.discard("")
|
||||
groups_fetched = True
|
||||
# 获取远端 Key 列表(支持自定义 managed_prefix)
|
||||
remote_key_ids = _fetch_remote_managed_key_ids(db, client, upstream_id)
|
||||
keys_fetched = True
|
||||
|
||||
Reference in New Issue
Block a user