fix: reconcile upstream keys on list/generate/import to prevent stale key imports

- Extract reconcile_upstream_keys() to website_sync.py (shared scheduler + on-demand)
- Add reconcile_upstream_keys_full() for on-demand reconciliation at three entry points:
  list_generated_keys, generate_keys_by_groups, import_upstream_keys_as_accounts
- Safe on failure: active_group_ids=None / remote_key_ids=None skip cleanup
- Support custom managed_prefix via _fetch_remote_managed_key_ids() helper
- Exclude orphaned keys from frontend importable list
- Remove hardcoded search='SmartUp' from scheduler path
This commit is contained in:
liumangmang
2026-06-01 11:29:37 +08:00
parent 3408795289
commit bea4344bb3
5 changed files with 175 additions and 46 deletions
+11
View File
@@ -127,6 +127,11 @@ def list_upstreams(db: Session = Depends(get_db), _=Depends(get_current_user)):
def list_generated_keys(uid: int, db: Session = Depends(get_db), _=Depends(get_current_user)):
if not db.query(Upstream.id).filter(Upstream.id == uid).first():
raise HTTPException(404, "upstream not found")
# 返回前强制对账,清理远端已删除的 Key
try:
website_sync.reconcile_upstream_keys_full(db, uid)
except Exception as exc:
logger.warning("list_generated_keys reconcile failed for %s: %s", uid, exc)
rows = (
db.query(UpstreamGeneratedKey)
.filter(UpstreamGeneratedKey.upstream_id == uid)
@@ -293,6 +298,12 @@ def generate_keys_by_groups(
if u.api_prefix.strip("/") != "api/v1":
raise HTTPException(400, "首版仅支持 Sub2API 上游(API Prefix 应为 /api/v1")
# 生成前先对账,清理远端已删除的旧 Key
try:
website_sync.reconcile_upstream_keys_full(db, uid)
except Exception as exc:
logger.warning("generate_keys_by_groups reconcile failed for %s: %s", uid, exc)
auth_config = json.loads(u.auth_config_json or "{}")
selected = set(body.group_ids)
prefix = body.name_prefix
+27 -1
View File
@@ -34,7 +34,7 @@ from app.schemas.website import (
)
from app.services.website_client import Sub2ApiWebsiteClient
from app.services.website_sync import binding_sources, sync_binding, build_rate_priority_map
from app.services.website_sync import binding_sources, sync_binding, build_rate_priority_map, reconcile_upstream_keys_full
from app.utils.auth import get_current_user
router = APIRouter(tags=["websites"])
@@ -483,6 +483,20 @@ def import_upstream_keys_as_accounts(
.order_by(UpstreamGeneratedKey.id)
.all()
)
# 导入前尝试对账(失败不阻塞,仅打日志—避免远端不可达时误删本地 Key)
upstream_ids = {row.upstream_id for row in rows}
for uid in upstream_ids:
try:
reconcile_upstream_keys_full(db, uid)
except Exception as exc:
logger.warning("import reconcile failed for upstream %s: %s", uid, exc)
# 重新查询(对账成功时已清理失效 Key)
rows = (
db.query(UpstreamGeneratedKey)
.filter(UpstreamGeneratedKey.id.in_(body.upstream_key_ids))
.order_by(UpstreamGeneratedKey.id)
.all()
)
found_ids = {row.id for row in rows}
missing_ids = [kid for kid in body.upstream_key_ids if kid not in found_ids]
items: list[ImportAccountItem] = [
@@ -518,6 +532,18 @@ def import_upstream_keys_as_accounts(
with _client(website) as c:
for row in rows:
# 跳过远端已不存在或导入失败的 Key(对账后标记为 orphaned / import_failed
if row.status in ("orphaned", "failed", "import_failed"):
items.append(ImportAccountItem(
upstream_key_id=row.id,
source_group_id=row.group_id,
source_group_name=row.group_name,
target_group_id=body.target_group_map.get(row.group_id),
platform=body.default_platform,
status="failed",
message="远端 Key 已不存在,请重新生成",
))
continue
# 先确定平台(失败项也需要记录)
if body.platform_mode == "auto":
platform = _detect_platform(