This commit is contained in:
2026-07-18 20:54:48 +08:00
parent 452be6c0a5
commit ca957385f2
9 changed files with 115 additions and 18 deletions
+40
View File
@@ -533,3 +533,43 @@ async def get_cert_info(domain_id: int, db: AsyncSession = Depends(get_db), _: b
"need_renew": need_renew,
"days_left": days_left,
}
# ──────────────── 证书下载 ────────────────
def _cert_store_dir(domain: str) -> str:
"""泛域名用裸域名作为存储目录"""
return domain[2:] if domain.startswith("*.") else domain
@router.get("/cert-download/{domain_id}/{file_type}")
async def download_cert_file(domain_id: int, file_type: str, db: AsyncSession = Depends(get_db), _: bool = Depends(require_auth)):
"""下载证书文件,file_type: fullchain 或 private"""
from fastapi.responses import FileResponse
if file_type not in ("fullchain", "private"):
raise HTTPException(400, "file_type must be fullchain or private")
result = await db.execute(select(Domain).where(Domain.id == domain_id))
domain = result.scalar_one_or_none()
if not domain:
raise HTTPException(404, "Domain not found")
settings = get_settings()
store_dir = _cert_store_dir(domain.domain)
if file_type == "fullchain":
file_path = Path(settings.cert_dir) / store_dir / "fullchain.pem"
filename = f"{store_dir}-fullchain.pem"
else:
file_path = Path(settings.cert_dir) / store_dir / "private.key"
filename = f"{store_dir}-private.key"
if not file_path.exists():
raise HTTPException(404, "文件不存在,请先申请证书")
return FileResponse(
str(file_path),
media_type="application/x-pem-file",
filename=filename,
)
+12 -7
View File
@@ -34,7 +34,7 @@ async def verify_token(
return server
@router.get("/version/{domain}")
@router.get("/version")
async def get_version(domain: str, db: AsyncSession = Depends(get_db)):
"""返回当前证书版本号(无需认证,方便客户端轻量检查)"""
result = await db.execute(select(Domain).where(Domain.domain == domain))
@@ -44,7 +44,12 @@ async def get_version(domain: str, db: AsyncSession = Depends(get_db)):
return PlainTextResponse(d.version)
@router.get("/cert/{domain}/fullchain")
def _cert_store_dir(domain: str) -> str:
"""泛域名用裸域名作为存储目录"""
return domain[2:] if domain.startswith("*.") else domain
@router.get("/cert/fullchain")
async def get_fullchain(
domain: str,
server: Server = Depends(verify_token),
@@ -57,13 +62,13 @@ async def get_fullchain(
d = result.scalar_one_or_none()
if not d:
raise HTTPException(status_code=404, detail="Domain not found for this server")
cert_path = Path(settings.cert_dir) / domain / "fullchain.pem"
cert_path = Path(settings.cert_dir) / _cert_store_dir(domain) / "fullchain.pem"
if not cert_path.exists():
raise HTTPException(status_code=404, detail="Certificate file not found")
return FileResponse(str(cert_path), media_type="application/x-pem-file", filename="fullchain.pem")
@router.get("/cert/{domain}/private")
@router.get("/cert/private")
async def get_private_key(
domain: str,
server: Server = Depends(verify_token),
@@ -76,13 +81,13 @@ async def get_private_key(
d = result.scalar_one_or_none()
if not d:
raise HTTPException(status_code=404, detail="Domain not found for this server")
key_path = Path(settings.cert_dir) / domain / "private.key"
key_path = Path(settings.cert_dir) / _cert_store_dir(domain) / "private.key"
if not key_path.exists():
raise HTTPException(status_code=404, detail="Private key file not found")
return FileResponse(str(key_path), media_type="application/x-pem-file", filename="private.key")
@router.get("/script/{domain}")
@router.get("/script")
async def generate_script(
domain: str,
server_name: str,
@@ -123,7 +128,7 @@ async def generate_script(
return PlainTextResponse(script, media_type=media_type)
@router.post("/report/{domain}")
@router.post("/report")
async def report_deploy(
domain: str,
status: str,