多配置问题修复

This commit is contained in:
2026-07-20 10:57:12 +08:00
parent b5ffb45b7a
commit 2ddae8b2d6
2 changed files with 47 additions and 7 deletions
+45 -7
View File
@@ -299,6 +299,17 @@ async def create_domain(data: DomainCreate, db: AsyncSession = Depends(get_db),
dump = data.model_dump()
if not dump.get("cert_dir"):
dump["cert_dir"] = dump["domain"].lstrip("*.")
# 校验:同域名 + 同服务器 + 同 ACME 配置不允许重复
conditions = [Domain.domain == dump["domain"], Domain.server_id == dump["server_id"]]
if dump.get("acme_config_id"):
conditions.append(Domain.acme_config_id == dump["acme_config_id"])
else:
conditions.append(Domain.acme_config_id.is_(None))
existing = (await db.execute(select(Domain).where(*conditions))).scalars().first()
if existing:
raise HTTPException(409, "该域名在此服务器下已存在相同 ACME 配置的记录")
domain = Domain(**dump)
db.add(domain)
await db.flush()
@@ -312,7 +323,27 @@ async def update_domain(domain_id: int, data: DomainUpdate, db: AsyncSession = D
domain = result.scalar_one_or_none()
if not domain:
raise HTTPException(404, "Domain not found")
for key, value in data.model_dump(exclude_unset=True).items():
update_fields = data.model_dump(exclude_unset=True)
# 如果改了 server_id 或 acme_config_id,校验是否与已有记录冲突
new_server_id = update_fields.get("server_id", domain.server_id)
new_acme_config_id = update_fields.get("acme_config_id", domain.acme_config_id)
if "server_id" in update_fields or "acme_config_id" in update_fields:
conditions = [
Domain.domain == domain.domain,
Domain.server_id == new_server_id,
Domain.id != domain_id,
]
if new_acme_config_id:
conditions.append(Domain.acme_config_id == new_acme_config_id)
else:
conditions.append(Domain.acme_config_id.is_(None))
dup = (await db.execute(select(Domain).where(*conditions))).scalars().first()
if dup:
raise HTTPException(409, "该域名在此服务器下已存在相同 ACME 配置的记录")
for key, value in update_fields.items():
setattr(domain, key, value)
return {"id": domain.id}
@@ -606,7 +637,7 @@ async def list_acme_logs(status: str | None = None, db: AsyncSession = Depends(g
# ──────────────── 部署脚本 ────────────────
async def _render_script(domain: str, server_name: str, os: str, db: AsyncSession) -> tuple[str, Server, Domain]:
async def _render_script(domain: str, server_name: str, os: str, db: AsyncSession, acme_config_id: int | None = None) -> tuple[str, Server, Domain]:
"""渲染部署脚本,返回 (script, server, domain_obj)"""
from jinja2 import Environment, FileSystemLoader
@@ -618,9 +649,11 @@ async def _render_script(domain: str, server_name: str, os: str, db: AsyncSessio
if not server:
raise HTTPException(404, "Server not found")
result = await db.execute(
select(Domain).where(Domain.domain == domain, Domain.server_id == server.id)
)
# 同一域名+服务器可能有多条记录(测试/正式不同 ACME 配置),用 acme_config_id 精确匹配
conditions = [Domain.domain == domain, Domain.server_id == server.id]
if acme_config_id is not None:
conditions.append(Domain.acme_config_id == acme_config_id)
result = await db.execute(select(Domain).where(*conditions))
d = result.scalars().first()
if not d:
raise HTTPException(404, "Domain not found for this server")
@@ -644,11 +677,12 @@ async def admin_generate_script(
domain: str,
server_name: str,
os: str = "linux",
acme_config_id: int | None = None,
db: AsyncSession = Depends(get_db),
_: bool = Depends(require_auth),
):
"""管理端生成部署脚本(JSON"""
script, server, d = await _render_script(domain, server_name, os, db)
script, server, d = await _render_script(domain, server_name, os, db, acme_config_id)
return {
"script": script,
"token": server.token,
@@ -663,11 +697,12 @@ async def admin_download_script(
domain: str,
server_name: str,
os: str = "linux",
acme_config_id: int | None = None,
db: AsyncSession = Depends(get_db),
_: bool = Depends(require_auth),
):
"""下载脚本文件(纯文本,供 curl 使用)"""
script, _, _ = await _render_script(domain, server_name, os, db)
script, _, _ = await _render_script(domain, server_name, os, db, acme_config_id)
ext = "ps1" if os == "windows" else "sh"
filename = f"deploy-{domain.replace('*', '_')}.{ext}"
return PlainTextResponse(
@@ -683,6 +718,7 @@ async def admin_setup_script(
server_name: str,
os: str = "linux",
token: str = "",
acme_config_id: int | None = None,
db: AsyncSession = Depends(get_db),
_: bool = Depends(require_auth),
):
@@ -697,6 +733,8 @@ async def admin_setup_script(
base_url = settings.base_url.rstrip("/")
ext = "ps1" if os == "windows" else "sh"
download_url = f"{base_url}/admin/api/script/download?domain={quote(domain)}&server_name={quote(server_name)}&os={os}"
if acme_config_id is not None:
download_url += f"&acme_config_id={acme_config_id}"
template_name = "setup-cert.ps1.j2" if os == "windows" else "setup-cert.sh.j2"
tpl = jinja_env.get_template(template_name)