new changes
This commit is contained in:
Binary file not shown.
@@ -14,6 +14,7 @@ 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.core.utils import exec_shell_sync
|
||||
from app.services.site_service import regenerate_site_vhost
|
||||
|
||||
router = APIRouter(prefix="/ssl", tags=["ssl"])
|
||||
@@ -97,6 +98,27 @@ def _certbot_missing_message() -> str:
|
||||
)
|
||||
|
||||
|
||||
def _reload_panel_and_common_nginx() -> None:
|
||||
"""Reload nginx so new vhost (ACME path) is live before certbot HTTP-01."""
|
||||
cfg = get_runtime_config()
|
||||
seen: set[str] = set()
|
||||
binaries: list[str] = []
|
||||
panel_ngx = os.path.join(cfg.get("setup_path") or "", "nginx", "sbin", "nginx")
|
||||
if os.path.isfile(panel_ngx):
|
||||
binaries.append(panel_ngx)
|
||||
seen.add(os.path.realpath(panel_ngx))
|
||||
for alt in ("/usr/sbin/nginx", "/usr/bin/nginx", "/usr/local/nginx/sbin/nginx"):
|
||||
if not os.path.isfile(alt):
|
||||
continue
|
||||
rp = os.path.realpath(alt)
|
||||
if rp in seen:
|
||||
continue
|
||||
binaries.append(alt)
|
||||
seen.add(rp)
|
||||
for ngx in binaries:
|
||||
exec_shell_sync(f'"{ngx}" -t && "{ngx}" -s reload', timeout=60)
|
||||
|
||||
|
||||
@router.get("/domains")
|
||||
async def ssl_domains(
|
||||
current_user: User = Depends(get_current_user),
|
||||
@@ -144,6 +166,25 @@ async def ssl_request_cert(
|
||||
raise HTTPException(status_code=400, detail="Webroot must be under www_root or setup_path")
|
||||
|
||||
dom = body.domain.split(":")[0].strip()
|
||||
webroot_norm = webroot_abs.rstrip(os.sep)
|
||||
|
||||
result_dom = await db.execute(select(Domain).where(Domain.name == dom).limit(1))
|
||||
dom_row = result_dom.scalar_one_or_none()
|
||||
if dom_row:
|
||||
regen_pre = await regenerate_site_vhost(db, dom_row.pid)
|
||||
if not regen_pre.get("status"):
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Cannot refresh nginx vhost before certificate request: " + str(regen_pre.get("msg", "")),
|
||||
)
|
||||
_reload_panel_and_common_nginx()
|
||||
|
||||
challenge_dir = os.path.join(webroot_norm, ".well-known", "acme-challenge")
|
||||
try:
|
||||
os.makedirs(challenge_dir, mode=0o755, exist_ok=True)
|
||||
except OSError as e:
|
||||
raise HTTPException(status_code=500, detail=f"Cannot create ACME webroot directory: {e}") from e
|
||||
|
||||
prefix = _certbot_command()
|
||||
if not prefix:
|
||||
raise HTTPException(status_code=500, detail=_certbot_missing_message())
|
||||
@@ -152,7 +193,7 @@ async def ssl_request_cert(
|
||||
"certonly",
|
||||
"--webroot",
|
||||
"-w",
|
||||
body.webroot,
|
||||
webroot_norm,
|
||||
"-d",
|
||||
dom,
|
||||
"--non-interactive",
|
||||
@@ -180,10 +221,13 @@ async def ssl_request_cert(
|
||||
|
||||
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])
|
||||
hint = (
|
||||
" Check: DNS A/AAAA for this domain points to this server; port 80 is reachable; "
|
||||
"the website is enabled in YakPanel; nginx on port 80 loads this site’s vhost (same server as panel nginx if used)."
|
||||
)
|
||||
raise HTTPException(status_code=500, detail=(msg + hint)[:8000])
|
||||
|
||||
result = await db.execute(select(Domain).where(Domain.name == dom).limit(1))
|
||||
row = result.scalar_one_or_none()
|
||||
row = dom_row
|
||||
if row:
|
||||
regen = await regenerate_site_vhost(db, row.pid)
|
||||
if not regen.get("status"):
|
||||
|
||||
Binary file not shown.
@@ -126,9 +126,10 @@ def _build_ssl_server_block(
|
||||
f" error_page 404 /404.html;\n"
|
||||
f" error_page 502 /502.html;\n"
|
||||
f" location ^~ /.well-known/acme-challenge/ {{\n"
|
||||
f" root {root_path};\n"
|
||||
f' default_type "text/plain";\n'
|
||||
f" allow all;\n"
|
||||
f" try_files $uri =404;\n"
|
||||
f" access_log off;\n"
|
||||
f" }}\n"
|
||||
f"{redirect_block}\n"
|
||||
r" location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {" + "\n"
|
||||
@@ -480,16 +481,17 @@ async def set_site_status(db: AsyncSession, site_id: int, status: int) -> dict:
|
||||
|
||||
|
||||
async def regenerate_site_vhost(db: AsyncSession, site_id: int) -> dict:
|
||||
"""Regenerate nginx vhost for a site (e.g. after redirect changes)."""
|
||||
"""Regenerate nginx vhost for a site (e.g. after redirect changes or before LE validation)."""
|
||||
result = await db.execute(select(Site).where(Site.id == site_id))
|
||||
site = result.scalar_one_or_none()
|
||||
if not site:
|
||||
return {"status": False, "msg": "Site not found"}
|
||||
cfg = get_runtime_config()
|
||||
vhost_path = os.path.join(cfg["setup_path"], "panel", "vhost", "nginx")
|
||||
conf_path = os.path.join(vhost_path, f"{site.name}.conf")
|
||||
if site.status != 1:
|
||||
return {"status": True, "msg": "Site disabled, vhost not active"}
|
||||
conf_path, disabled_path = _vhost_path(site.name)
|
||||
if site.status == 1:
|
||||
write_path = conf_path
|
||||
else:
|
||||
write_path = disabled_path if os.path.isfile(disabled_path) else conf_path
|
||||
panel_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
template_path = os.path.join(panel_root, "webserver", "templates", "nginx_site.conf")
|
||||
if not os.path.exists(template_path):
|
||||
@@ -507,7 +509,7 @@ async def regenerate_site_vhost(db: AsyncSession, site_id: int) -> dict:
|
||||
content = _render_vhost(
|
||||
template, server_names, site.path, cfg["www_logs"], site.name, php_ver, fhttps, redirects, le_hosts
|
||||
)
|
||||
write_file(conf_path, content)
|
||||
write_file(write_path, content)
|
||||
nginx_bin = os.path.join(cfg["setup_path"], "nginx", "sbin", "nginx")
|
||||
if os.path.exists(nginx_bin):
|
||||
exec_shell_sync(f"{nginx_bin} -t && {nginx_bin} -s reload")
|
||||
|
||||
@@ -8,11 +8,12 @@ server {
|
||||
error_page 404 /404.html;
|
||||
error_page 502 /502.html;
|
||||
|
||||
# ACME HTTP-01 (Let's Encrypt). Prefix match wins over regex locations.
|
||||
# ACME HTTP-01 (Let's Encrypt). Prefix match beats regex; explicit root; no try_files so server error_page cannot mask failures.
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
root {ROOT_PATH};
|
||||
default_type "text/plain";
|
||||
allow all;
|
||||
try_files $uri =404;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Force HTTPS (skipped for ACME — see if block)
|
||||
|
||||
Reference in New Issue
Block a user