#!/usr/bin/env python3 """Diagnose and fix NEXT_PUBLIC_* URLs to HTTPS on production.""" from __future__ import annotations import base64 import paramiko APP_HOST, APP_USER, APP_PASS = "192.168.10.162", "morteza", "Moli@5404" NGX_HOST, NGX_USER, NGX_PASS = "192.168.10.156", "torbatyaruser", "Moli@5404" REMOTE = "/home/morteza/torbatyar" HTTPS_KEYS = { "NEXT_PUBLIC_BACKEND_URL": "https://api.torbatyar.ir", "NEXT_PUBLIC_API_BASE_URL": "https://api.torbatyar.ir", "NEXT_PUBLIC_IDENTITY_API_URL": "https://identity.torbatyar.ir", "NEXT_PUBLIC_KEYCLOAK_URL": "https://auth.torbatyar.ir", "NEXT_PUBLIC_ACCOUNTING_API_URL": "https://accounting.torbatyar.ir", "KEYCLOAK_PUBLIC_URL": "https://auth.torbatyar.ir", "FRONTEND_CALLBACK_URL": "https://torbatyar.ir/auth/callback", } def connect(host, user, password): c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect(host, username=user, password=password, timeout=30, allow_agent=False, look_for_keys=False) return c def run(c, cmd, password=None, timeout=600): b64 = base64.b64encode(cmd.encode()).decode() if password: full = f"echo {password!r} | sudo -S -p '' bash -c 'echo {b64} | base64 -d | bash'" else: full = f"bash -lc 'echo {b64} | base64 -d | bash'" print("\n>>>", cmd[:200].replace("\n", " | ")) _, out, err = c.exec_command(full, timeout=timeout, get_pty=True) text = (out.read() + err.read()).decode("utf-8", "replace") code = out.channel.recv_exit_status() print(text[-5000:].encode("ascii", "replace").decode("ascii")) return code, text def main() -> int: # Public smoke first ngx = connect(NGX_HOST, NGX_USER, NGX_PASS) try: run( ngx, """ echo === identity https === curl -sI https://identity.torbatyar.ir/health | head -n 12 || true curl -s https://identity.torbatyar.ir/health || curl -sk https://identity.torbatyar.ir/health || echo FAIL echo echo === identity http === curl -sI http://identity.torbatyar.ir/health | head -n 12 || true """, password=NGX_PASS, timeout=60, ) finally: ngx.close() app = connect(APP_HOST, APP_USER, APP_PASS) try: keys_repr = repr(HTTPS_KEYS) code, _ = run( app, f""" set -e cd {REMOTE} echo === BEFORE === grep -E 'NEXT_PUBLIC_|KEYCLOAK_PUBLIC_URL|FRONTEND_CALLBACK' .env || true python3 - <<'PY' from pathlib import Path p = Path('.env') text = p.read_text(encoding='utf-8') if p.exists() else '' keys = {keys_repr} lines = text.splitlines() seen = set() out = [] for line in lines: if not line.strip() or line.lstrip().startswith('#') or '=' not in line: out.append(line) continue k = line.split('=', 1)[0].strip() if k in keys: out.append(f'{{k}}={{keys[k]}}') seen.add(k) else: # also rewrite accidental http://*.torbatyar.ir public URLs if k.startswith('NEXT_PUBLIC_') or k in ('KEYCLOAK_PUBLIC_URL', 'FRONTEND_CALLBACK_URL', 'CORS_ORIGINS'): v = line.split('=', 1)[1] v2 = v.replace('http://api.torbatyar.ir', 'https://api.torbatyar.ir') \\ .replace('http://identity.torbatyar.ir', 'https://identity.torbatyar.ir') \\ .replace('http://auth.torbatyar.ir', 'https://auth.torbatyar.ir') \\ .replace('http://torbatyar.ir', 'https://torbatyar.ir') \\ .replace('http://www.torbatyar.ir', 'https://www.torbatyar.ir') \\ .replace('http://accounting.torbatyar.ir', 'https://accounting.torbatyar.ir') out.append(f'{{k}}={{v2}}') seen.add(k) continue out.append(line) for k, v in keys.items(): if k not in seen: out.append(f'{{k}}={{v}}') p.write_text('\\n'.join(out).rstrip() + '\\n', encoding='utf-8') print('env_patched') PY echo === AFTER === grep -E 'NEXT_PUBLIC_|KEYCLOAK_PUBLIC_URL|FRONTEND_CALLBACK' .env || true # recreate frontend so Next picks new NEXT_PUBLIC_* at process start docker compose up -d --force-recreate frontend identity-access-service sleep 10 docker compose exec -T frontend printenv | grep -E 'NEXT_PUBLIC_IDENTITY|NEXT_PUBLIC_BACKEND|NEXT_PUBLIC_KEYCLOAK|NEXT_PUBLIC_ACCOUNTING' || true docker compose ps frontend identity-access-service curl -sf http://127.0.0.1:8001/health || echo IDENTITY_LOCAL_FAIL echo curl -s -o /dev/null -w 'accounting_page=%{{http_code}}\\n' http://127.0.0.1:3000/accounting """, password=APP_PASS, timeout=300, ) if code != 0: raise RuntimeError("fix failed") finally: app.close() print("\n=== DONE ===") return 0 if __name__ == "__main__": raise SystemExit(main())