new changes
This commit is contained in:
Binary file not shown.
@@ -13,6 +13,75 @@ from app.core.utils import path_safe_check, write_file, read_file, exec_shell_sy
|
||||
|
||||
DOMAIN_REGEX = re.compile(r"^([\w\-\*]{1,100}\.){1,8}([\w\-]{1,24}|[\w\-]{1,24}\.[\w\-]{1,24})$")
|
||||
|
||||
LETSENCRYPT_LIVE = "/etc/letsencrypt/live"
|
||||
SSL_EXPIRING_DAYS = 14
|
||||
|
||||
|
||||
def _backup_count(site_name: str, backup_dir: str) -> int:
|
||||
if not backup_dir or not os.path.isdir(backup_dir):
|
||||
return 0
|
||||
prefix = f"{site_name}_"
|
||||
n = 0
|
||||
try:
|
||||
for f in os.listdir(backup_dir):
|
||||
if f.startswith(prefix) and f.endswith(".tar.gz"):
|
||||
n += 1
|
||||
except OSError:
|
||||
return 0
|
||||
return n
|
||||
|
||||
|
||||
def _parse_cert_not_after(cert_path: str) -> datetime | None:
|
||||
if not os.path.isfile(cert_path):
|
||||
return None
|
||||
out, _err = exec_shell_sync(f'openssl x509 -in "{cert_path}" -noout -enddate', timeout=5)
|
||||
if not out or "notAfter=" not in out:
|
||||
return None
|
||||
val = out.strip().split("=", 1)[1].strip()
|
||||
try:
|
||||
dt = datetime.strptime(val, "%b %d %H:%M:%S %Y GMT")
|
||||
return dt.replace(tzinfo=timezone.utc)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def _best_ssl_for_hostnames(hostnames: list[str]) -> dict:
|
||||
"""Match LE certs by live/<domain>/fullchain.pem; pick longest validity."""
|
||||
none = {"status": "none", "days_left": None, "cert_name": None}
|
||||
live_root = LETSENCRYPT_LIVE
|
||||
try:
|
||||
if not os.path.isdir(live_root):
|
||||
return none
|
||||
best_days: int | None = None
|
||||
best_name: str | None = None
|
||||
for host in hostnames:
|
||||
h = (host or "").split(":")[0].strip().lower()
|
||||
if not h:
|
||||
continue
|
||||
folder = os.path.join(live_root, h)
|
||||
if not os.path.isdir(folder):
|
||||
continue
|
||||
fullchain = os.path.join(folder, "fullchain.pem")
|
||||
end = _parse_cert_not_after(fullchain)
|
||||
if end is None:
|
||||
continue
|
||||
now = datetime.now(timezone.utc)
|
||||
days = int((end - now).total_seconds() // 86400)
|
||||
if best_days is None or days > best_days:
|
||||
best_days = days
|
||||
best_name = h
|
||||
if best_days is None:
|
||||
return none
|
||||
if best_days < 0:
|
||||
status = "expired"
|
||||
elif best_days <= SSL_EXPIRING_DAYS:
|
||||
status = "expiring"
|
||||
else:
|
||||
status = "active"
|
||||
return {"status": status, "days_left": best_days, "cert_name": best_name}
|
||||
except OSError:
|
||||
return none
|
||||
|
||||
|
||||
def _render_vhost(
|
||||
template: str,
|
||||
|
||||
Reference in New Issue
Block a user