new changes
This commit is contained in:
@@ -204,42 +204,42 @@ def _letsencrypt_paths_any(hostnames: list[str]) -> tuple[str, str] | None:
|
||||
return None
|
||||
|
||||
|
||||
def _build_ssl_server_block(
|
||||
server_names: str,
|
||||
root_path: str,
|
||||
logs_path: str,
|
||||
site_name: str,
|
||||
php_version: str,
|
||||
fullchain: str,
|
||||
privkey: str,
|
||||
redirects: list[tuple[str, str, int]] | None,
|
||||
) -> str:
|
||||
"""Second server {} for HTTPS when LE certs exist."""
|
||||
pv = php_version or "74"
|
||||
redirect_lines: list[str] = []
|
||||
for src, tgt, code in (redirects or []):
|
||||
if src and tgt:
|
||||
redirect_lines.append(f" location = {src} {{ return {code} {tgt}; }}")
|
||||
redirect_block = ("\n" + "\n".join(redirect_lines)) if redirect_lines else ""
|
||||
q_fc = fullchain.replace("\\", "\\\\").replace('"', '\\"')
|
||||
q_pk = privkey.replace("\\", "\\\\").replace('"', '\\"')
|
||||
def _build_php_deny_execute_block(enabled: int) -> str:
|
||||
if not enabled:
|
||||
return ""
|
||||
return (
|
||||
r" location ~* ^/uploads/.*\.(php|phar|phtml|php5)$ {" + "\n"
|
||||
r" deny all;" + "\n"
|
||||
r" }" + "\n"
|
||||
r" location ~* ^/storage/.*\.(php|phar|phtml|php5)$ {" + "\n"
|
||||
r" deny all;" + "\n"
|
||||
r" }" + "\n"
|
||||
)
|
||||
|
||||
|
||||
def _build_main_app_block(proxy_upstream: str, proxy_websocket: int, php_version: str) -> str:
|
||||
pu = (proxy_upstream or "").strip()
|
||||
pv = php_version or "74"
|
||||
if pu:
|
||||
ws_lines = ""
|
||||
if proxy_websocket:
|
||||
ws_lines = (
|
||||
" proxy_set_header Upgrade $http_upgrade;\n"
|
||||
' proxy_set_header Connection "upgrade";\n'
|
||||
)
|
||||
return (
|
||||
f" location / {{\n"
|
||||
f" proxy_pass {pu};\n"
|
||||
f" proxy_http_version 1.1;\n"
|
||||
f" proxy_set_header Host $host;\n"
|
||||
f" proxy_set_header X-Real-IP $remote_addr;\n"
|
||||
f" proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n"
|
||||
f" proxy_set_header X-Forwarded-Proto $scheme;\n"
|
||||
f"{ws_lines}"
|
||||
f" proxy_read_timeout 3600s;\n"
|
||||
f" }}\n"
|
||||
)
|
||||
return (
|
||||
f"server {{\n"
|
||||
f" listen 443 ssl;\n"
|
||||
f" server_name {server_names};\n"
|
||||
f' ssl_certificate "{q_fc}";\n'
|
||||
f' ssl_certificate_key "{q_pk}";\n'
|
||||
f" index index.php index.html index.htm default.php default.htm default.html;\n"
|
||||
f" root {root_path};\n"
|
||||
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" access_log off;\n"
|
||||
f" }}\n"
|
||||
f"{redirect_block}\n"
|
||||
r" location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {" + "\n"
|
||||
f" expires 30d;\n"
|
||||
f" access_log off;\n"
|
||||
@@ -253,6 +253,116 @@ def _build_ssl_server_block(
|
||||
f" fastcgi_index index.php;\n"
|
||||
f" include fastcgi.conf;\n"
|
||||
f" }}\n"
|
||||
)
|
||||
|
||||
|
||||
def _build_dir_auth_block(
|
||||
dir_path: str,
|
||||
user_file: str,
|
||||
proxy_upstream: str,
|
||||
root_path: str,
|
||||
) -> str:
|
||||
dp = (dir_path or "").strip()
|
||||
uf = (user_file or "").strip()
|
||||
if not dp or not uf or ".." in dp or ".." in uf:
|
||||
return ""
|
||||
if not dp.startswith("/"):
|
||||
dp = "/" + dp
|
||||
qf = uf.replace("\\", "\\\\").replace('"', '\\"')
|
||||
qr = root_path.replace("\\", "\\\\")
|
||||
pu = (proxy_upstream or "").strip()
|
||||
if pu:
|
||||
puc = pu.rstrip("/")
|
||||
return (
|
||||
f" location ^~ {dp} {{\n"
|
||||
f' auth_basic "YakPanel";\n'
|
||||
f' auth_basic_user_file "{qf}";\n'
|
||||
f" proxy_pass {puc};\n"
|
||||
f" proxy_http_version 1.1;\n"
|
||||
f" proxy_set_header Host $host;\n"
|
||||
f" proxy_set_header X-Real-IP $remote_addr;\n"
|
||||
f" proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n"
|
||||
f" proxy_set_header X-Forwarded-Proto $scheme;\n"
|
||||
f" }}\n"
|
||||
)
|
||||
return (
|
||||
f" location ^~ {dp} {{\n"
|
||||
f' auth_basic "YakPanel";\n'
|
||||
f' auth_basic_user_file "{qf}";\n'
|
||||
f" root {qr};\n"
|
||||
f" try_files $uri $uri/ =404;\n"
|
||||
f" }}\n"
|
||||
)
|
||||
|
||||
|
||||
def _build_location_bundle(
|
||||
root_path: str,
|
||||
redirects: list[tuple[str, str, int]] | None,
|
||||
proxy_upstream: str,
|
||||
proxy_websocket: int,
|
||||
dir_auth_path: str,
|
||||
dir_auth_user_file: str,
|
||||
php_deny_execute: int,
|
||||
php_version: str,
|
||||
) -> str:
|
||||
acme = (
|
||||
f" location ^~ /.well-known/acme-challenge/ {{\n"
|
||||
f" root {root_path};\n"
|
||||
f' default_type "text/plain";\n'
|
||||
f" allow all;\n"
|
||||
f" access_log off;\n"
|
||||
f" }}\n"
|
||||
)
|
||||
redirect_lines = []
|
||||
for src, tgt, code in redirects or []:
|
||||
if src and tgt:
|
||||
redirect_lines.append(f" location = {src} {{ return {code} {tgt}; }}")
|
||||
redirect_block = ("\n" + "\n".join(redirect_lines)) if redirect_lines else ""
|
||||
dir_auth = _build_dir_auth_block(dir_auth_path, dir_auth_user_file, proxy_upstream, root_path)
|
||||
php_deny = _build_php_deny_execute_block(php_deny_execute)
|
||||
main = _build_main_app_block(proxy_upstream, proxy_websocket, php_version)
|
||||
return acme + redirect_block + "\n" + dir_auth + php_deny + main
|
||||
|
||||
|
||||
def _build_ssl_server_block(
|
||||
server_names: str,
|
||||
root_path: str,
|
||||
logs_path: str,
|
||||
site_name: str,
|
||||
php_version: str,
|
||||
fullchain: str,
|
||||
privkey: str,
|
||||
redirects: list[tuple[str, str, int]] | None,
|
||||
proxy_upstream: str = "",
|
||||
proxy_websocket: int = 0,
|
||||
dir_auth_path: str = "",
|
||||
dir_auth_user_file: str = "",
|
||||
php_deny_execute: int = 0,
|
||||
) -> str:
|
||||
"""Second server {} for HTTPS when LE certs exist."""
|
||||
q_fc = fullchain.replace("\\", "\\\\").replace('"', '\\"')
|
||||
q_pk = privkey.replace("\\", "\\\\").replace('"', '\\"')
|
||||
bundle = _build_location_bundle(
|
||||
root_path,
|
||||
redirects,
|
||||
proxy_upstream,
|
||||
proxy_websocket,
|
||||
dir_auth_path,
|
||||
dir_auth_user_file,
|
||||
php_deny_execute,
|
||||
php_version,
|
||||
)
|
||||
return (
|
||||
f"server {{\n"
|
||||
f" listen 443 ssl;\n"
|
||||
f" server_name {server_names};\n"
|
||||
f' ssl_certificate "{q_fc}";\n'
|
||||
f' ssl_certificate_key "{q_pk}";\n'
|
||||
f" index index.php index.html index.htm default.php default.htm default.html;\n"
|
||||
f" root {root_path};\n"
|
||||
f" error_page 404 /404.html;\n"
|
||||
f" error_page 502 /502.html;\n"
|
||||
f"{bundle}"
|
||||
f" access_log {logs_path}/{site_name}.log;\n"
|
||||
f" error_log {logs_path}/{site_name}.error.log;\n"
|
||||
f"}}\n"
|
||||
@@ -269,6 +379,11 @@ def _render_vhost(
|
||||
force_https: int,
|
||||
redirects: list[tuple[str, str, int]] | None = None,
|
||||
le_hostnames: list[str] | None = None,
|
||||
proxy_upstream: str = "",
|
||||
proxy_websocket: int = 0,
|
||||
dir_auth_path: str = "",
|
||||
dir_auth_user_file: str = "",
|
||||
php_deny_execute: int = 0,
|
||||
) -> str:
|
||||
"""Render nginx vhost template. redirects: [(source, target, code), ...]"""
|
||||
if force_https:
|
||||
@@ -279,28 +394,43 @@ def _render_vhost(
|
||||
)
|
||||
else:
|
||||
force_block = ""
|
||||
redirect_lines = []
|
||||
for src, tgt, code in (redirects or []):
|
||||
if src and tgt:
|
||||
redirect_lines.append(f" location = {src} {{ return {code} {tgt}; }}")
|
||||
redirect_block = "\n".join(redirect_lines) if redirect_lines else ""
|
||||
hosts = le_hostnames if le_hostnames is not None else [p for p in server_names.split() if p]
|
||||
ssl_block = ""
|
||||
for h in hosts:
|
||||
le = _letsencrypt_paths(h)
|
||||
if le:
|
||||
fc, pk = le
|
||||
ssl_block = _build_ssl_server_block(
|
||||
server_names, root_path, logs_path, site_name, php_version, fc, pk, redirects
|
||||
)
|
||||
break
|
||||
le = _letsencrypt_paths_any(hosts)
|
||||
if le:
|
||||
fc, pk = le
|
||||
ssl_block = _build_ssl_server_block(
|
||||
server_names,
|
||||
root_path,
|
||||
logs_path,
|
||||
site_name,
|
||||
php_version,
|
||||
fc,
|
||||
pk,
|
||||
redirects,
|
||||
proxy_upstream,
|
||||
proxy_websocket,
|
||||
dir_auth_path,
|
||||
dir_auth_user_file,
|
||||
php_deny_execute,
|
||||
)
|
||||
bundle = _build_location_bundle(
|
||||
root_path,
|
||||
redirects,
|
||||
proxy_upstream,
|
||||
proxy_websocket,
|
||||
dir_auth_path,
|
||||
dir_auth_user_file,
|
||||
php_deny_execute,
|
||||
php_version,
|
||||
)
|
||||
content = template.replace("{SERVER_NAMES}", server_names)
|
||||
content = content.replace("{ROOT_PATH}", root_path)
|
||||
content = content.replace("{LOGS_PATH}", logs_path)
|
||||
content = content.replace("{SITE_NAME}", site_name)
|
||||
content = content.replace("{PHP_VERSION}", php_version or "74")
|
||||
content = content.replace("{FORCE_HTTPS_BLOCK}", force_block)
|
||||
content = content.replace("{REDIRECTS_BLOCK}", redirect_block)
|
||||
content = content.replace("{LOCATION_BUNDLE}", bundle)
|
||||
content = content.replace("{SSL_SERVER_BLOCK}", ssl_block)
|
||||
return content
|
||||
|
||||
@@ -327,6 +457,16 @@ async def domain_exists(db: AsyncSession, domains: list[str], exclude_site_id: i
|
||||
return None
|
||||
|
||||
|
||||
def _vhost_kwargs_from_site(site: Site) -> dict:
|
||||
return {
|
||||
"proxy_upstream": getattr(site, "proxy_upstream", None) or "",
|
||||
"proxy_websocket": int(getattr(site, "proxy_websocket", 0) or 0),
|
||||
"dir_auth_path": getattr(site, "dir_auth_path", None) or "",
|
||||
"dir_auth_user_file": getattr(site, "dir_auth_user_file", None) or "",
|
||||
"php_deny_execute": int(getattr(site, "php_deny_execute", 0) or 0),
|
||||
}
|
||||
|
||||
|
||||
async def create_site(
|
||||
db: AsyncSession,
|
||||
name: str,
|
||||
@@ -336,6 +476,11 @@ async def create_site(
|
||||
ps: str = "",
|
||||
php_version: str = "74",
|
||||
force_https: int = 0,
|
||||
proxy_upstream: str = "",
|
||||
proxy_websocket: int = 0,
|
||||
dir_auth_path: str = "",
|
||||
dir_auth_user_file: str = "",
|
||||
php_deny_execute: int = 0,
|
||||
) -> dict:
|
||||
"""Create a new site with vhost config."""
|
||||
if not path_safe_check(name) or not path_safe_check(path):
|
||||
@@ -359,7 +504,19 @@ async def create_site(
|
||||
if not os.path.exists(site_path):
|
||||
os.makedirs(site_path, 0o755)
|
||||
|
||||
site = Site(name=name, path=site_path, ps=ps, project_type=project_type, php_version=php_version or "74", force_https=force_https or 0)
|
||||
site = Site(
|
||||
name=name,
|
||||
path=site_path,
|
||||
ps=ps,
|
||||
project_type=project_type,
|
||||
php_version=php_version or "74",
|
||||
force_https=force_https or 0,
|
||||
proxy_upstream=(proxy_upstream or "")[:512],
|
||||
proxy_websocket=1 if proxy_websocket else 0,
|
||||
dir_auth_path=(dir_auth_path or "")[:256],
|
||||
dir_auth_user_file=(dir_auth_user_file or "")[:512],
|
||||
php_deny_execute=1 if php_deny_execute else 0,
|
||||
)
|
||||
db.add(site)
|
||||
await db.flush()
|
||||
|
||||
@@ -379,8 +536,18 @@ async def create_site(
|
||||
template = read_file(template_path) or ""
|
||||
server_names = " ".join(d.split(":")[0] for d in domains)
|
||||
le_hosts = [d.split(":")[0] for d in domains]
|
||||
vk = _vhost_kwargs_from_site(site)
|
||||
content = _render_vhost(
|
||||
template, server_names, site_path, www_logs, name, php_version or "74", force_https or 0, [], le_hosts
|
||||
template,
|
||||
server_names,
|
||||
site_path,
|
||||
www_logs,
|
||||
name,
|
||||
php_version or "74",
|
||||
force_https or 0,
|
||||
[],
|
||||
le_hosts,
|
||||
**vk,
|
||||
)
|
||||
write_file(conf_path, content)
|
||||
|
||||
@@ -477,6 +644,11 @@ async def get_site_with_domains(db: AsyncSession, site_id: int) -> dict | None:
|
||||
"project_type": site.project_type,
|
||||
"php_version": getattr(site, "php_version", None) or "74",
|
||||
"force_https": getattr(site, "force_https", 0) or 0,
|
||||
"proxy_upstream": getattr(site, "proxy_upstream", None) or "",
|
||||
"proxy_websocket": int(getattr(site, "proxy_websocket", 0) or 0),
|
||||
"dir_auth_path": getattr(site, "dir_auth_path", None) or "",
|
||||
"dir_auth_user_file": getattr(site, "dir_auth_user_file", None) or "",
|
||||
"php_deny_execute": int(getattr(site, "php_deny_execute", 0) or 0),
|
||||
"domains": domain_list,
|
||||
}
|
||||
|
||||
@@ -489,6 +661,11 @@ async def update_site(
|
||||
ps: str | None = None,
|
||||
php_version: str | None = None,
|
||||
force_https: int | None = None,
|
||||
proxy_upstream: str | None = None,
|
||||
proxy_websocket: int | None = None,
|
||||
dir_auth_path: str | None = None,
|
||||
dir_auth_user_file: str | None = None,
|
||||
php_deny_execute: int | None = None,
|
||||
) -> dict:
|
||||
"""Update site domains, path, or note."""
|
||||
result = await db.execute(select(Site).where(Site.id == site_id))
|
||||
@@ -518,11 +695,30 @@ async def update_site(
|
||||
site.php_version = php_version or "74"
|
||||
if force_https is not None:
|
||||
site.force_https = 1 if force_https else 0
|
||||
if proxy_upstream is not None:
|
||||
site.proxy_upstream = (proxy_upstream or "")[:512]
|
||||
if proxy_websocket is not None:
|
||||
site.proxy_websocket = 1 if proxy_websocket else 0
|
||||
if dir_auth_path is not None:
|
||||
site.dir_auth_path = (dir_auth_path or "")[:256]
|
||||
if dir_auth_user_file is not None:
|
||||
site.dir_auth_user_file = (dir_auth_user_file or "")[:512]
|
||||
if php_deny_execute is not None:
|
||||
site.php_deny_execute = 1 if php_deny_execute else 0
|
||||
|
||||
await db.flush()
|
||||
|
||||
# Regenerate Nginx vhost if domains, php_version, or force_https changed
|
||||
if domains is not None or php_version is not None or force_https is not None:
|
||||
regen = (
|
||||
domains is not None
|
||||
or php_version is not None
|
||||
or force_https is not None
|
||||
or proxy_upstream is not None
|
||||
or proxy_websocket is not None
|
||||
or dir_auth_path is not None
|
||||
or dir_auth_user_file is not None
|
||||
or php_deny_execute is not None
|
||||
)
|
||||
if regen:
|
||||
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")
|
||||
@@ -538,8 +734,18 @@ async def update_site(
|
||||
redir_result = await db.execute(select(SiteRedirect).where(SiteRedirect.site_id == site.id))
|
||||
redirects = [(r.source, r.target, r.code or 301) for r in redir_result.scalars().all()]
|
||||
le_hosts = [d.name for d in domain_rows]
|
||||
vk = _vhost_kwargs_from_site(site)
|
||||
content = _render_vhost(
|
||||
template, server_names, site.path, cfg["www_logs"], site.name, php_ver, fhttps, redirects, le_hosts
|
||||
template,
|
||||
server_names,
|
||||
site.path,
|
||||
cfg["www_logs"],
|
||||
site.name,
|
||||
php_ver,
|
||||
fhttps,
|
||||
redirects,
|
||||
le_hosts,
|
||||
**vk,
|
||||
)
|
||||
write_file(conf_path, content)
|
||||
reload_ok, reload_err = nginx_reload_all_known()
|
||||
@@ -623,8 +829,18 @@ async def regenerate_site_vhost(db: AsyncSession, site_id: int) -> dict:
|
||||
redir_result = await db.execute(select(SiteRedirect).where(SiteRedirect.site_id == site.id))
|
||||
redirects = [(r.source, r.target, r.code or 301) for r in redir_result.scalars().all()]
|
||||
le_hosts = [d.name for d in domain_rows]
|
||||
vk = _vhost_kwargs_from_site(site)
|
||||
content = _render_vhost(
|
||||
template, server_names, site.path, cfg["www_logs"], site.name, php_ver, fhttps, redirects, le_hosts
|
||||
template,
|
||||
server_names,
|
||||
site.path,
|
||||
cfg["www_logs"],
|
||||
site.name,
|
||||
php_ver,
|
||||
fhttps,
|
||||
redirects,
|
||||
le_hosts,
|
||||
**vk,
|
||||
)
|
||||
write_file(write_path, content)
|
||||
reload_ok, reload_err = nginx_reload_all_known()
|
||||
|
||||
Reference in New Issue
Block a user