Flask-based VPS management panel with SSH remote command execution. Includes E2E encrypted SSH tunnel (AES-256-GCM + Go agent), setup wizard, security hardening tools, DNS management, firewall configs, monitoring, backup, and .sec patch update system. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
3.5 KiB
Python
111 lines
3.5 KiB
Python
import requests
|
|
import ssh_client
|
|
import config
|
|
|
|
BASE = "https://api.hostinger.com/api/dns/v1/zones"
|
|
|
|
def _headers():
|
|
cfg = config.load()
|
|
return {
|
|
"Authorization": f"Bearer {cfg['hostinger_api_key']}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
def _domain():
|
|
return config.load()["domain"]
|
|
|
|
def get_records():
|
|
"""Try Hostinger API first (via VPS to avoid Cloudflare), fall back to dig."""
|
|
cfg = config.load()
|
|
key = cfg.get("hostinger_api_key", "")
|
|
domain = cfg["domain"]
|
|
|
|
# Try API via VPS
|
|
try:
|
|
cmd = (
|
|
f"curl -s -w '\\nHTTP_CODE:%{{http_code}}' "
|
|
f"-H 'Authorization: Bearer {key}' "
|
|
f"-H 'Content-Type: application/json' "
|
|
f"'https://api.hostinger.com/api/dns/v1/zones/{domain}' 2>&1"
|
|
)
|
|
result = ssh_client.run(cmd, timeout=15)
|
|
output = result["stdout"]
|
|
if "HTTP_CODE:200" in output:
|
|
import json
|
|
body = output.rsplit("\nHTTP_CODE:", 1)[0]
|
|
return json.loads(body)
|
|
except Exception:
|
|
pass
|
|
|
|
# Try direct from local
|
|
try:
|
|
r = requests.get(f"{BASE}/{domain}", headers=_headers(), timeout=15)
|
|
if r.status_code == 200:
|
|
return r.json()
|
|
except Exception:
|
|
pass
|
|
|
|
# Fallback: use dig to show current DNS records
|
|
result = ssh_client.run(
|
|
f"echo '=== A Records ===' && dig +short A {domain} && "
|
|
f"echo '=== CNAME ===' && dig +short CNAME {domain} && "
|
|
f"echo '=== MX ===' && dig +short MX {domain} && "
|
|
f"echo '=== TXT ===' && dig +short TXT {domain} && "
|
|
f"echo '=== NS ===' && dig +short NS {domain} && "
|
|
f"echo '=== Subdomains ===' && "
|
|
f"for sub in repo git app files lists mail www; do "
|
|
f" ip=$(dig +short A $sub.{domain}); "
|
|
f" [ -n \"$ip\" ] && echo \"$sub.{domain} -> $ip\"; "
|
|
f"done",
|
|
timeout=15,
|
|
)
|
|
return {"source": "dig (API unavailable)", "records": result["stdout"]}
|
|
|
|
|
|
def add_a_record(name, ip, ttl=14400):
|
|
cfg = config.load()
|
|
key = cfg.get("hostinger_api_key", "")
|
|
domain = cfg["domain"]
|
|
payload = f'{{"records":[{{"name":"{name}","type":"A","content":"{ip}","ttl":{ttl}}}],"overwrite":false}}'
|
|
|
|
result = ssh_client.run(
|
|
f"curl -s -w '\\nHTTP_CODE:%{{http_code}}' -X PUT "
|
|
f"-H 'Authorization: Bearer {key}' "
|
|
f"-H 'Content-Type: application/json' "
|
|
f"-d '{payload}' "
|
|
f"'https://api.hostinger.com/api/dns/v1/zones/{domain}' 2>&1",
|
|
timeout=15,
|
|
)
|
|
return {"response": result["stdout"]}
|
|
|
|
|
|
def add_txt_record(name, value, ttl=14400):
|
|
cfg = config.load()
|
|
key = cfg.get("hostinger_api_key", "")
|
|
domain = cfg["domain"]
|
|
payload = f'{{"records":[{{"name":"{name}","type":"TXT","content":"{value}","ttl":{ttl}}}],"overwrite":false}}'
|
|
|
|
result = ssh_client.run(
|
|
f"curl -s -w '\\nHTTP_CODE:%{{http_code}}' -X PUT "
|
|
f"-H 'Authorization: Bearer {key}' "
|
|
f"-H 'Content-Type: application/json' "
|
|
f"-d '{payload}' "
|
|
f"'https://api.hostinger.com/api/dns/v1/zones/{domain}' 2>&1",
|
|
timeout=15,
|
|
)
|
|
return {"response": result["stdout"]}
|
|
|
|
|
|
def delete_record(record_id):
|
|
cfg = config.load()
|
|
key = cfg.get("hostinger_api_key", "")
|
|
domain = cfg["domain"]
|
|
|
|
result = ssh_client.run(
|
|
f"curl -s -w '\\nHTTP_CODE:%{{http_code}}' -X DELETE "
|
|
f"-H 'Authorization: Bearer {key}' "
|
|
f"'https://api.hostinger.com/api/dns/v1/zones/{domain}/records/{record_id}' 2>&1",
|
|
timeout=15,
|
|
)
|
|
return {"response": result["stdout"]}
|