feat: Umami/ntfy 可观测性栈与 WordPress AdSense 部署
This commit is contained in:
163
deploy/wordpress/remote_provision.py
Normal file
163
deploy/wordpress/remote_provision.py
Normal file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Upload WordPress deploy bundle and provision on the Nomadro server."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import secrets
|
||||
import string
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import paramiko
|
||||
|
||||
HOST = "107.173.30.245"
|
||||
USER = "root"
|
||||
PASSWORD = os.environ.get("NOMADRO_SSH_PASS", "")
|
||||
REMOTE_DIR = "/var/www/nomadro-wp"
|
||||
LOCAL_DIR = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def gen_pass(n: int = 24) -> str:
|
||||
alphabet = string.ascii_letters + string.digits
|
||||
return "".join(secrets.choice(alphabet) for _ in range(n))
|
||||
|
||||
|
||||
def safe_print(text: str) -> None:
|
||||
try:
|
||||
print(text)
|
||||
except UnicodeEncodeError:
|
||||
print(text.encode("utf-8", errors="replace").decode("utf-8", errors="replace"))
|
||||
|
||||
|
||||
def run(ssh: paramiko.SSHClient, cmd: str, timeout: int = 600) -> str:
|
||||
print(f"\n$ {cmd}")
|
||||
stdin, stdout, stderr = ssh.exec_command(cmd, timeout=timeout, get_pty=True)
|
||||
out = stdout.read().decode(errors="replace")
|
||||
err = stderr.read().decode(errors="replace")
|
||||
code = stdout.channel.recv_exit_status()
|
||||
if out:
|
||||
safe_print(out[-4000:])
|
||||
if err.strip():
|
||||
safe_print(err[-2000:])
|
||||
if code != 0:
|
||||
raise RuntimeError(f"Command failed ({code}): {cmd}")
|
||||
return out
|
||||
|
||||
|
||||
def sftp_mkdirs(sftp: paramiko.SFTPClient, remote: str) -> None:
|
||||
parts = remote.strip("/").split("/")
|
||||
cur = ""
|
||||
for part in parts:
|
||||
cur += "/" + part
|
||||
try:
|
||||
sftp.stat(cur)
|
||||
except FileNotFoundError:
|
||||
sftp.mkdir(cur)
|
||||
|
||||
|
||||
def upload_tree(sftp: paramiko.SFTPClient, local: Path, remote: str) -> None:
|
||||
sftp_mkdirs(sftp, remote)
|
||||
for path in local.rglob("*"):
|
||||
if path.name.startswith(".env") or path.name.endswith(".pyc"):
|
||||
continue
|
||||
if path.name == "remote_provision.py":
|
||||
continue
|
||||
rel = path.relative_to(local).as_posix()
|
||||
target = f"{remote}/{rel}"
|
||||
if path.is_dir():
|
||||
sftp_mkdirs(sftp, target)
|
||||
else:
|
||||
sftp_mkdirs(sftp, str(Path(target).parent).replace("\\", "/"))
|
||||
print(f"put {rel}")
|
||||
sftp.put(str(path), target)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not PASSWORD:
|
||||
print("Set NOMADRO_SSH_PASS", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
mysql_pass = gen_pass()
|
||||
mysql_root = gen_pass()
|
||||
wp_admin = "Xiao4669805@"
|
||||
wp_user = "hackrobot"
|
||||
wp_email = "admin@nomadro.com"
|
||||
|
||||
env_text = (
|
||||
f"MYSQL_PASSWORD={mysql_pass}\n"
|
||||
f"MYSQL_ROOT_PASSWORD={mysql_root}\n"
|
||||
f"WP_ADMIN_USER={wp_user}\n"
|
||||
f"WP_ADMIN_PASSWORD={wp_admin}\n"
|
||||
f"WP_ADMIN_EMAIL={wp_email}\n"
|
||||
)
|
||||
|
||||
secrets_path = LOCAL_DIR / ".env.generated"
|
||||
secrets_path.write_text(env_text, encoding="utf-8")
|
||||
print(f"Wrote local secrets to {secrets_path} (do not commit)")
|
||||
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(
|
||||
HOST,
|
||||
username=USER,
|
||||
password=PASSWORD,
|
||||
timeout=30,
|
||||
allow_agent=False,
|
||||
look_for_keys=False,
|
||||
)
|
||||
sftp = ssh.open_sftp()
|
||||
|
||||
print("==> Upload wordpress bundle")
|
||||
upload_tree(sftp, LOCAL_DIR, REMOTE_DIR)
|
||||
with sftp.file(f"{REMOTE_DIR}/.env", "w") as f:
|
||||
f.write(env_text)
|
||||
sftp.chmod(f"{REMOTE_DIR}/install-docker.sh", 0o755)
|
||||
sftp.chmod(f"{REMOTE_DIR}/bootstrap-wp.sh", 0o755)
|
||||
sftp.close()
|
||||
|
||||
run(ssh, f"bash {REMOTE_DIR}/install-docker.sh", timeout=900)
|
||||
run(ssh, f"cd {REMOTE_DIR} && docker compose pull", timeout=900)
|
||||
run(ssh, f"cd {REMOTE_DIR} && docker compose up -d", timeout=300)
|
||||
|
||||
print("==> Wait for WP HTTP")
|
||||
for _ in range(60):
|
||||
stdin, stdout, stderr = ssh.exec_command(
|
||||
"curl -fsS -o /dev/null -w '%{http_code}' http://127.0.0.1:8088 || true"
|
||||
)
|
||||
code = stdout.read().decode().strip()
|
||||
if code.startswith("2") or code.startswith("3"):
|
||||
print("WP HTTP", code)
|
||||
break
|
||||
time.sleep(3)
|
||||
else:
|
||||
raise RuntimeError("WordPress did not become ready on :8088")
|
||||
|
||||
# Preserve extra caddy site imports already on server
|
||||
run(
|
||||
ssh,
|
||||
f"cp {REMOTE_DIR}/Caddyfile.adsense /etc/caddy/Caddyfile && "
|
||||
"caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy",
|
||||
)
|
||||
|
||||
run(ssh, f"cd {REMOTE_DIR} && bash bootstrap-wp.sh", timeout=900)
|
||||
|
||||
# Quick public check via local caddy
|
||||
run(ssh, "curl -fsSI https://nomadro.com | head -20 || true")
|
||||
run(
|
||||
ssh,
|
||||
"curl -fsS https://nomadro.com | tr '\\n' ' ' | grep -o '<title>[^<]*</title>' | head -1 || true",
|
||||
)
|
||||
|
||||
print("\n=== CREDENTIALS (save now) ===")
|
||||
print(f"WP admin user: {wp_user}")
|
||||
print(f"WP admin pass: {wp_admin}")
|
||||
print(f"WP admin URL: https://nomadro.com/wp-admin/")
|
||||
print(f"Next.js portal: https://portal.nomadro.com (add DNS A -> {HOST})")
|
||||
ssh.close()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user