new changes
This commit is contained in:
Binary file not shown.
@@ -1,5 +1,7 @@
|
||||
"""YakPanel - SSL/Domains API - Let's Encrypt via certbot"""
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
@@ -7,14 +9,25 @@ from pydantic import BaseModel
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.config import get_runtime_config
|
||||
from app.core.utils import exec_shell_sync
|
||||
from app.core.utils import environment_with_system_path
|
||||
from app.api.auth import get_current_user
|
||||
from app.models.user import User
|
||||
from app.models.site import Site, Domain
|
||||
from app.services.site_service import regenerate_site_vhost
|
||||
|
||||
router = APIRouter(prefix="/ssl", tags=["ssl"])
|
||||
|
||||
|
||||
def _certbot_executable() -> str:
|
||||
w = shutil.which("certbot")
|
||||
if w:
|
||||
return w
|
||||
for p in ("/usr/bin/certbot", "/usr/local/bin/certbot"):
|
||||
if os.path.isfile(p):
|
||||
return p
|
||||
return "certbot"
|
||||
|
||||
|
||||
@router.get("/domains")
|
||||
async def ssl_domains(
|
||||
current_user: User = Depends(get_current_user),
|
||||
@@ -48,8 +61,9 @@ class RequestCertRequest(BaseModel):
|
||||
async def ssl_request_cert(
|
||||
body: RequestCertRequest,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Request Let's Encrypt certificate via certbot (webroot challenge)"""
|
||||
"""Request Let's Encrypt certificate via certbot (webroot challenge)."""
|
||||
if not body.domain or not body.webroot or not body.email:
|
||||
raise HTTPException(status_code=400, detail="domain, webroot and email required")
|
||||
if ".." in body.domain or ".." in body.webroot:
|
||||
@@ -59,14 +73,62 @@ async def ssl_request_cert(
|
||||
webroot_abs = os.path.abspath(body.webroot)
|
||||
if not any(webroot_abs.startswith(a + os.sep) or webroot_abs == a for a in allowed):
|
||||
raise HTTPException(status_code=400, detail="Webroot must be under www_root or setup_path")
|
||||
cmd = (
|
||||
f'certbot certonly --webroot -w "{body.webroot}" -d "{body.domain}" '
|
||||
f'--non-interactive --agree-tos --email "{body.email}"'
|
||||
)
|
||||
out, err = exec_shell_sync(cmd, timeout=120)
|
||||
if err and "error" in err.lower() and "successfully" not in err.lower():
|
||||
raise HTTPException(status_code=500, detail=err.strip() or out.strip())
|
||||
return {"status": True, "msg": "Certificate requested", "output": out}
|
||||
|
||||
dom = body.domain.split(":")[0].strip()
|
||||
certbot_bin = _certbot_executable()
|
||||
cmd = [
|
||||
certbot_bin,
|
||||
"certonly",
|
||||
"--webroot",
|
||||
"-w",
|
||||
body.webroot,
|
||||
"-d",
|
||||
dom,
|
||||
"--non-interactive",
|
||||
"--agree-tos",
|
||||
"--email",
|
||||
body.email,
|
||||
"--preferred-challenges",
|
||||
"http",
|
||||
"--no-eff-email",
|
||||
]
|
||||
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=180,
|
||||
env=environment_with_system_path(),
|
||||
)
|
||||
except FileNotFoundError:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="certbot not found. Install it (e.g. apt install certbot) and ensure it is on PATH.",
|
||||
) from None
|
||||
except subprocess.TimeoutExpired:
|
||||
raise HTTPException(status_code=500, detail="certbot timed out (180s)") from None
|
||||
|
||||
if proc.returncode != 0:
|
||||
msg = (proc.stderr or proc.stdout or "").strip() or f"certbot exited with code {proc.returncode}"
|
||||
raise HTTPException(status_code=500, detail=msg[:8000])
|
||||
|
||||
result = await db.execute(select(Domain).where(Domain.name == dom).limit(1))
|
||||
row = result.scalar_one_or_none()
|
||||
if row:
|
||||
regen = await regenerate_site_vhost(db, row.pid)
|
||||
if not regen.get("status"):
|
||||
return {
|
||||
"status": True,
|
||||
"msg": "Certificate issued but nginx vhost update failed: " + str(regen.get("msg", "")),
|
||||
"output": (proc.stdout or "")[-2000:],
|
||||
}
|
||||
|
||||
return {
|
||||
"status": True,
|
||||
"msg": "Certificate issued and nginx updated",
|
||||
"output": (proc.stdout or "")[-2000:],
|
||||
}
|
||||
|
||||
|
||||
@router.get("/certificates")
|
||||
|
||||
Reference in New Issue
Block a user