feat(servers): 凭据轮询登录与连接失败列表

- 密码/SSH密钥预设增加用户名;快速添加(IP)轮询全部预设尝试 SSH 登录
- 成功入 servers 列表;失败写入 pending_servers 并支持重试/删除
- 新增 credential_poller、try_ssh_login 与 add-by-ip/pending API
This commit is contained in:
Nexus Deploy
2026-06-06 20:08:48 +08:00
parent 5a56e5a8cb
commit 4ba45abf38
51 changed files with 1372 additions and 21 deletions
+29 -4
View File
@@ -491,7 +491,10 @@ async def list_presets(admin: Admin = Depends(get_current_admin), db: AsyncSessi
"""List all password presets"""
repo = PasswordPresetRepositoryImpl(db)
presets = await repo.get_all()
return [{"id": p.id, "name": p.name, "created_at": str(p.created_at)} for p in presets]
return [
{"id": p.id, "name": p.name, "username": p.username or "root", "created_at": str(p.created_at)}
for p in presets
]
@preset_router.post("/", response_model=dict, status_code=201)
@@ -505,7 +508,11 @@ async def create_preset(
from server.infrastructure.database.crypto import encrypt_value
repo = PasswordPresetRepositoryImpl(db)
encrypted = encrypt_value(payload.encrypted_pw)
preset = PasswordPreset(name=payload.name, encrypted_pw=encrypted)
preset = PasswordPreset(
name=payload.name,
username=(payload.username or "root").strip() or "root",
encrypted_pw=encrypted,
)
created = await repo.create(preset)
audit_repo = AuditLogRepositoryImpl(db)
@@ -541,6 +548,8 @@ async def update_preset(
new_pw = updates.pop("encrypted_pw", None)
if updates.get("name"):
preset.name = updates["name"]
if updates.get("username"):
preset.username = updates["username"].strip() or "root"
if new_pw:
preset.encrypted_pw = encrypt_value(new_pw)
@@ -625,7 +634,16 @@ async def list_ssh_key_presets(admin: Admin = Depends(get_current_admin), db: As
"""List all SSH key presets"""
repo = SshKeyPresetRepositoryImpl(db)
presets = await repo.get_all()
return [{"id": p.id, "name": p.name, "public_key": p.public_key or "", "created_at": str(p.created_at)} for p in presets]
return [
{
"id": p.id,
"name": p.name,
"username": p.username or "root",
"public_key": p.public_key or "",
"created_at": str(p.created_at),
}
for p in presets
]
@ssh_key_preset_router.post("/", response_model=dict, status_code=201)
@@ -639,7 +657,12 @@ async def create_ssh_key_preset(
from server.infrastructure.database.crypto import encrypt_value
repo = SshKeyPresetRepositoryImpl(db)
encrypted_private = encrypt_value(payload.private_key)
preset = SshKeyPreset(name=payload.name, encrypted_private_key=encrypted_private, public_key=payload.public_key)
preset = SshKeyPreset(
name=payload.name,
username=(payload.username or "root").strip() or "root",
encrypted_private_key=encrypted_private,
public_key=payload.public_key,
)
created = await repo.create(preset)
audit_repo = AuditLogRepositoryImpl(db)
@@ -675,6 +698,8 @@ async def update_ssh_key_preset(
new_private_key = updates.pop("private_key", None)
if updates.get("name"):
preset.name = updates["name"]
if updates.get("username"):
preset.username = updates["username"].strip() or "root"
if new_private_key:
preset.encrypted_private_key = encrypt_value(new_private_key)
if "public_key" in updates: