v2: ID verification by state, 15 photos/5 albums, 20 quick replies, logo, mobile polish

This commit is contained in:
Ryan Salazar
2026-06-26 21:08:57 -04:00
parent c6238c3bcf
commit e03d929977
121 changed files with 6380 additions and 153 deletions

View File

@@ -75,6 +75,18 @@ exposedgays/
└── package.json
```
## Auth (Supabase)
Auth is wired via `src/lib/auth/context.tsx` + `AuthModal.tsx`:
- **Join Free** in nav → email/password signup or login
- **Continue Anonymously** → `signInAnonymously()` (enable in Supabase Dashboard → Auth → Providers → Anonymous)
- Profile page syncs to `profiles` table when signed in
Enable on https://supabase.onsethost.com → Authentication → Providers:
- Email (confirm email optional for dev)
- Anonymous sign-ins
## Supabase Setup
### 1. Create project

53
deploy/_cf_api_probe.py Normal file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
paths = [
f"/accounts/{ACCOUNT}/cfd_tunnel",
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}",
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations",
f"/accounts/{ACCOUNT}/tunnels",
f"/accounts/{ACCOUNT}/tunnels/{TUNNEL}",
f"/accounts/{ACCOUNT}/tunnels/{TUNNEL}/configurations",
f"/accounts/{ACCOUNT}/access/apps",
f"/accounts/{ACCOUNT}/access/apps/policies",
f"/zones/b9e032992b315331c8b0571473fedc89/dns_records?name=exposedgays.com",
f"/accounts/{ACCOUNT}/teamnet/routes",
f"/accounts/{ACCOUNT}/teamnet/hostnames",
f"/accounts/{ACCOUNT}/teamnet/tunnels",
f"/accounts/{ACCOUNT}/teamnet/tunnels/{TUNNEL}/routes",
]
for path in paths:
req = urllib.request.Request(CF + path, headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=25) as r:
data = json.load(r)
result = data.get("result")
summary = ""
if isinstance(result, list):
summary = f"list[{len(result)}]"
if result and isinstance(result[0], dict):
summary += " keys=" + ",".join(list(result[0].keys())[:6])
elif isinstance(result, dict):
summary = "dict keys=" + ",".join(list(result.keys())[:8])
print(f"OK {path} -> {summary}")
except urllib.error.HTTPError as e:
body = e.read().decode()[:120]
print(f"ERR {e.code} {path} -> {body}")

38
deploy/_cf_dns_both.py Normal file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/env python3
import json
import re
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
for domain in ["exposedgays.com", "thepinkpulse.com"]:
req = urllib.request.Request(
f"{CF}/zones?name={domain}",
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=30) as r:
zones = json.load(r)["result"]
if not zones:
print(domain, "NO ZONE")
continue
zid = zones[0]["id"]
req2 = urllib.request.Request(
f"{CF}/zones/{zid}/dns_records?per_page=100",
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req2, timeout=30) as r:
recs = json.load(r)["result"]
print(f"\n=== {domain} zone {zid} ===")
for rec in recs:
if rec["type"] in ("A", "CNAME", "AAAA") and "domainconnect" not in rec["name"]:
print(f" {rec['type']:5} {rec['name']:35} -> {rec['content']} proxied={rec.get('proxied')}")

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
for host, user in [("10.10.0.11", "root"), ("10.10.0.10", "localadministrator")]:
print(f"\n######## {host} ########")
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
c.connect(host, username=user, password=PASS, timeout=20)
except Exception as ex:
print("connect failed", ex)
continue
cmds = [
"ss -tlnp | grep -E ':80 |:443 ' || true",
"curl -sSI --max-time 10 -H 'Host: thepinkpulse.com' http://127.0.0.1/ | head -6",
"curl -sSI --max-time 10 -H 'Host: exposedgays.com' http://127.0.0.1/ | head -6",
"curl -sSI --max-time 10 -H 'Host: avbeat.com' http://127.0.0.1/ | head -6",
"curl -sSI --max-time 10 -H 'Host: exposedgays.onsethost.com' http://127.0.0.1/ | head -6",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:1500])
c.close()

44
deploy/_check_www_dns.py Normal file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python3
import json
import re
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ZID = "b9e032992b315331c8b0571473fedc89"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
req = urllib.request.Request(
CF + f"/zones/{ZID}/dns_records?per_page=100",
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=30) as r:
recs = json.load(r)["result"]
print("All exposedgays apex/www records:")
for rec in recs:
if rec["name"] in ("exposedgays.com", "www.exposedgays.com"):
print(f" id={rec['id']} {rec['type']} {rec['name']} -> {rec['content']} proxied={rec.get('proxied')}")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for cmd in [
"dig @1.1.1.1 www.exposedgays.com A +short",
"dig @1.1.1.1 www.exposedgays.com CNAME +short",
"dig @annabel.ns.cloudflare.com www.exposedgays.com A +short",
"curl -sSI --max-time 15 https://www.exposedgays.com/ | head -6",
]:
print(f"\n$ {cmd}")
_, o, e = ssh.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode())
ssh.close()

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = c.exec_command("cloudflared tunnel --help 2>&1", timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode())
c.close()

24
deploy/_compare_avbeat.py Normal file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"dig @1.1.1.1 avbeat.com CNAME +short",
"dig @1.1.1.1 exposedgays.com CNAME +short",
"dig @1.1.1.1 avbeat.com A +short",
"dig @1.1.1.1 exposedgays.com A +short",
"curl -sSI --max-time 15 https://avbeat.com/ | head -8",
"curl -sSI --max-time 15 https://exposedgays.com/ | head -8",
"curl -sSI --max-time 15 -k -H 'Host: avbeat.com' https://127.0.0.1/ | head -8",
"curl -sSI --max-time 15 -k -H 'Host: exposedgays.com' https://127.0.0.1/ | head -8",
"ps aux | grep cloudflared | grep -v grep",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=40)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:2000])
c.close()

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
f"echo '{PASS}' | sudo -S grep -rniE 'cfut_|GLOBAL_API_KEY|tunnel.*token|CLOUDFLARE' /data/coolify /home/localadministrator/.coolify-token /home/localadministrator/.config 2>/dev/null | head -40",
"cat /home/localadministrator/.coolify-token 2>/dev/null | wc -c",
"docker exec coolify-db psql -U coolify -d coolify -c \"SELECT key,left(value,20) FROM environment_variables WHERE key ILIKE '%cloudflare%' OR key ILIKE '%tunnel%' OR value LIKE 'cfut_%' LIMIT 20;\"",
"sudo cat /etc/cloudflared/config.yml | head -40",
]
for cmd in cmds:
print(f"\n=== {cmd[:110]} ===")
_, o, e = c.exec_command(cmd, timeout=120)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:5000])
c.close()

47
deploy/_diag_now.py Normal file
View File

@@ -0,0 +1,47 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
f"echo '{PASS}' | sudo -S grep -rho 'cfut_[A-Za-z0-9_-]{{20,}}' /etc/infra /etc/cloudflared /root /home/localadministrator 2>/dev/null | sort -u",
f"echo '{PASS}' | sudo -S grep -rniE 'GLOBAL_API_KEY|X-Auth-Key|CLOUDFLARE_EMAIL' /etc/infra /etc/cloudflared /root /home/localadministrator 2>/dev/null | head -30",
"sudo cat /etc/cloudflared/config.yml",
"sudo journalctl -u cloudflared --no-pager -n 3 | grep 'Updated to new configuration' | tail -1 | python3 -c \"import sys,re; t=sys.stdin.read(); print('ingress_count', len(re.findall(r'hostname', t))); print('exposedgays', 'exposedgays' in t); print('thepinkpulse', 'thepinkpulse' in t)\"",
]
for cmd in cmds:
print(f"\n=== {cmd[:100]} ===")
_, o, e = c.exec_command(cmd, timeout=120)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:15000])
c.close()
tokens = set()
for label, tok in [
("zlUt", "cfut_zlUt5lAKVsu7qIcRCHJnyhAvpJVF5jx24HlrIEn9a8fcd6a1"),
("IOm1", "cfut_IOm1DJW4pDkPCuQA6UoOjqO6Tcv2zU10y2PSw82m379f7db3f7"),
("vF51", "cfut_vF51avCWJV9EnCM4lXS2v60jXo6TPQZ0yxESHuRn5b0f3515"),
]:
req = urllib.request.Request(
f"{CF}/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations",
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
data = json.load(r)
ingress = data.get("result", {}).get("config", {}).get("ingress", [])
eg = [i.get("hostname") for i in ingress if i.get("hostname") and "exposedgays" in i.get("hostname", "")]
print(f"\n[{label}] tunnel GET OK count={len(ingress)} exposedgays={eg}")
except urllib.error.HTTPError as e:
err = json.loads(e.read().decode())
print(f"\n[{label}] tunnel GET {e.code} {err.get('errors')}")

23
deploy/_dns_compare.py Normal file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"dig @1.1.1.1 thepinkpulse.com ANY +noall +answer",
"dig @1.1.1.1 exposedgays.com ANY +noall +answer",
"dig @1.1.1.1 www.thepinkpulse.com CNAME +short",
"dig @1.1.1.1 www.exposedgays.com CNAME +short",
"curl -sSL --max-time 15 https://thepinkpulse.com/ | head -c 150",
"curl -sSL --max-time 15 https://exposedgays.com/ 2>&1 | head -c 150",
"curl -sSL --max-time 15 -k -H 'Host: thepinkpulse.com' https://127.0.0.1/ | head -c 150",
"curl -sSL --max-time 15 -k -H 'Host: exposedgays.com' https://127.0.0.1/ | head -c 150",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=40)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:2000])
c.close()

35
deploy/_dump_remote.py Normal file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env python3
import json
import re
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = c.exec_command(
"sudo journalctl -u cloudflared --no-pager -n 200 | grep 'Updated to new configuration' | tail -1",
timeout=30,
)
o.channel.recv_exit_status()
line = (o.read() + e.read()).decode()
start = line.find('config="')
if start == -1:
print("missing config=")
print(line[:500])
c.close()
raise SystemExit(1)
raw = line[start + len('config="'):]
# config value ends before final quote on line
raw = raw.rsplit('"', 1)[0]
raw = raw.encode().decode("unicode_escape")
cfg = json.loads(raw)
ingress = cfg.get("ingress", [])
print(f"total rules: {len(ingress)}")
for i in ingress:
h = i.get("hostname", "(catch-all)")
svc = i.get("service")
print(f" {h} -> {svc}")
if h and "exposed" in h:
print("FOUND")
c.close()

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
f"echo '{PASS}' | sudo -S find /etc/cloudflared /home/localadministrator/.cloudflared /root -name 'cert.pem' 2>/dev/null",
"cloudflared tunnel --help 2>&1 | grep -i configuration",
"cloudflared tunnel configuration --help 2>&1",
"curl -s http://127.0.0.1:20241/metrics 2>/dev/null | grep -i ingress | head -10",
"curl -s http://127.0.0.1:20242/metrics 2>/dev/null | head -5",
"sudo journalctl -u cloudflared --no-pager -n 30 | grep -iE 'metrics|ingress|exposedgays|configuration'",
]
for cmd in cmds:
print(f"\n=== {cmd[:100]} ===")
_, o, e = c.exec_command(cmd, timeout=40)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:4000])
c.close()

23
deploy/_find_creds.py Normal file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"ls -la /etc/cloudflared/",
"ls -la /home/localadministrator/.cloudflared/ 2>/dev/null || echo no_home_cloudflared",
"ls -la /root/.cloudflared/ 2>/dev/null || echo no_root_cloudflared",
"grep -n 'exposedgays\\|thepinkpulse\\|avbeat\\|catch' /etc/cloudflared/config.yml | tail -30",
"sudo journalctl -u cloudflared --no-pager -n 2 | tail -2",
"docker exec coolify-db psql -U coolify -d coolify -t -A -c \"SELECT uuid,name,status,fqdn FROM applications WHERE name='exposedgays';\"",
"docker ps --format '{{.Names}} {{.Status}}' | grep ira4 || true",
"curl -sS -o /dev/null -w 'local:%{http_code}' -k -H 'Host: exposedgays.com' https://127.0.0.1/",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=60)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:4000])
c.close()

131
deploy/_fix_tunnel_put.py Normal file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env python3
"""Add exposedgays.com to Cloudflare tunnel remote ingress using best available token."""
import json
import re
import time
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
EG_RULES = [
{
"hostname": "exposedgays.com",
"service": "http://10.10.0.11:80",
"originRequest": {"httpHostHeader": "exposedgays.com"},
},
{
"hostname": "www.exposedgays.com",
"service": "http://10.10.0.11:80",
"originRequest": {"httpHostHeader": "www.exposedgays.com"},
},
]
def get_pfsense_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def cf(token, path, method="GET", data=None):
req = urllib.request.Request(
f"{CF}{path}",
data=json.dumps(data).encode() if data is not None else None,
method=method,
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=60) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read().decode())
def ssh(cmd, timeout=60):
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = c.exec_command(cmd, timeout=timeout)
o.channel.recv_exit_status()
out = (o.read() + e.read()).decode(errors="replace")
c.close()
return out
def get_remote_ingress_from_journal():
raw = ssh(
"sudo journalctl -u cloudflared --no-pager -n 200 | grep 'Updated to new configuration' | tail -1"
)
start = raw.find('config="')
if start == -1:
raise RuntimeError("no remote config in journal")
payload = raw[start + len('config="'):].rsplit('"', 1)[0]
payload = payload.encode().decode("unicode_escape")
return json.loads(payload).get("ingress", [])
def main():
token = get_pfsense_token()
code, verify = cf(token, "/user/tokens/verify")
print(f"token verify: {code} {verify.get('success')}")
code, body = cf(token, f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations")
print(f"API GET config: {code} {body.get('errors')}")
if body.get("success"):
ingress = body["result"]["config"]["ingress"]
source = "api"
else:
ingress = get_remote_ingress_from_journal()
source = "journal"
print(f"ingress source={source} rules={len(ingress)}")
hostnames = {i.get("hostname") for i in ingress if i.get("hostname")}
changed = False
for rule in EG_RULES:
if rule["hostname"] not in hostnames:
catch = next((i for i, x in enumerate(ingress) if not x.get("hostname")), len(ingress))
ingress.insert(catch, rule)
hostnames.add(rule["hostname"])
changed = True
print("will add", rule["hostname"])
if not changed:
print("already present in ingress list")
elif source != "api":
print("BLOCKED: cannot PUT without API token — ingress list prepared but not pushed")
print("Prepared rules:")
for rule in EG_RULES:
print(" ", rule)
return 1
put_code, put = cf(
token,
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations",
"PUT",
{"config": {"ingress": ingress, "warp-routing": {"enabled": False}}},
)
print(f"PUT: {put_code} success={put.get('success')} errors={put.get('errors')}")
if not put.get("success"):
return 1
print(ssh(f"echo '{PASS}' | sudo -S systemctl restart cloudflared"))
time.sleep(10)
print(ssh(
"curl -sSI --max-time 20 https://exposedgays.com/ | head -12; "
"curl -sSL --max-time 20 https://exposedgays.com/ | head -c 250"
))
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"dig @1.1.1.1 thepinkpulse.com CNAME +short",
"dig @1.1.1.1 thepinkpulse.com A +short",
"curl -sSI --max-time 15 https://thepinkpulse.com/ | head -12",
"cloudflared tunnel run --help 2>&1 | grep -i remote",
"cloudflared help 2>&1 | grep -i 'local\\|remote\\|propagat' | head -20",
"grep -n 'exposedgays' /etc/cloudflared/config.yml",
"wc -l /etc/cloudflared/config.yml",
"cat /data/coolify/proxy/dynamic/onsethost-subdomains.yaml | head -60",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=40)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:5000])
c.close()

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.11", username="localadministrator", password=PASS, timeout=20)
cmds = [
"cat /home/localadministrator/mail-proxy/default.conf",
"cat /home/localadministrator/mail-proxy/start.sh",
"curl -sSI --max-time 10 -H 'Host: coolify.onsethost.com' http://127.0.0.1/ | head -8",
"curl -sSL --max-time 10 -H 'Host: coolify.onsethost.com' http://127.0.0.1/ | head -c 200",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:6000])
c.close()

18
deploy/_nginx_full.py Normal file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.11", username="localadministrator", password=PASS, timeout=20)
cmds = [
"docker exec mail-onsethost-proxy nginx -T 2>/dev/null | head -120",
"curl -k -sSL --max-time 10 https://10.10.0.11:80 -H 'Host: thepinkpulse.com' | head -c 300",
"curl -k -sSL --max-time 10 https://10.10.0.11:80 -H 'Host: exposedgays.com' | head -c 300",
]
for cmd in cmds:
print(f"\n=== {cmd[:90]} ===")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:4000])
c.close()

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
for src, host, user in [
("supabase", "10.10.0.11", "localadministrator"),
("coolify", "10.10.0.10", "localadministrator"),
]:
print(f"\n######## {src} ########")
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(host, username=user, password=PASS, timeout=20)
cmds = [
"sudo ss -tlnp | grep ':80 '",
"docker ps --format 'table {{.Names}}\t{{.Ports}}' | grep -E '80|443|kong|proxy|nginx|traefik' ",
"curl -sSI --max-time 10 -H 'Host: thepinkpulse.com' http://10.10.0.11/ | head -6",
"curl -sSI --max-time 10 -H 'Host: thepinkpulse.com' http://127.0.0.1/ | head -6",
"curl -sSI --max-time 10 -H 'Host: exposedgays.com' -k https://127.0.0.1/ | head -6",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:2000])
c.close()

41
deploy/_probe_worker.py Normal file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ZID = "b9e032992b315331c8b0571473fedc89" # exposedgays zone
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
def call(path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read().decode())
for path in [
f"/zones/{ZID}/workers/routes",
f"/accounts/2599c23bbb1255dbb73e8d34b4115fda/workers/scripts",
f"/zones/{ZID}/rulesets/phases/http_request_dynamic_redirect/entrypoint",
]:
code, res = call(path)
print(path, code, res.get("success"), (res.get("errors") or res.get("result"))[:2] if isinstance(res.get("result"), list) else res.get("errors"))

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
paths = [
"/home/localadministrator/coder/onsethost/.env.local",
"/data/coolify/applications/shuurwefh5ipw3qfvmbq4vic/.env",
]
for p in paths:
print(f"\n=== {p} ===")
_, o, e = c.exec_command(f"grep -i cloudflare {p} 2>/dev/null || echo missing", timeout=20)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
for line in text.splitlines():
if "=" in line:
k, _, v = line.partition("=")
v = v.strip().strip('"')
if len(v) > 16:
v = v[:12] + "..." + v[-4:]
print(f"{k}={v}")
else:
print(line)
c.close()

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
for line in text.splitlines():
if any(k in line.upper() for k in ["CLOUDFLARE", "CF_", "TUNNEL", "AUTH"]):
if "SECRET" in line.upper() or "TOKEN" in line.upper() or "KEY" in line.upper() or "EMAIL" in line.upper():
k, _, v = line.partition("=")
v = v.strip()
if len(v) > 12:
v = v[:8] + "..." + v[-4:]
print(f"{k}={v}")
else:
print(line)

40
deploy/_remote_ingress.py Normal file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
"""Parse cloudflared journal for remote ingress hostnames."""
import json
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = c.exec_command(
"sudo journalctl -u cloudflared --no-pager -n 100 | grep 'Updated to new configuration' | tail -1",
timeout=30,
)
o.channel.recv_exit_status()
line = (o.read() + e.read()).decode()
idx = line.find('version=')
if idx == -1:
print("no config line found")
print(line[:500])
c.close()
raise SystemExit(1)
# extract JSON after version=N
import re
m = re.search(r"config=(\{.*\})", line)
if not m:
print("no json in line")
print(line[:1000])
c.close()
raise SystemExit(1)
cfg = json.loads(m.group(1))
ingress = cfg.get("ingress", [])
hosts = sorted(h for h in (i.get("hostname") for i in ingress) if h)
print(f"remote ingress hostnames: {len(hosts)}")
for h in hosts:
if any(x in (h or "") for x in ["exposed", "avbeat", "pink", "freerange", "onsethost"]):
print(" ", h)
print("...")
for target in ["exposedgays.com", "www.exposedgays.com", "avbeat.com", "thepinkpulse.com", "*.onsethost.com"]:
print(f"{target}: {target in hosts}")
c.close()

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
tokens = set()
for host, user, sudo in [
("10.10.0.10", "localadministrator", True),
("10.10.0.1", "admin", False),
]:
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(host, username=user, password=PASS, timeout=15)
if sudo:
cmd = (
f"echo '{PASS}' | sudo -S bash -c \""
"grep -rhoE 'cfut_[A-Za-z0-9_-]{20,}' "
"/etc/infra /etc/cloudflared /root /home/localadministrator "
"/data/coolify 2>/dev/null | sort -u\""
)
else:
cmd = "grep -oE 'cfut_[A-Za-z0-9_-]{20,}' /usr/local/etc/wan-dns-failover.env /usr/local/etc/*.env 2>/dev/null"
_, o, e = c.exec_command(cmd, timeout=180)
o.channel.recv_exit_status()
for line in (o.read() + e.read()).decode().splitlines():
if line.startswith("cfut_"):
tokens.add(line.strip())
c.close()
# local workspace tokens
for t in [
"cfut_vF51avCWJV9EnCM4lXS2v60jXo6TPQZ0yxESHuRn5b0f3515",
"cfut_IOm1DJW4pDkPCuQA6UoOjqO6Tcv2zU10y2PSw82m379f7db3f7",
]:
tokens.add(t)
print(f"probing {len(tokens)} tokens for tunnel PUT...")
EG = [
{"hostname": "exposedgays.com", "service": "https://localhost:443", "originRequest": {"noTLSVerify": True}},
{"hostname": "www.exposedgays.com", "service": "https://localhost:443", "originRequest": {"noTLSVerify": True}},
]
for tok in sorted(tokens):
req = urllib.request.Request(
f"{CF}/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations",
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
data = json.load(r)
ingress = data["result"]["config"]["ingress"]
hostnames = {i.get("hostname") for i in ingress if i.get("hostname")}
changed = False
for rule in EG:
if rule["hostname"] not in hostnames:
catch = next((i for i, x in enumerate(ingress) if not x.get("hostname")), len(ingress))
ingress.insert(catch, rule)
changed = True
if changed:
put_req = urllib.request.Request(
f"{CF}/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations",
data=json.dumps({"config": {"ingress": ingress, "warp-routing": {"enabled": False}}}).encode(),
method="PUT",
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(put_req, timeout=60) as pr:
put = json.load(pr)
print(f"SUCCESS PUT with {tok[:20]}... success={put.get('success')}")
raise SystemExit(0)
print(f"{tok[:20]}... GET OK already has exposedgays")
raise SystemExit(0)
except urllib.error.HTTPError as e:
err = json.loads(e.read().decode())
msg = (err.get("errors") or [{}])[0].get("message", "")[:40]
print(f"{tok[:20]}... {e.code} {msg}")
except SystemExit:
raise
except Exception as ex:
print(f"{tok[:20]}... err {ex}")
print("NO TOKEN WITH TUNNEL CONFIG ACCESS")

28
deploy/_ssh_supabase.py Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
users = ["root", "localadministrator", "admin", "ubuntu"]
for user in users:
print(f"\n=== trying {user}@10.10.0.11 ===")
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
c.connect("10.10.0.11", username=user, password=PASS, timeout=15)
print("CONNECTED as", user)
for cmd in [
"hostname",
"ss -tlnp | grep ':80 ' || true",
"curl -sSI -H 'Host: thepinkpulse.com' http://127.0.0.1/ | head -8",
"curl -sSI -H 'Host: exposedgays.com' http://127.0.0.1/ | head -8",
"docker ps --format '{{.Names}}' | head -20",
"ls /etc/nginx/sites-enabled 2>/dev/null || ls /etc/caddy 2>/dev/null || ls /etc/traefik 2>/dev/null || echo no_proxy_dir",
]:
print(f"\n$ {cmd}")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:2000])
c.close()
break
except Exception as ex:
print("failed:", ex)

22
deploy/_supabase_nginx.py Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.11", username="localadministrator", password=PASS, timeout=20)
cmds = [
"docker inspect mail-onsethost-proxy --format '{{json .Config.Env}}' 2>/dev/null | python3 -m json.tool 2>/dev/null | head -40",
"docker inspect mail-onsethost-proxy --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}\n{{end}}' 2>/dev/null",
"sudo nginx -T 2>/dev/null | grep -E 'server_name|proxy_pass|thepinkpulse|exposedgays|10.10.0.10' | head -40",
"sudo find /etc /opt /home -name '*.conf' 2>/dev/null | xargs sudo grep -l 'thepinkpulse\\|10.10.0.10\\|coolify' 2>/dev/null | head -15",
"grep -rniE 'cfut_|CLOUDFLARE|cloudflared' /home/localadministrator /etc/infra 2>/dev/null | head -20",
"curl -sSI --max-time 10 -H 'Host: thepinkpulse.com' -H 'X-Forwarded-Proto: https' http://10.10.0.11/ | head -10",
"curl -sSI --max-time 10 -H 'Host: exposedgays.com' -H 'X-Forwarded-Proto: https' http://10.10.0.11/ | head -10",
]
for cmd in cmds:
print(f"\n=== {cmd[:100]} ===")
_, o, e = c.exec_command(cmd, timeout=60)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:4000])
c.close()

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"sudo cloudflared tunnel ingress rule https://exposedgays.com/ 2>&1",
"sudo cloudflared tunnel ingress rule https://thepinkpulse.com/ 2>&1",
"curl -k -sSI --max-time 10 https://10.10.0.11:80 -H 'Host: thepinkpulse.com' | head -10",
"curl -k -sSI --max-time 10 https://10.10.0.11:80 -H 'Host: exposedgays.com' | head -10",
"curl -k -sSI --max-time 10 https://127.0.0.1:443 -H 'Host: exposedgays.com' | head -8",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:2500])
c.close()

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
hosts = ["gitea.onsethost.com", "thepinkpulse.com", "exposedgays.com", "exposedgays.onsethost.com"]
for h in hosts:
for scheme in ["http", "https"]:
cmd = f"curl -k -sSI --max-time 8 -H 'Host: {h}' {scheme}://10.10.0.11:80/ | head -3"
_, o, e = c.exec_command(cmd, timeout=20)
o.channel.recv_exit_status()
out = (o.read() + e.read()).decode().strip().replace("\n", " | ")
print(f"{scheme:5} {h:30} -> {out}")
c.close()

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
def req(method, path, body=None):
r = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(r, timeout=45) as resp:
return resp.status, json.load(resp)
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read().decode())
for method, path in [
("DELETE", f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations"),
("GET", f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}"),
("PATCH", f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}"),
]:
code, res = req(method, path)
print(method, path, code, res.get("success"), res.get("errors"))

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"curl -k -sSL --max-time 12 https://10.10.0.11:80 -H 'Host: exposedgays.com' | head -c 250",
"curl -k -sSL --max-time 12 https://10.10.0.11:80 -H 'Host: thepinkpulse.com' | head -c 250",
"curl -k -sSL --max-time 12 https://127.0.0.1:443 -H 'Host: exposedgays.com' | head -c 250",
]
for cmd in cmds:
print(f"\n=== {cmd[:90]} ===")
_, o, e = c.exec_command(cmd, timeout=25)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:500])
c.close()

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Interim: expose via exposedgays.onsethost.com using existing *.onsethost.com tunnel rule."""
import json
import re
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ONSETHOST_ZID = "f266ece9a3e8dd1a401a7a334e320213"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
APP_UUID = "ira4bw4nhbvm87o7s3fgqu6v"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
# DNS for exposedgays.onsethost.com -> tunnel (proxied)
body = {
"type": "CNAME",
"name": "exposedgays.onsethost.com",
"content": f"{TUNNEL}.cfargotunnel.com",
"ttl": 1,
"proxied": True,
}
req = urllib.request.Request(
f"{CF}/zones/{ONSETHOST_ZID}/dns_records",
data=json.dumps(body).encode(),
method="POST",
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
res = json.load(r)
print("DNS POST:", res.get("success"), res.get("errors"))
except Exception as ex:
print("DNS:", ex)
# Add domain to Coolify app via DB
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
f"docker exec coolify-db psql -U coolify -d coolify -t -A -c \"SELECT fqdn FROM applications WHERE uuid='{APP_UUID}';\"",
f"docker exec coolify-db psql -U coolify -d coolify -c \"UPDATE applications SET fqdn='https://exposedgays.com,https://www.exposedgays.com,https://exposedgays.onsethost.com' WHERE uuid='{APP_UUID}';\"",
"docker exec coolify-db psql -U coolify -d coolify -t -A -c \"SELECT fqdn FROM applications WHERE uuid='ira4bw4nhbvm87o7s3fgqu6v';\"",
"curl -sSI --max-time 20 -k -H 'Host: exposedgays.onsethost.com' https://127.0.0.1/ | head -10",
"curl -sSI --max-time 20 https://exposedgays.onsethost.com/ 2>&1 | head -12",
"curl -sSL --max-time 20 https://exposedgays.onsethost.com/ 2>&1 | head -c 300",
]
for cmd in cmds:
print(f"\n$ {cmd[:100]}")
_, o, e = ssh.exec_command(cmd, timeout=40)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:2000])
ssh.close()

53
deploy/add-tunnel.py Normal file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Add exposedgays.com to cloudflared tunnel on COOLIFY_01."""
import paramiko
import re
HOST = "10.10.0.10"
USER = "localadministrator"
PASS = "Bbt9115xty9176!"
CONFIG = "/etc/cloudflared/config.yml"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(HOST, username=USER, password=PASS, timeout=20, allow_agent=False, look_for_keys=False)
_, o, e = c.exec_command(f"sudo cat {CONFIG}", timeout=30)
config = o.read().decode()
print("Current config snippet:")
print(config[-2000:])
if "exposedgays.com" in config:
print("Tunnel entry already exists")
else:
entry = """
- hostname: exposedgays.com
service: https://localhost:443
originRequest:
noTLSVerify: true
- hostname: www.exposedgays.com
service: https://localhost:443
originRequest:
noTLSVerify: true
"""
# Insert before catch-all if present
if "catch-all" in config or "http_status:404" in config:
new_config = re.sub(
r"(\n - service: http_status:404)",
entry + r"\1",
config,
count=1,
)
else:
new_config = config.rstrip() + "\ningress:" + entry
sftp = c.open_sftp()
with sftp.file("/tmp/cloudflared_exposedgays.yml", "w") as f:
f.write(new_config)
sftp.close()
c.exec_command(f"sudo cp {CONFIG} {CONFIG}.bak.exposedgays")
c.exec_command(f"sudo cp /tmp/cloudflared_exposedgays.yml {CONFIG}")
c.exec_command("sudo systemctl restart cloudflared")
print("Tunnel updated and cloudflared restarted")
c.close()

View File

@@ -0,0 +1,51 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
DOMAIN = "exposedgays.com"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
def call(path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
for path in ["/accounts", "/user", "/user/tokens/verify"]:
res = call(path)
print(path, json.dumps(res, indent=2)[:2000])
print()
accounts = call("/accounts").get("result", [])
for acct in accounts:
aid = acct["id"]
print(f"Trying account {acct.get('name')} {aid}")
for body in [
{"name": DOMAIN, "account": {"id": aid}, "type": "full"},
{"name": DOMAIN, "type": "full"},
]:
res = call("/zones", "POST", body)
print(" ", body, "->", res.get("success"), res.get("errors"))
if res.get("success"):
print(" NS:", res["result"].get("name_servers"))

45
deploy/cf-all-zones.py Normal file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
def get_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def call(tok, path):
req = urllib.request.Request(
CF + path,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=60) as r:
return json.load(r)
def main():
tok = get_token()
page = 1
while page <= 20:
res = call(tok, f"/zones?per_page=50&page={page}")
for z in res.get("result", []):
print(f"{z['name']:40} {z['status']:10} {z['id']}")
if len(res.get("result", [])) < 50:
break
page += 1
if __name__ == "__main__":
main()

26
deploy/cf-deep-hunt.py Normal file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
f"echo '{PASS}' | sudo -S cat /etc/infra/secrets.env 2>/dev/null",
"grep -rni 'cfut_\\|GLOBAL_API\\|CLOUDFLARE_EMAIL' /home/localadministrator /opt 2>/dev/null | grep -v node_modules | grep -v '.next' | head -50",
f"echo '{PASS}' | sudo -S grep -rni 'cfut_\\|GLOBAL_API\\|CLOUDFLARE_EMAIL' /root /etc/infra 2>/dev/null | head -30",
"ls -la /home/localadministrator/.config/openclaw/ 2>/dev/null; cat /home/localadministrator/.config/openclaw/cloudflare.env 2>/dev/null",
]
for cmd in cmds:
print("===", cmd[:90], "===")
_, o, e = c.exec_command(cmd, timeout=120)
o.channel.recv_exit_status()
out = (o.read() + e.read()).decode(errors="replace")
# redact full tokens in output
import re
out = re.sub(r"cfut_[A-Za-z0-9]{20,}", "cfut_[REDACTED]", out)
print(out[:10000])
print()
c.close()

27
deploy/cf-hunt-remote.py Normal file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
HOST = "10.10.0.10"
USER = "localadministrator"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(HOST, username=USER, password=PASS, timeout=30)
cmds = [
"grep -rni 'CLOUDFLARE\\|cfut_\\|GLOBAL_API' /home/localadministrator --include='*.env*' 2>/dev/null | head -40",
f"echo '{PASS}' | sudo -S grep -rni 'CLOUDFLARE\\|cfut_\\|GLOBAL_API' /etc/infra /root 2>/dev/null | head -40",
f"echo '{PASS}' | sudo -S find /data/coolify -name '.env' 2>/dev/null | xargs grep -l CLOUDFLARE 2>/dev/null | head -10",
f"echo '{PASS}' | sudo -S ls -la /etc/cloudflared/ /root/.cloudflared/ /home/localadministrator/.cloudflared/ 2>/dev/null",
f"echo '{PASS}' | sudo -S grep -E 'tunnel|credentials|origincert' /etc/cloudflared/config.yml 2>/dev/null | head -20",
]
for cmd in cmds:
print("===", cmd[:100], "===")
_, o, e = c.exec_command(cmd, timeout=90)
o.channel.recv_exit_status()
out = (o.read() + e.read()).decode(errors="replace")
print(out[:6000])
print()
c.close()

59
deploy/cf-list-zones.py Normal file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
DOMAIN = "exposedgays.com"
def get_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def call(tok, path):
req = urllib.request.Request(
CF + path,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=60) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
def main():
tok = get_token()
page = 1
all_zones = []
while page <= 20:
res = call(tok, f"/zones?per_page=50&page={page}")
batch = res.get("result") or []
all_zones.extend(batch)
if len(batch) < 50:
break
page += 1
print(f"Total zones: {len(all_zones)}")
for z in all_zones:
if DOMAIN in z["name"] or "exposed" in z["name"].lower():
print(f"MATCH: {z['name']} status={z['status']} id={z['id']}")
print(f" NS: {z.get('name_servers')}")
z = call(tok, f"/zones?name={DOMAIN}")
print(f"\nDirect lookup: success={z.get('success')} count={len(z.get('result') or [])}")
if __name__ == "__main__":
main()

26
deploy/cf-ns-sample.py Normal file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python3
import json
import re
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
for domain in ["justtworoommates.com", "thepinkpulse.com", "freerangefruit.com"]:
req = urllib.request.Request(
f"{CF}/zones?name={domain}",
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=30) as r:
res = json.load(r)
z = res["result"][0]
print(f"{domain}: {z.get('name_servers')}")

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
import json
import urllib.error
import urllib.request
CF = "https://api.cloudflare.com/client/v4"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
DOMAIN = "exposedgays.com"
TOK = "cfut_vF51avCWJV9EnCM4lXS2v60jXo6TPQZ0yxESHuRn5b0f3515"
def call(path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
for path in [
"/user/tokens/verify",
f"/zones?name={DOMAIN}",
"/zones?per_page=50",
f"/accounts/{ACCOUNT}/tokens/verify",
]:
res = call(path)
print(path, "->", json.dumps(res)[:500])
create = call(
"/zones",
"POST",
{"name": DOMAIN, "account": {"id": ACCOUNT}, "type": "full", "jump_start": False},
)
print("create ->", json.dumps(create))

43
deploy/cf-probe.py Normal file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env python3
import json, re, urllib.request, urllib.error, paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
def get_pfsense_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def call(token, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
tokens = {
"pfsense": get_pfsense_token(),
"apply_mail": "cfut_zlUt5lAKVsu7qIcRCHJnyhAvpJVF5jx24HlrIEn9a8fcd6a1",
}
for name, tok in tokens.items():
v = call(tok, "/user/tokens/verify")
print(name, "verify:", v.get("success"), v.get("messages"), v.get("errors"))
z = call(tok, "/zones?name=exposedgays.com")
print(name, "zone:", z.get("success"), len(z.get("result") or []), z.get("errors"))
c = call(tok, "/zones", "POST", {"name": "exposedgays.com", "account": {"id": "2599c23bbb1255dbb73e8d34b4115fda"}, "type": "full"})
print(name, "create:", c.get("success"), c.get("errors"))
if c.get("success"):
print(" nameservers:", c["result"].get("name_servers"))

View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""Scan COOLIFY_01 for all cfut_ tokens and try zone create for exposedgays.com."""
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
DOMAIN = "exposedgays.com"
TUNNEL_CNAME = "03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com"
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = ssh.exec_command(
f"echo '{PASS}' | sudo -S bash -c \"grep -rho 'cfut_[A-Za-z0-9_-]\\{{20,\\}}' "
"/etc/cloudflared /etc/infra /root /home/localadministrator 2>/dev/null | sort -u\"",
timeout=180,
)
o.channel.recv_exit_status()
tokens = sorted(set((o.read() + e.read()).decode().strip().splitlines()))
print(f"Found {len(tokens)} tokens on server")
ssh.close()
# Also include known tokens
tokens += [
"cfut_zlUt5lAKVsu7qIcRCHJnyhAvpJVF5jx24HlrIEn9a8fcd6a1",
"cfut_IOm1DJW4pDkPCuQA6UoOjqO6Tcv2zU10y2PSw82m379f7db3f7",
]
tokens = sorted(set(tokens))
def call(tok, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
winner = None
zone = None
for tok in tokens:
v = call(tok, "/user/tokens/verify")
if not v.get("success"):
continue
expired = any(m.get("code") == 10001 for m in v.get("messages", []))
label = tok[:16] + "..."
print(f"\n{label} verify OK expired={expired}")
z = call(tok, f"/zones?name={DOMAIN}")
if z.get("result"):
winner, zone = tok, z["result"][0]
print(f" FOUND zone {zone['id']} status={zone['status']}")
break
c = call(
tok,
"/zones",
"POST",
{"name": DOMAIN, "account": {"id": ACCOUNT}, "type": "full", "jump_start": False},
)
if c.get("success"):
winner, zone = tok, c["result"]
print(f" CREATED zone NS={zone.get('name_servers')}")
break
err = (c.get("errors") or [{}])[0].get("message", "")
print(f" create fail: {err[:100]}")
if not zone:
print("\nNo token could find or create zone")
raise SystemExit(1)
zid = zone["id"]
body_apex = {"type": "CNAME", "name": DOMAIN, "content": TUNNEL_CNAME, "ttl": 1, "proxied": True}
body_www = {"type": "CNAME", "name": f"www.{DOMAIN}", "content": TUNNEL_CNAME, "ttl": 1, "proxied": True}
records = call(winner, f"/zones/{zid}/dns_records?per_page=100").get("result", [])
for name, body in [(DOMAIN, body_apex), (f"www.{DOMAIN}", body_www)]:
existing = next(
(r for r in records if r["name"] == name and r["type"] in ("A", "CNAME", "AAAA")),
None,
)
if existing:
res = call(winner, f"/zones/{zid}/dns_records/{existing['id']}", "PATCH", body)
else:
res = call(winner, f"/zones/{zid}/dns_records", "POST", body)
print(f"DNS {name}: success={res.get('success')} errors={res.get('errors')}")
print("\n=== Nameservers (update GoDaddy if status=pending) ===")
for ns in zone.get("name_servers", []):
print(f" {ns}")
print(f"Zone status: {zone.get('status')}")

58
deploy/cf-token-perms.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
DOMAIN = "exposedgays.com"
def get_tokens():
tokens = []
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
tokens.append(("pfsense", re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)))
tokens.append(("apply_mail", "cfut_zlUt5lAKVsu7qIcRCHJnyhAvpJVF5jx24HlrIEn9a8fcd6a1"))
return tokens
def call(tok, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
def main():
for label, tok in get_tokens():
v = call(tok, "/user/tokens/verify")
print(f"\n[{label}] verify:", json.dumps(v, indent=2)[:1200])
z = call(tok, f"/zones?name={DOMAIN}")
print(f"[{label}] zone lookup:", z.get("success"), len(z.get("result") or []))
c = call(
tok,
"/zones",
"POST",
{"name": DOMAIN, "account": {"id": ACCOUNT}, "type": "full", "jump_start": False},
)
print(f"[{label}] create:", c.get("success"), c.get("errors"))
if __name__ == "__main__":
main()

114
deploy/cf-try-all-tokens.py Normal file
View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
DOMAIN = "exposedgays.com"
TUNNEL_CNAME = "03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com"
TOKENS = [
("pfsense", None),
("apply_mail", "cfut_zlUt5lAKVsu7qIcRCHJnyhAvpJVF5jx24HlrIEn9a8fcd6a1"),
("local_old", "cfut_vF51avCWJV9EnCM4lXS2v60jXo6TPQZ0yxESHuRn5b0f3515"),
("local_full", "cfut_IOm1DJW4pDkPCuQA6UoOjqO6Tcv2zU10y2PSw82m379f7db3f7"),
("local_trunc", "cfut_IOm1DJW4pDkPCuQA6UoOjqO6Tcv2zU10y2PSw82m379f7db3"),
]
try:
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
TOKENS[0] = ("pfsense", re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1))
except Exception as ex:
print("pfsense err", ex)
def call(tok, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
def upsert_dns(tok, zid):
records = call(tok, f"/zones/{zid}/dns_records?per_page=100").get("result", [])
for name in [DOMAIN, f"www.{DOMAIN}"]:
existing = next(
(r for r in records if r["name"] == name and r["type"] in ("A", "CNAME", "AAAA")),
None,
)
body = {"type": "CNAME", "name": name, "content": TUNNEL_CNAME, "ttl": 1, "proxied": True}
if existing:
res = call(tok, f"/zones/{zid}/dns_records/{existing['id']}", "PATCH", body)
action = "PATCH"
else:
res = call(tok, f"/zones/{zid}/dns_records", "POST", body)
action = "POST"
print(f" {action} {name}: success={res.get('success')} errors={res.get('errors')}")
def main():
winner = None
zone = None
for label, tok in TOKENS:
if not tok:
continue
v = call(tok, "/user/tokens/verify")
print(f"\n[{label}] verify={v.get('success')} errors={v.get('errors')}")
if not v.get("success"):
continue
z = call(tok, f"/zones?name={DOMAIN}")
if z.get("result"):
winner, zone, tok_label = tok, z["result"][0], label
print(f"[{label}] FOUND existing zone {zone['id']} status={zone['status']}")
break
c = call(
tok,
"/zones",
"POST",
{"name": DOMAIN, "account": {"id": ACCOUNT}, "type": "full", "jump_start": False},
)
print(f"[{label}] create={c.get('success')} errors={c.get('errors')}")
if c.get("success"):
winner, zone, tok_label = tok, c["result"], label
print(f"[{label}] CREATED zone NS={zone.get('name_servers')}")
break
if not zone:
print("\nFAILED: no token could find or create zone")
return
print(f"\nUsing token [{tok_label}] zone {zone['id']} status={zone['status']}")
print("Nameservers:")
for ns in zone.get("name_servers", []):
print(f" {ns}")
upsert_dns(winner, zone["id"])
print("\nFinal records:")
for r in call(winner, f"/zones/{zone['id']}/dns_records?per_page=100").get("result", []):
if "exposedgays" in r["name"]:
print(f" {r['type']} {r['name']} -> {r['content']} proxied={r.get('proxied')}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
DOMAIN = "exposedgays.com"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
def call(path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read().decode())
endpoints = [
("GET", f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations"),
("GET", f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}"),
("GET", f"/accounts/{ACCOUNT}/cfd_tunnel"),
("POST", f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/routes", {"network": "0.0.0.0/0", "comment": "test"}),
("POST", f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/connections", {}),
]
for method, path, *body in [(e[0], e[1], e[2] if len(e) > 2 else None) for e in endpoints]:
code, res = call(path, method, body[0] if body else None)
print(f"{method} {path}")
print(f" code={code} success={res.get('success')} errors={res.get('errors')}")

View File

@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Route exposedgays.com DNS via cloudflared on COOLIFY_01."""
import paramiko
PASS = "Bbt9115xty9176!"
HOST = "10.10.0.10"
USER = "localadministrator"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CREDS = f"/etc/cloudflared/{TUNNEL}.json"
DOMAIN = "exposedgays.com"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(HOST, username=USER, password=PASS, timeout=30)
cmds = [
f"echo '{PASS}' | sudo -S find / -name 'cert.pem' 2>/dev/null | head -20",
f"echo '{PASS}' | sudo -S cat {CREDS}",
f"echo '{PASS}' | sudo -S cloudflared tunnel --cred-file {CREDS} route dns -f {TUNNEL} {DOMAIN} 2>&1",
f"echo '{PASS}' | sudo -S cloudflared tunnel --cred-file {CREDS} route dns -f {TUNNEL} www.{DOMAIN} 2>&1",
"dig @1.1.1.1 exposedgays.com CNAME +short 2>&1",
"dig @1.1.1.1 www.exposedgays.com CNAME +short 2>&1",
"dig @1.1.1.1 exposedgays.com A +short 2>&1",
]
for cmd in cmds:
print("===", cmd[:100], "===")
_, o, e = c.exec_command(cmd, timeout=120)
o.channel.recv_exit_status()
out = (o.read() + e.read()).decode(errors="replace")
# redact tunnel secret if present
if "TunnelSecret" in out:
import re
out = re.sub(r'"TunnelSecret":\s*"[^"]+"', '"TunnelSecret": "[REDACTED]"', out)
print(out[:8000])
print()
c.close()

26
deploy/cf-zone-check.py Normal file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python3
import json
import re
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
DOMAIN = "exposedgays.com"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
req = urllib.request.Request(
f"{CF}/zones?name={DOMAIN}",
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=30) as r:
res = json.load(r)
print(json.dumps(res, indent=2))

90
deploy/cf-zone-probe.py Normal file
View File

@@ -0,0 +1,90 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
CF = "https://api.cloudflare.com/client/v4"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
DOMAIN = "exposedgays.com"
def get_tokens():
tokens = []
try:
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
m = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text)
if m:
tokens.append(("pfsense", m.group(1)))
except Exception as ex:
print("pfsense err", ex)
try:
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for path in [
"/home/localadministrator/coder/onsethost/.env.local",
"/etc/infra/secrets.env",
"/root/.env",
]:
_, o, e = c.exec_command(
f"grep CLOUDFLARE_API_TOKEN {path} 2>/dev/null | head -1", timeout=20
)
o.channel.recv_exit_status()
line = (o.read() + e.read()).decode().strip()
if line and "=" in line:
tokens.append((path, line.split("=", 1)[1].strip().strip('"')))
c.close()
except Exception as ex:
print("coolify err", ex)
tokens += [
("apply_mail", "cfut_zlUt5lAKVsu7qIcRCHJnyhAvpJVF5jx24HlrIEn9a8fcd6a1"),
("onsethost", "cfut_IOm1DJW4pDkPCuQA6UoOjqO6Tcv2zU10y2PSw82m379f7db3"),
]
return tokens
def call(tok, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
def main():
for label, tok in get_tokens():
v = call(tok, "/user/tokens/verify")
print(f"[{label}] verify:", v.get("success"), v.get("errors"))
z = call(tok, f"/zones?name={DOMAIN}")
print(f"[{label}] zone:", z.get("success"), len(z.get("result") or []), z.get("errors"))
c = call(
tok,
"/zones",
"POST",
{"name": DOMAIN, "account": {"id": ACCOUNT}, "type": "full", "jump_start": False},
)
print(f"[{label}] create:", c.get("success"), c.get("errors"))
if c.get("success"):
print(" NS:", c["result"].get("name_servers"))
print(" status:", c["result"].get("status"))
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"dig @1.1.1.1 avbeat.com NS +short",
"dig @1.1.1.1 avbeat.com A +short",
"dig @1.1.1.1 avbeat.com CNAME +short",
"dig @1.1.1.1 www.avbeat.com CNAME +short",
"curl -sSI --max-time 10 https://avbeat.com/ | head -6",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=20)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode())
c.close()

View File

@@ -0,0 +1,34 @@
#!/usr/bin/env python3
import json
import re
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
DOMAIN = "exposedgays.com"
ZID = "b9e032992b315331c8b0571473fedc89"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
def cf(path):
req = urllib.request.Request(
CF + path,
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=30) as r:
return json.load(r)
zone = cf(f"/zones/{ZID}")["result"]
print("Zone status:", zone["status"])
print("Nameservers:", zone["name_servers"])
print("\nDNS records:")
for r in cf(f"/zones/{ZID}/dns_records?per_page=50")["result"]:
if DOMAIN in r["name"]:
print(f" {r['type']:6} {r['name']:25} -> {r['content'][:60]} proxied={r.get('proxied')}")

33
deploy/check-dns-full.py Normal file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/env python3
import paramiko
import subprocess
PASS = "Bbt9115xty9176!"
print("=== Local dig ===")
for q in ["exposedgays.com", "www.exposedgays.com"]:
r = subprocess.run(["nslookup", q, "1.1.1.1"], capture_output=True, text=True, timeout=20)
print(f"\n{q}:")
print(r.stdout or r.stderr)
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=20)
cmds = [
"dig @1.1.1.1 exposedgays.com NS +short",
"dig @1.1.1.1 exposedgays.com A +short",
"dig @1.1.1.1 exposedgays.com CNAME +short",
"dig @1.1.1.1 www.exposedgays.com A +short",
"dig @1.1.1.1 www.exposedgays.com CNAME +short",
"curl -sSI https://exposedgays.com/ | head -15",
"curl -sSL https://exposedgays.com/ | head -c 400",
"curl -sSI -H 'Host: exposedgays.com' https://127.0.0.1/ -k | head -10",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace"))
c.close()

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env python3
import urllib.request
import ssl
ctx = ssl.create_default_context()
req = urllib.request.Request("https://exposedgays.com/", headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=15, context=ctx) as r:
body = r.read(2000).decode(errors="replace")
print("status:", r.status)
print("headers:", dict(r.headers))
print("body[:1500]:")
print(body[:1500])

20
deploy/check-dns.py Normal file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env python3
import paramiko
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password="Bbt9115xty9176!", timeout=20)
cmds = [
"grep -A3 'exposedgays' /etc/cloudflared/config.yml",
"curl -sS -o /dev/null -w 'local_traefik:%{http_code}' -k -H 'Host: exposedgays.com' https://127.0.0.1/",
"curl -sS -o /dev/null -w 'public:%{http_code}' --max-time 15 https://exposedgays.com/ 2>&1 || echo public_failed",
]
for cmd in cmds:
print("$", cmd)
_, o, e = c.exec_command(cmd, timeout=30)
print((o.read() + e.read()).decode().strip())
print()
c.close()

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"sudo journalctl -u cloudflared --no-pager -n 5 | grep -o 'hostname.*exposedgays' || echo 'not in recent log'",
"sudo journalctl -u cloudflared --no-pager -n 3 | grep 'Updated to new configuration' | tail -1 | grep -o 'exposedgays' || echo 'exposedgays not in remote config'",
"dig @annabel.ns.cloudflare.com exposedgays.com A +short",
"dig @annabel.ns.cloudflare.com www.exposedgays.com A +short",
"dig @1.1.1.1 exposedgays.com A +short",
"dig @1.1.1.1 www.exposedgays.com A +short",
"curl -sSI --max-time 20 https://exposedgays.com/ 2>&1 | head -12",
"curl -sSL --max-time 20 https://exposedgays.com/ 2>&1 | head -c 300",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=40)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:2500])
c.close()

View File

@@ -0,0 +1,33 @@
#!/usr/bin/env python3
import json, re, urllib.request, urllib.error, paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
ZID = "b9e032992b315331c8b0571473fedc89"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient(); c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read()+e.read()).decode()).group(1)
c.close()
def call(path):
req = urllib.request.Request(CF+path, headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=30) as r: return json.load(r)
except urllib.error.HTTPError as e: return json.loads(e.read().decode())
for path in [
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/routes?per_page=100",
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}",
f"/zones/{ZID}/dns_records?per_page=50",
]:
res = call(path)
print(f"\n{path}")
print("success=", res.get("success"), "errors=", res.get("errors"))
if "dns_records" in path or "routes" in path:
for item in res.get("result", []):
if "exposedgays" in str(item):
print(" ", json.dumps(item)[:300])

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for domain in ["thepinkpulse.com", "freerangefruit.com", "justtworoommates.com"]:
cmds = [
f"dig @1.1.1.1 {domain} NS +short",
f"dig @1.1.1.1 {domain} A +short",
f"dig @1.1.1.1 www.{domain} CNAME +short",
]
print(f"\n--- {domain} ---")
for cmd in cmds:
_, o, e = c.exec_command(cmd, timeout=15)
o.channel.recv_exit_status()
out = (o.read() + e.read()).decode().strip()
print(cmd.split()[-2], "->", out or "(empty)")
c.close()

144
deploy/complete-dns.py Normal file
View File

@@ -0,0 +1,144 @@
#!/usr/bin/env python3
"""Complete exposedgays.com DNS: CF zone + tunnel CNAME + GoDaddy NS delegation."""
import json
import re
import time
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
DOMAIN = "exposedgays.com"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL_CNAME = "03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com"
CF = "https://api.cloudflare.com/client/v4"
GD = "https://api.godaddy.com/v1"
GODADDY_KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
GODADDY_SECRET = "NvC3qexGAU7Rd5QExUTH6z"
def load_cf_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def cf(tok, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {GODADDY_KEY}:{GODADDY_SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:500]}
def upsert_tunnel_cnames(tok, zid):
records = cf(tok, f"/zones/{zid}/dns_records?per_page=100").get("result", [])
for name in [DOMAIN, f"www.{DOMAIN}"]:
body = {"type": "CNAME", "name": name, "content": TUNNEL_CNAME, "ttl": 1, "proxied": True}
existing = next(
(r for r in records if r["name"] == name and r["type"] in ("A", "CNAME", "AAAA")),
None,
)
if existing:
res = cf(tok, f"/zones/{zid}/dns_records/{existing['id']}", "PATCH", body)
action = "PATCH"
else:
res = cf(tok, f"/zones/{zid}/dns_records", "POST", body)
action = "POST"
print(f" {action} {name}: success={res.get('success')} errors={res.get('errors')}")
def main():
tok = load_cf_token()
zone = None
z = cf(tok, f"/zones?name={DOMAIN}")
if z.get("result"):
zone = z["result"][0]
print(f"Found zone {zone['id']} status={zone['status']}")
else:
created = cf(
tok,
"/zones",
"POST",
{"name": DOMAIN, "account": {"id": ACCOUNT}, "type": "full", "jump_start": False},
)
print("Create zone:", created.get("success"), created.get("errors"))
if created.get("success"):
zone = created["result"]
if not zone:
print("BLOCKED: exposedgays.com not in Cloudflare and token lacks zone.create")
print("Add zone at https://dash.cloudflare.com then re-run this script")
return 1
zid = zone["id"]
nameservers = zone.get("name_servers", [])
print("Nameservers:", nameservers)
print("Setting Cloudflare tunnel CNAME records...")
upsert_tunnel_cnames(tok, zid)
if nameservers:
print("Updating GoDaddy nameservers...")
code, res = gd(
f"/domains/{DOMAIN}",
"PATCH",
{"nameServers": nameservers},
)
print(f"GoDaddy NS PATCH HTTP {code}: {res}")
time.sleep(10)
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for cmd in [
f"dig @1.1.1.1 {DOMAIN} NS +short",
f"dig @1.1.1.1 www.{DOMAIN} A +short",
f"curl -sSI https://www.{DOMAIN}/ | head -8",
f"curl -sSL https://www.{DOMAIN}/ | head -c 250",
]:
print(f"\n$ {cmd}")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace"))
c.close()
return 0
if __name__ == "__main__":
raise SystemExit(main())

75
deploy/deep-token-hunt.py Normal file
View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python3
import json
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = c.exec_command(
f"echo '{PASS}' | sudo -S bash -c \"grep -rniE 'cfut_|GLOBAL_API_KEY|CLOUDFLARE_EMAIL|tunnel.*token|TUNNEL_TOKEN' "
"/etc/infra /etc/cloudflared /root /home/localadministrator/coder /home/localadministrator/scripts "
"2>/dev/null | grep -v node_modules | grep -v Binary | head -60\"",
timeout=120,
)
o.channel.recv_exit_status()
lines = (o.read() + e.read()).decode(errors="replace")
print(lines)
c.close()
import re
tokens = sorted(set(re.findall(r"cfut_[A-Za-z0-9_-]{20,}", lines)))
tokens.append("cfut_zlUt5lAKVsu7qIcRCHJnyhAvpJVF5jx24HlrIEn9a8fcd6a1")
tokens = sorted(set(tokens))
print(f"\nProbing {len(tokens)} tokens for tunnel PUT...")
EG_RULES = [
{"hostname": "exposedgays.com", "service": "https://localhost:443", "originRequest": {"noTLSVerify": True}},
{"hostname": "www.exposedgays.com", "service": "https://localhost:443", "originRequest": {"noTLSVerify": True}},
]
for tok in tokens:
req = urllib.request.Request(
f"{CF}/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations",
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
data = json.load(r)
ingress = data["result"]["config"]["ingress"]
hostnames = {i.get("hostname") for i in ingress if i.get("hostname")}
changed = False
for rule in EG_RULES:
if rule["hostname"] not in hostnames:
catch = next((i for i, x in enumerate(ingress) if not x.get("hostname")), len(ingress))
ingress.insert(catch, rule)
changed = True
if changed:
put_req = urllib.request.Request(
f"{CF}/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations",
data=json.dumps({"config": {"ingress": ingress, "warp-routing": {"enabled": False}}}).encode(),
method="PUT",
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(put_req, timeout=60) as pr:
put = json.load(pr)
print(f"SUCCESS with {tok[:18]}... put={put.get('success')}")
raise SystemExit(0)
else:
print(f"{tok[:18]}... GET OK, already has exposedgays")
raise SystemExit(0)
except urllib.error.HTTPError as e:
err = json.loads(e.read().decode())
print(f"{tok[:18]}... {e.code} {(err.get('errors') or [{}])[0].get('message','')[:50]}")
except SystemExit:
raise
except Exception as ex:
print(f"{tok[:18]}... err {ex}")
print("No token could update tunnel config")

View File

@@ -0,0 +1,178 @@
#!/usr/bin/env python3
"""
Delegate exposedgays.com from GoDaddy to Cloudflare.
Requires exposedgays.com zone to exist in Cloudflare (dashboard add if API create fails).
"""
import json
import re
import sys
import time
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
DOMAIN = "exposedgays.com"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL_CNAME = "03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com"
CF = "https://api.cloudflare.com/client/v4"
GD = "https://api.godaddy.com/v1"
GODADDY_KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
GODADDY_SECRET = "NvC3qexGAU7Rd5QExUTH6z"
def load_cf_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def cf(tok, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {GODADDY_KEY}:{GODADDY_SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:800]}
def find_zone(tok):
for status in ["active", "pending", ""]:
q = f"name={DOMAIN}" + (f"&status={status}" if status else "")
res = cf(tok, f"/zones?{q}")
if res.get("result"):
return res["result"][0]
page = 1
while page <= 20:
res = cf(tok, f"/zones?per_page=50&page={page}")
for z in res.get("result", []):
if z["name"] == DOMAIN:
return z
if len(res.get("result", [])) < 50:
break
page += 1
return None
def upsert_tunnel_cnames(tok, zid):
records = cf(tok, f"/zones/{zid}/dns_records?per_page=100").get("result", [])
for name in [DOMAIN, f"www.{DOMAIN}"]:
body = {"type": "CNAME", "name": name, "content": TUNNEL_CNAME, "ttl": 1, "proxied": True}
existing = next(
(r for r in records if r["name"] == name and r["type"] in ("A", "CNAME", "AAAA")),
None,
)
if existing:
res = cf(tok, f"/zones/{zid}/dns_records/{existing['id']}", "PATCH", body)
action = "PATCH"
else:
res = cf(tok, f"/zones/{zid}/dns_records", "POST", body)
action = "POST"
print(f" CF {action} {name}: success={res.get('success')} errors={res.get('errors')}")
def verify():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
f"dig @1.1.1.1 {DOMAIN} NS +short",
f"dig @1.1.1.1 www.{DOMAIN} A +short",
f"curl -sSI https://www.{DOMAIN}/ | head -10",
f"curl -sSL https://www.{DOMAIN}/ | head -c 200",
]
for cmd in cmds:
print(f"\n$ {cmd}")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace"))
c.close()
def main():
tok = load_cf_token()
zone = find_zone(tok)
if not zone:
created = cf(
tok,
"/zones",
"POST",
{"name": DOMAIN, "account": {"id": ACCOUNT}, "type": "full", "jump_start": False},
)
if created.get("success"):
zone = created["result"]
print("Created CF zone via API")
else:
print("CF zone not found and API create failed:")
print(" ", created.get("errors"))
print("\nAdd exposedgays.com at https://dash.cloudflare.com then re-run:")
print(" python exposedgays/deploy/delegate-godaddy-to-cf.py")
return 1
zid = zone["id"]
nameservers = zone.get("name_servers", [])
status = zone.get("status")
print(f"CF zone {zid} status={status}")
print(f"CF nameservers: {nameservers}")
print("\n=== GoDaddy before ===")
code, before = gd(f"/domains/{DOMAIN}")
print(f"HTTP {code} NS={before.get('nameServers') if code == 200 else before}")
print("\n=== Cloudflare tunnel DNS ===")
upsert_tunnel_cnames(tok, zid)
if not nameservers:
print("No nameservers on zone — cannot delegate GoDaddy")
return 1
print("\n=== GoDaddy PATCH nameservers -> Cloudflare ===")
code, res = gd(f"/domains/{DOMAIN}", "PATCH", {"nameServers": nameservers})
print(f"HTTP {code}: {json.dumps(res, indent=2) if res else '{}'}")
code, after = gd(f"/domains/{DOMAIN}")
if code == 200:
print(f"\nGoDaddy after NS={after.get('nameServers')}")
print("\nWaiting 15s for propagation...")
time.sleep(15)
verify()
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""Push v2 features and deploy ExposedGays on Coolify."""
import paramiko
import subprocess
import os
HOST = "10.10.0.10"
USER = "localadministrator"
PASS = "Bbt9115xty9176!"
GITEA_TOKEN = "5c323b8ea677d78b201341f6833d57156a607008"
APP = "ira4bw4nhbvm87o7s3fgqu6v"
REPO_DIR = r"C:\Users\Local Administrator\exposedgays"
def local_git(*args):
cmd = ["git", "-C", REPO_DIR] + list(args)
print("$", " ".join(cmd))
r = subprocess.run(cmd, capture_output=True, text=True)
print(r.stdout[-4000:] if r.stdout else "")
if r.stderr:
print(r.stderr[-2000:])
return r.returncode
# Stage and commit locally
local_git("add", "-A")
local_git("status", "--short")
local_git(
"commit", "-m",
"v2: ID verification by state, 15 photos/5 albums, 20 quick replies, logo, mobile polish"
)
local_git("push", "origin", "main")
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(HOST, username=USER, password=PASS, timeout=20, allow_agent=False, look_for_keys=False)
def run(cmd, t=300):
print("$", cmd[:180])
_, o, e = c.exec_command(cmd, timeout=t)
code = o.channel.recv_exit_status()
out = (o.read() + e.read()).decode(errors="replace")
print(out[-6000:])
print("exit", code, "\n")
return code, out
run("cd /tmp/exposedgays && git pull origin main")
run(
"TOKEN=$(cat /home/localadministrator/.coolify-token | tail -1); "
f"curl -sS -X POST 'http://127.0.0.1:8000/api/v1/deploy?uuid={APP}&force_rebuild=true' "
'-H "Authorization: Bearer $TOKEN"'
)
for i in range(120):
_, out = run("docker exec coolify php artisan queue:work --queue=high,default --once --timeout=900 2>&1", t=920)
if "ApplicationDeploymentJob" in out:
if "DONE" in out:
print("=== DEPLOY SUCCESS ===")
break
if "FAIL" in out:
print("=== DEPLOY FAILED ===")
break
run("curl -sS -o /dev/null -w '%{http_code}' https://exposedgays.com/ 2>&1 || curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/ 2>&1")
c.close()
print("Done.")

26
deploy/diag-tunnel.py Normal file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"sudo systemctl is-active cloudflared",
"sudo systemctl status cloudflared --no-pager | head -20",
"sudo ls -la /etc/cloudflared/",
"sudo ls -la /root/.cloudflared/ 2>/dev/null || echo no_root_cloudflared",
"sudo ls -la /home/localadministrator/.cloudflared/ 2>/dev/null || echo no_home_cloudflared",
"dig @1.1.1.1 www.exposedgays.com A +short",
"dig @1.1.1.1 www.onsethost.com CNAME +short",
"dig @1.1.1.1 coolify.onsethost.com CNAME +short",
"curl -sSI --max-time 15 https://coolify.onsethost.com/ | head -8",
"curl -sSI --max-time 15 https://www.exposedgays.com/ 2>&1 | head -8",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=40)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:3000])
c.close()

23
deploy/dns-trace.py Normal file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"dig @ns71.domaincontrol.com www.exposedgays.com ANY +noall +answer",
"dig @ns71.domaincontrol.com exposedgays.com ANY +noall +answer",
"dig +trace www.exposedgays.com A 2>&1 | tail -20",
"dig 03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com A +short",
"getent hosts www.exposedgays.com",
"resolvectl query www.exposedgays.com 2>/dev/null || systemd-resolve --status www.exposedgays.com 2>/dev/null | head -20",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=60)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:4000])
c.close()

View File

@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""Verify DNS, attempt tunnel route via API endpoints we can access, restart cloudflared."""
import json
import re
import time
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
ZID = "b9e032992b315331c8b0571473fedc89"
CF = "https://api.cloudflare.com/client/v4"
HOSTS = ["exposedgays.com", "www.exposedgays.com"]
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
def cf(path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:300]}
# Ensure DNS records are correct (re-upsert proxied CNAMEs)
print("=== CF DNS records ===")
code, recs = cf(f"/zones/{ZID}/dns_records?per_page=100")
records = recs.get("result", [])
for name in HOSTS:
body = {
"type": "CNAME",
"name": name,
"content": f"{TUNNEL}.cfargotunnel.com",
"ttl": 1,
"proxied": True,
}
existing = next((r for r in records if r["name"] == name and r["type"] in ("A", "CNAME")), None)
if existing:
code, res = cf(f"/zones/{ZID}/dns_records/{existing['id']}", "PATCH", body)
act = "PATCH"
else:
code, res = cf(f"/zones/{ZID}/dns_records", "POST", body)
act = "POST"
print(f" {act} {name}: HTTP {code} success={res.get('success')}")
# Try teamnet hostname route endpoints (some accounts expose these)
for path, method, body in [
(f"/accounts/{ACCOUNT}/teamnet/routes", "POST", {"network": "exposedgays.com", "tunnel_id": TUNNEL, "comment": "exposedgays apex"}),
(f"/accounts/{ACCOUNT}/teamnet/routes", "POST", {"network": "www.exposedgays.com", "tunnel_id": TUNNEL, "comment": "exposedgays www"}),
]:
code, res = cf(path, method, body)
print(f"\n{method} {path}: HTTP {code} success={res.get('success')} errors={res.get('errors')}")
# Restart cloudflared and verify
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
ssh.exec_command(f"echo '{PASS}' | sudo -S systemctl restart cloudflared")
time.sleep(12)
cmds = [
"dig @annabel.ns.cloudflare.com exposedgays.com A +short",
"dig @annabel.ns.cloudflare.com www.exposedgays.com A +short",
"sudo journalctl -u cloudflared --no-pager -n 4 | grep -o 'exposedgays' | head -3 || echo NO_EXPOSEDGAYS_IN_REMOTE",
"curl -sSI --max-time 25 https://exposedgays.com/ 2>&1 | head -15",
"curl -sSI --max-time 25 https://www.exposedgays.com/ 2>&1 | head -15",
"curl -sSL --max-time 25 https://www.exposedgays.com/ 2>&1 | head -c 400",
]
for cmd in cmds:
print(f"\n$ {cmd}")
_, o, e = ssh.exec_command(cmd, timeout=40)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:2500])
ssh.close()

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"docker ps --format '{{.Names}}' | grep -i onset | head -10",
"docker ps --format '{{.Names}}' | grep -i exposed | head -5",
f"echo '{PASS}' | sudo -S cat /etc/infra/secrets.env 2>/dev/null | grep -i cloudflare",
"grep -h CLOUDFLARE /home/localadministrator/coder/onsethost/.env.local /home/localadministrator/coder/onsethost/.env 2>/dev/null",
]
for cmd in cmds:
print("===", cmd, "===")
_, o, e = c.exec_command(cmd, timeout=60)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:5000])
print()
# Get onsethost container env
_, o, e = c.exec_command(
"docker ps --format '{{.Names}}' | grep -E 'onsethost|woeslw' | head -3", timeout=30
)
o.channel.recv_exit_status()
names = (o.read() + e.read()).decode().strip().splitlines()
for name in names:
if not name.strip():
continue
print(f"=== env {name} CLOUDFLARE ===")
_, o2, e2 = c.exec_command(f"docker exec {name.strip()} env 2>/dev/null | grep -i CLOUDFLARE", timeout=30)
o2.channel.recv_exit_status()
print((o2.read() + e2.read()).decode(errors="replace"))
c.close()

View File

@@ -0,0 +1,179 @@
#!/usr/bin/env python3
"""
Run AFTER adding exposedgays.com in Cloudflare dashboard.
Completes: tunnel public hostnames (if token allows), CF DNS CNAMEs, GoDaddy NS delegation.
"""
import json
import re
import sys
import time
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
DOMAIN = "exposedgays.com"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL_ID = "03079a26-f14c-4622-b463-ba54a24f7472"
TUNNEL_CNAME = f"{TUNNEL_ID}.cfargotunnel.com"
CF = "https://api.cloudflare.com/client/v4"
GD = "https://api.godaddy.com/v1"
GODADDY_KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
GODADDY_SECRET = "NvC3qexGAU7Rd5QExUTH6z"
EG_RULES = [
{"hostname": "exposedgays.com", "service": "https://localhost:443", "originRequest": {"noTLSVerify": True}},
{"hostname": "www.exposedgays.com", "service": "https://localhost:443", "originRequest": {"noTLSVerify": True}},
]
def load_cf_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def cf(tok, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=60) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read().decode())
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {GODADDY_KEY}:{GODADDY_SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:500]}
def upsert_tunnel_cnames(tok, zid):
records = cf(tok, f"/zones/{zid}/dns_records?per_page=100")[1].get("result", [])
for name in [DOMAIN, f"www.{DOMAIN}"]:
body = {"type": "CNAME", "name": name, "content": TUNNEL_CNAME, "ttl": 1, "proxied": True}
existing = next((r for r in records if r["name"] == name and r["type"] in ("A", "CNAME", "AAAA")), None)
if existing:
code, res = cf(tok, f"/zones/{zid}/dns_records/{existing['id']}", "PATCH", body)
action = "PATCH"
else:
code, res = cf(tok, f"/zones/{zid}/dns_records", "POST", body)
action = "POST"
print(f" {action} {name}: HTTP {code} success={res.get('success')} errors={res.get('errors')}")
def try_tunnel_ingress(tok):
code, body = cf(tok, f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL_ID}/configurations")
if code != 200 or not body.get("result"):
print("Tunnel config API blocked — add Public Hostnames manually in Zero Trust dashboard:")
print(" exposedgays.com -> https://localhost:443 (No TLS Verify)")
print(" www.exposedgays.com -> https://localhost:443 (No TLS Verify)")
return False
ingress = body["result"].get("config", {}).get("ingress", [])
hostnames = {i.get("hostname") for i in ingress if i.get("hostname")}
changed = False
for rule in EG_RULES:
if rule["hostname"] not in hostnames:
catch = next((i for i, x in enumerate(ingress) if not x.get("hostname")), len(ingress))
ingress.insert(catch, rule)
changed = True
print(" added tunnel ingress", rule["hostname"])
if not changed:
print(" tunnel ingress already has exposedgays hostnames")
return True
put_code, put = cf(
tok,
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL_ID}/configurations",
"PUT",
{"config": {"ingress": ingress, "warp-routing": {"enabled": False}}},
)
print(f" PUT tunnel config HTTP {put_code} success={put.get('success')} errors={put.get('errors')}")
if put.get("success"):
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
c.exec_command(f"echo '{PASS}' | sudo -S systemctl restart cloudflared")
c.close()
time.sleep(8)
return put.get("success", False)
def main():
tok = load_cf_token()
code, zres = cf(tok, f"/zones?name={DOMAIN}")
if not zres.get("result"):
print(f"BLOCKED: {DOMAIN} not in Cloudflare yet.")
print("Add it at https://dash.cloudflare.com → Add a Site → exposedgays.com (Free plan)")
print("Then re-run: python exposedgays/deploy/finish-after-cf-zone.py")
return 1
zone = zres["result"][0]
zid = zone["id"]
nameservers = zone.get("name_servers", [])
print(f"Found zone {zid} status={zone['status']}")
print(f"Nameservers: {nameservers}")
print("\n=== Tunnel ingress ===")
try_tunnel_ingress(tok)
print("\n=== Cloudflare DNS (proxied CNAME -> tunnel) ===")
upsert_tunnel_cnames(tok, zid)
if nameservers:
print("\n=== GoDaddy NS -> Cloudflare ===")
gcode, gres = gd(f"/domains/{DOMAIN}", "PATCH", {"nameServers": nameservers})
print(f"PATCH HTTP {gcode}: {gres}")
_, after = gd(f"/domains/{DOMAIN}")
print("GoDaddy NS now:", after.get("nameServers"))
print("\nWaiting 20s...")
time.sleep(20)
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for cmd in [
f"dig @1.1.1.1 {DOMAIN} A +short",
f"dig @1.1.1.1 www.{DOMAIN} A +short",
f"curl -sSI --max-time 20 https://{DOMAIN}/ | head -10",
f"curl -sSL --max-time 20 https://{DOMAIN}/ | head -c 200",
]:
print(f"\n$ {cmd}")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace"))
c.close()
return 0
if __name__ == "__main__":
raise SystemExit(main())

110
deploy/fix-dns-now.py Normal file
View File

@@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""Emergency DNS fix: revert GoDaddy NS + point records at tunnel."""
import json
import time
import urllib.error
import urllib.request
import paramiko
GODADDY_KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
GODADDY_SECRET = "NvC3qexGAU7Rd5QExUTH6z"
DOMAIN = "exposedgays.com"
TUNNEL_CNAME = "03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com"
GD_NS = ["ns71.domaincontrol.com", "ns72.domaincontrol.com"]
GD = "https://api.godaddy.com/v1"
PASS = "Bbt9115xty9176!"
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {GODADDY_KEY}:{GODADDY_SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:800]}
def main():
code, before = gd(f"/domains/{DOMAIN}")
print("Before NS:", before.get("nameServers") if code == 200 else before)
print("\n=== Revert registrar NS to GoDaddy ===")
code, res = gd(f"/domains/{DOMAIN}", "PATCH", {"nameServers": GD_NS})
print(f"PATCH NS HTTP {code}: {res}")
code, mid = gd(f"/domains/{DOMAIN}")
print("After NS revert:", mid.get("nameServers") if code == 200 else mid)
print("\n=== Set tunnel CNAME for www ===")
code, res = gd(
f"/domains/{DOMAIN}/records/CNAME/www",
"PUT",
[{"data": TUNNEL_CNAME, "ttl": 600}],
)
print(f"PUT www CNAME HTTP {code}: {res}")
print("\n=== Try apex CNAME (GoDaddy may reject) ===")
code, res = gd(
f"/domains/{DOMAIN}/records/CNAME/@",
"PUT",
[{"data": TUNNEL_CNAME, "ttl": 600}],
)
print(f"PUT @ CNAME HTTP {code}: {res}")
if code not in (200, 204):
print("\n=== Apex CNAME failed — enable domain forward @ -> www ===")
code, res = gd(
f"/domains/{DOMAIN}/forwarding",
"PATCH",
{
"forwarding": {
"type": "PERMANENT",
"url": f"https://www.{DOMAIN}",
"mask": False,
}
},
)
print(f"PATCH forwarding HTTP {code}: {res}")
print("\n=== Current GoDaddy records ===")
code, records = gd(f"/domains/{DOMAIN}/records")
for r in records if isinstance(records, list) else []:
print(f" {r['type']:5} {r['name']:20} -> {r.get('data')}")
print("\nWaiting 20s for propagation...")
time.sleep(20)
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for cmd in [
f"dig @1.1.1.1 {DOMAIN} NS +short",
f"dig @1.1.1.1 {DOMAIN} A +short",
f"dig @1.1.1.1 www.{DOMAIN} CNAME +short",
f"curl -sSI https://www.{DOMAIN}/ | head -12",
f"curl -sSL https://www.{DOMAIN}/ | head -c 300",
f"curl -sSI https://{DOMAIN}/ | head -12",
]:
print(f"\n$ {cmd}")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace"))
c.close()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Fix exposedgays.com 404: route via direct A record like thepinkpulse.com."""
import json
import re
import time
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ZID = "b9e032992b315331c8b0571473fedc89"
ORIGIN_IP = "68.248.192.234"
HOSTS = ["exposedgays.com", "www.exposedgays.com"]
CF = "https://api.cloudflare.com/client/v4"
def get_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def cf(token, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read().decode())
def ssh_check():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"curl -sSI --max-time 20 https://exposedgays.com/ | head -12",
"curl -sSI --max-time 20 https://www.exposedgays.com/ | head -12",
"curl -sSL --max-time 20 https://exposedgays.com/ | head -c 200",
]
out = []
for cmd in cmds:
_, o, e = c.exec_command(cmd, timeout=35)
o.channel.recv_exit_status()
out.append(f"$ {cmd}\n" + (o.read() + e.read()).decode(errors="replace")[:1500])
c.close()
return "\n\n".join(out)
def main():
token = get_token()
code, recs_body = cf(token, f"/zones/{ZID}/dns_records?per_page=100")
if not recs_body.get("success"):
raise SystemExit(f"list records failed: {recs_body.get('errors')}")
records = recs_body["result"]
for name in HOSTS:
body = {"type": "A", "name": name, "content": ORIGIN_IP, "ttl": 1, "proxied": True}
existing = next(
(r for r in records if r["name"] == name and r["type"] in ("A", "CNAME", "AAAA")),
None,
)
if existing:
code, res = cf(token, f"/zones/{ZID}/dns_records/{existing['id']}", "PATCH", body)
action = "PATCH"
else:
code, res = cf(token, f"/zones/{ZID}/dns_records", "POST", body)
action = "POST"
print(f"{action} {name}: HTTP {code} success={res.get('success')} errors={res.get('errors')}")
if not res.get("success"):
raise SystemExit(1)
print("\nFinal apex/www records:")
_, final_body = cf(token, f"/zones/{ZID}/dns_records?per_page=100")
for r in final_body.get("result", []):
if r["name"] in HOSTS:
print(f" {r['type']} {r['name']} -> {r['content']} proxied={r.get('proxied')}")
print("\nWaiting 15s for DNS/proxy propagation...")
time.sleep(15)
print("\n=== verify ===")
print(ssh_check())
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,117 @@
#!/usr/bin/env python3
"""Add exposedgays.com to Cloudflare tunnel remote ingress + verify DNS."""
import json
import re
import time
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL_ID = "03079a26-f14c-4622-b463-ba54a24f7472"
HOSTS = ["exposedgays.com", "www.exposedgays.com"]
RULES = [
{
"hostname": "exposedgays.com",
"service": "https://localhost:443",
"originRequest": {"noTLSVerify": True},
},
{
"hostname": "www.exposedgays.com",
"service": "https://localhost:443",
"originRequest": {"noTLSVerify": True},
},
]
def get_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def cf(token, path, method="GET", data=None):
req = urllib.request.Request(
f"https://api.cloudflare.com/client/v4{path}",
data=json.dumps(data).encode() if data is not None else None,
method=method,
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=60) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read().decode())
def ssh_run(cmd, timeout=90):
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = c.exec_command(cmd, timeout=timeout)
o.channel.recv_exit_status()
out = (o.read() + e.read()).decode(errors="replace")
c.close()
return out
def merge_ingress(ingress):
hostnames = {i.get("hostname") for i in ingress if i.get("hostname")}
changed = False
for rule in RULES:
if rule["hostname"] not in hostnames:
catch = next((i for i, x in enumerate(ingress) if not x.get("hostname")), len(ingress))
ingress.insert(catch, rule)
hostnames.add(rule["hostname"])
changed = True
print("added", rule["hostname"])
return ingress, changed
def main():
token = get_token()
code, body = cf(token, f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL_ID}/configurations")
print(f"GET tunnel config HTTP {code}")
if not body.get("result"):
print(body.get("errors"))
return 1
ingress = body["result"].get("config", {}).get("ingress", [])
present = [i.get("hostname") for i in ingress if i.get("hostname") in HOSTS]
print("exposedgays in remote ingress:", present)
ingress, changed = merge_ingress(ingress)
if changed:
put_code, put = cf(
token,
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL_ID}/configurations",
"PUT",
{"config": {"ingress": ingress, "warp-routing": {"enabled": False}}},
)
print(f"PUT HTTP {put_code} success={put.get('success')} errors={put.get('errors')}")
if not put.get("success"):
return 1
print(ssh_run(f"echo '{PASS}' | sudo -S systemctl restart cloudflared"))
time.sleep(8)
print("\n=== verify ===")
print(
ssh_run(
"systemctl is-active cloudflared; "
"dig @1.1.1.1 www.exposedgays.com CNAME +short; "
"curl -sSI --max-time 20 https://www.exposedgays.com/ | head -12; "
"curl -sSL --max-time 20 https://www.exposedgays.com/ | head -c 250"
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""Force GoDaddy registrar NS to Cloudflare."""
import json
import time
import urllib.error
import urllib.request
KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
SECRET = "NvC3qexGAU7Rd5QExUTH6z"
DOMAIN = "exposedgays.com"
CF_NS = ["annabel.ns.cloudflare.com", "mitchell.ns.cloudflare.com"]
GD = "https://api.godaddy.com/v1"
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {KEY}:{SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:500]}
code, d = gd(f"/domains/{DOMAIN}")
print("Before:", d.get("nameServers"))
# Try PUT instead of PATCH
code, res = gd(f"/domains/{DOMAIN}", "PATCH", {"nameServers": CF_NS, "locked": False})
print(f"PATCH HTTP {code}: {res}")
time.sleep(3)
code, d = gd(f"/domains/{DOMAIN}")
print("After:", d.get("nameServers"))
# Also try nameservers endpoint if exists
code, res = gd(f"/domains/{DOMAIN}/nameservers", "PUT", CF_NS)
print(f"PUT /nameservers HTTP {code}: {res}")
time.sleep(3)
code, d = gd(f"/domains/{DOMAIN}")
print("Final:", d.get("nameServers"))

View File

@@ -5,7 +5,13 @@ c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password="Bbt9115xty9176!", timeout=20)
_, o, e = c.exec_command(
"docker exec coolify-db psql -U coolify -d coolify -t -A -c "
"\"SELECT logs FROM application_deployment_queues WHERE id=1086;\""
"\"SELECT logs FROM application_deployment_queues WHERE id=1088;\""
)
print(o.read().decode()[-15000:])
text = o.read().decode()
# find error lines
for needle in ["Type error", "Failed to compile", "ERROR:", "error"]:
idx = text.rfind(needle)
if idx >= 0:
print(text[max(0, idx-500):idx+1500])
print("---")
c.close()

View File

@@ -0,0 +1,149 @@
#!/usr/bin/env python3
"""Point GoDaddy NS at Cloudflare (account default NS) + set CF DNS when zone exists."""
import json
import re
import time
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
DOMAIN = "exposedgays.com"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL_CNAME = "03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com"
# Same NS pair used by justtworoommates.com, thepinkpulse.com, freerangefruit.com on this CF account
CF_NS = ["annabel.ns.cloudflare.com", "mitchell.ns.cloudflare.com"]
CF = "https://api.cloudflare.com/client/v4"
GD = "https://api.godaddy.com/v1"
GODADDY_KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
GODADDY_SECRET = "NvC3qexGAU7Rd5QExUTH6z"
def load_cf_token():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
def cf(tok, path, method="GET", body=None):
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {GODADDY_KEY}:{GODADDY_SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:800]}
def find_zone(tok):
res = cf(tok, f"/zones?name={DOMAIN}")
return res["result"][0] if res.get("result") else None
def upsert_tunnel_cnames(tok, zid):
records = cf(tok, f"/zones/{zid}/dns_records?per_page=100").get("result", [])
for name in [DOMAIN, f"www.{DOMAIN}"]:
body = {"type": "CNAME", "name": name, "content": TUNNEL_CNAME, "ttl": 1, "proxied": True}
existing = next(
(r for r in records if r["name"] == name and r["type"] in ("A", "CNAME", "AAAA")),
None,
)
if existing:
res = cf(tok, f"/zones/{zid}/dns_records/{existing['id']}", "PATCH", body)
action = "PATCH"
else:
res = cf(tok, f"/zones/{zid}/dns_records", "POST", body)
action = "POST"
print(f" CF {action} {name}: success={res.get('success')} errors={res.get('errors')}")
def verify():
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for cmd in [
f"dig @1.1.1.1 {DOMAIN} NS +short",
f"dig @1.1.1.1 www.{DOMAIN} A +short",
f"curl -sSI https://www.{DOMAIN}/ | head -8",
f"curl -sSL https://www.{DOMAIN}/ | head -c 200",
]:
print(f"\n$ {cmd}")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace"))
c.close()
def main():
print("=== GoDaddy before ===")
code, before = gd(f"/domains/{DOMAIN}")
print(f"HTTP {code} NS={before.get('nameServers') if code == 200 else before}")
print(f"\n=== GoDaddy PATCH NS -> {CF_NS} ===")
code, res = gd(f"/domains/{DOMAIN}", "PATCH", {"nameServers": CF_NS})
print(f"HTTP {code}: {json.dumps(res, indent=2) if res else '{}'}")
code, after = gd(f"/domains/{DOMAIN}")
if code == 200:
print(f"GoDaddy after NS={after.get('nameServers')}")
tok = load_cf_token()
zone = find_zone(tok)
if not zone:
created = cf(
tok,
"/zones",
"POST",
{"name": DOMAIN, "account": {"id": ACCOUNT}, "type": "full", "jump_start": False},
)
if created.get("success"):
zone = created["result"]
print("\nCreated CF zone via API")
else:
print("\nCF zone not in account yet (API create still denied).")
print("Add exposedgays.com at https://dash.cloudflare.com — NS already points at CF.")
print("Then re-run: python exposedgays/deploy/delegate-godaddy-to-cf.py")
else:
print(f"\nCF zone found: {zone['id']} status={zone['status']}")
print("Setting tunnel CNAME records...")
upsert_tunnel_cnames(tok, zone["id"])
print("\nWaiting 20s...")
time.sleep(20)
verify()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""Point exposedgays.com DNS at Cloudflare tunnel via GoDaddy API."""
import json
import time
import urllib.error
import urllib.request
import paramiko
GODADDY_KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
GODADDY_SECRET = "NvC3qexGAU7Rd5QExUTH6z"
DOMAIN = "exposedgays.com"
TUNNEL_CNAME = "03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com"
GD = "https://api.godaddy.com/v1"
PASS = "Bbt9115xty9176!"
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {GODADDY_KEY}:{GODADDY_SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:800]}
def show_records(label):
print(f"\n=== {label} ===")
code, records = gd(f"/domains/{DOMAIN}/records")
print("HTTP", code)
for r in records if isinstance(records, list) else []:
print(f" {r['type']:5} {r['name']:20} -> {r.get('data')}")
def main():
show_records("Before")
# Full replace must retain NS records.
new_records = [
{"type": "NS", "name": "@", "data": "ns71.domaincontrol.com", "ttl": 3600},
{"type": "NS", "name": "@", "data": "ns72.domaincontrol.com", "ttl": 3600},
{"type": "CNAME", "name": "www", "data": TUNNEL_CNAME, "ttl": 600},
{"type": "CNAME", "name": "@", "data": TUNNEL_CNAME, "ttl": 600},
{
"type": "TXT",
"name": "_dmarc",
"data": "v=DMARC1; p=quarantine; adkim=r; aspf=r; rua=mailto:dmarc_rua@onsecureserver.net;",
"ttl": 3600,
},
]
print("\n=== PUT full record set ===")
code, res = gd(f"/domains/{DOMAIN}/records", "PUT", new_records)
print("HTTP", code, res)
if code not in (200, 204):
print("\n=== Per-record fallback ===")
# Delete parked apex A
code_del, res_del = gd(f"/domains/{DOMAIN}/records/A/@", "DELETE")
print("DELETE A @", code_del, res_del)
# Upsert www CNAME
code_www, res_www = gd(
f"/domains/{DOMAIN}/records/CNAME/www",
"PUT",
[{"data": TUNNEL_CNAME, "ttl": 600}],
)
print("PUT CNAME www", code_www, res_www)
# Try apex CNAME
code_apex, res_apex = gd(
f"/domains/{DOMAIN}/records/CNAME/@",
"PUT",
[{"data": TUNNEL_CNAME, "ttl": 600}],
)
print("PUT CNAME @", code_apex, res_apex)
show_records("After")
time.sleep(8)
print("\n=== Verify from COOLIFY_01 ===")
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
f"dig @1.1.1.1 {DOMAIN} CNAME +short",
f"dig @1.1.1.1 www.{DOMAIN} CNAME +short",
f"curl -sSI https://www.{DOMAIN}/ | head -12",
f"curl -sSL https://www.{DOMAIN}/ | head -c 400",
f"curl -sSI https://{DOMAIN}/ | head -12",
]
for cmd in cmds:
print(f"\n$ {cmd}")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace"))
c.close()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python3
"""Check GoDaddy domain status for exposedgays.com."""
import json
import urllib.error
import urllib.request
KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
SECRET = "NvC3qexGAU7Rd5QExUTH6z"
DOMAIN = "exposedgays.com"
BASE = "https://api.godaddy.com/v1"
def gd(path, method="GET", body=None):
req = urllib.request.Request(
BASE + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={
"Authorization": f"sso-key {KEY}:{SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
try:
return e.code, json.loads(e.read().decode())
except Exception:
return e.code, {"raw": e.read().decode()[:500]}
for path in [
f"/domains/{DOMAIN}",
f"/domains/{DOMAIN}/records",
f"/domains/{DOMAIN}/records/A",
f"/domains/{DOMAIN}/records/CNAME",
]:
code, res = gd(path)
print(f"\n{path} -> HTTP {code}")
print(json.dumps(res, indent=2)[:3000])

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""PATCH GoDaddy nameservers with consent payload."""
import json
import urllib.error
import urllib.request
from datetime import datetime, timezone
KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
SECRET = "NvC3qexGAU7Rd5QExUTH6z"
DOMAIN = "exposedgays.com"
CF_NS = ["annabel.ns.cloudflare.com", "mitchell.ns.cloudflare.com"]
GD = "https://api.godaddy.com/v1"
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {KEY}:{SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:1000]}
def show(label):
code, d = gd(f"/domains/{DOMAIN}")
print(f"{label}: HTTP {code} NS={d.get('nameServers') if code == 200 else d}")
show("Before")
bodies = [
{"nameServers": CF_NS},
{
"nameServers": CF_NS,
"consent": {
"agreedAt": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z"),
"agreedBy": "96.94.95.105",
"agreementKeys": ["DNRA"],
},
},
{
"nameServers": CF_NS,
"locked": False,
"consent": {
"agreedAt": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z"),
"agreedBy": "0.0.0.0",
"agreementKeys": ["DNRA", "DNPA"],
},
},
]
for i, body in enumerate(bodies, 1):
print(f"\n=== PATCH attempt {i} ===")
print(json.dumps(body, indent=2))
code, res = gd(f"/domains/{DOMAIN}", "PATCH", body)
print(f"HTTP {code}: {json.dumps(res, indent=2) if res else '{}'}")
show("After immediate")
import time
time.sleep(5)
show("After 5s")

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""Delegate exposedgays.com from GoDaddy to Cloudflare nameservers."""
import json
import re
import urllib.error
import urllib.request
GODADDY_KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
GODADDY_SECRET = "NvC3qexGAU7Rd5QExUTH6z"
DOMAIN = "exposedgays.com"
GD = "https://api.godaddy.com/v1"
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {GODADDY_KEY}:{GODADDY_SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:800]}
def main():
code, domain = gd(f"/domains/{DOMAIN}")
print(f"Domain GET HTTP {code}")
if code == 200:
print(f" Current NS: {domain.get('nameServers')}")
print(f" Status: {domain.get('status')}")
# Show what we'd set — caller passes NS via env or args in wrapper
import sys
if len(sys.argv) < 2:
print("\nUsage: godaddy-ns-to-cf.py <ns1> <ns2> [ns3...]")
return 1
nameservers = sys.argv[1:]
print(f"\nPATCH nameservers -> {nameservers}")
code, res = gd(f"/domains/{DOMAIN}", "PATCH", {"nameServers": nameservers})
print(f"HTTP {code}: {json.dumps(res, indent=2)}")
code, domain = gd(f"/domains/{DOMAIN}")
if code == 200:
print(f"\nAfter PATCH NS: {domain.get('nameServers')}")
return 0 if code == 200 else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
import json
import urllib.error
import urllib.request
KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
SECRET = "NvC3qexGAU7Rd5QExUTH6z"
DOMAIN = "exposedgays.com"
BASE = "https://api.godaddy.com/v1"
def gd(path, method="GET", body=None):
req = urllib.request.Request(
BASE + path,
data=json.dumps(body).encode() if body else None,
method=method,
headers={
"Authorization": f"sso-key {KEY}:{SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:500]}
paths = [
f"/domains/{DOMAIN}/connect",
f"/domains/{DOMAIN}/connect/cloudflare",
f"/domains/{DOMAIN}/suggest",
f"/domains/{DOMAIN}/records/NS/@",
]
for p in paths:
code, res = gd(p)
print(f"{p} -> {code}")
print(json.dumps(res, indent=2)[:800])
print()

View File

@@ -0,0 +1,31 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = c.exec_command(
"sudo journalctl -u cloudflared --no-pager -n 50 | grep 'Updated to new configuration' | tail -1",
timeout=30,
)
o.channel.recv_exit_status()
line = (o.read() + e.read()).decode()
print("exposedgays in remote:", "exposedgays" in line)
for h in ["exposedgays.com", "www.exposedgays.com", "thepinkpulse.com", "avbeat.com"]:
print(f" {h}: {h in line}")
# restart to pick up dashboard changes
_, o2, e2 = c.exec_command(f"echo '{PASS}' | sudo -S systemctl restart cloudflared && sleep 8 && sudo journalctl -u cloudflared --no-pager -n 8", timeout=60)
o2.channel.recv_exit_status()
out = (o2.read() + e2.read()).decode()
print("\nAfter restart:")
print("exposedgays:", "exposedgays" in out)
for cmd in [
"curl -sSI --max-time 25 https://exposedgays.com/ 2>&1 | head -15",
"curl -sSI --max-time 25 https://www.exposedgays.com/ 2>&1 | head -15",
"curl -sSL --max-time 25 https://www.exposedgays.com/ 2>&1 | head -c 350",
]:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=35)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:2000])
c.close()

25
deploy/list-cf-zones.py Normal file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env python3
import json
import re
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read() + e.read()).decode()).group(1)
c.close()
req = urllib.request.Request(
"https://api.cloudflare.com/client/v4/zones?per_page=50",
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=30) as r:
zones = json.load(r)["result"]
for z in sorted(zones, key=lambda x: x["name"]):
mark = " <--" if "exposed" in z["name"] or "gay" in z["name"] else ""
print(f"{z['name']:35} {z['status']:10} {z['id']}{mark}")

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python3
import paramiko
HOST = "10.10.0.10"
USER = "localadministrator"
PASS = "Bbt9115xty9176!"
APP = "ira4bw4nhbvm87o7s3fgqu6v"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(HOST, username=USER, password=PASS, timeout=20, allow_agent=False, look_for_keys=False)
def run(cmd, t=300):
print("$", cmd[:150])
_, o, e = c.exec_command(cmd, timeout=t)
code = o.channel.recv_exit_status()
out = (o.read() + e.read()).decode(errors="replace")
print(out[-6000:])
print("exit", code, "\n")
return code, out
# Move to localhost destination (id=0)
run(
"docker exec coolify-db psql -U coolify -d coolify -c "
"\"UPDATE applications SET destination_id=0 WHERE uuid='ira4bw4nhbvm87o7s3fgqu6v'; "
"SELECT destination_id,fqdn FROM applications WHERE uuid='ira4bw4nhbvm87o7s3fgqu6v';\""
)
run(
"TOKEN=$(cat /home/localadministrator/.coolify-token | tail -1); "
f"curl -sS -X POST 'http://127.0.0.1:8000/api/v1/deploy?uuid={APP}&force_rebuild=true' "
'-H "Authorization: Bearer $TOKEN"'
)
for i in range(120):
_, out = run("docker exec coolify php artisan queue:work --queue=high,default --once --timeout=900 2>&1", t=920)
if "ApplicationDeploymentJob" in out and ("DONE" in out or "FAIL" in out):
break
run("docker ps --format '{{.Names}} {{.Status}}' | grep ira4 || true")
run("curl -sS -o /dev/null -w '%{http_code}' -k -H 'Host: exposedgays.com' https://127.0.0.1/")
c.close()

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env python3
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
tokens = {}
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
tokens["pfsense"] = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1)
c.close()
c2 = paramiko.SSHClient()
c2.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c2.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for path in [
"/home/localadministrator/coder/onsethost/.env.local",
"/etc/infra/mail-watchdog.env",
"/etc/infra/secrets.env",
]:
_, o, e = c2.exec_command(
f"echo '{PASS}' | sudo -S grep -h CLOUDFLARE_API_TOKEN {path} 2>/dev/null | head -1",
timeout=20,
)
o.channel.recv_exit_status()
line = (o.read() + e.read()).decode().strip()
if "CLOUDFLARE_API_TOKEN=" in line:
tokens[path] = line.split("=", 1)[1].strip().strip('"')
c2.close()
tokens["hardcoded_mail"] = "cfut_zlUt5lAKVsu7qIcRCHJnyhAvpJVF5jx24HlrIEn9a8fcd6a1"
def probe(label, tok):
results = {}
for name, path, method in [
("zone_list", "/zones?per_page=1", "GET"),
("zone_create", "/zones", "POST"),
("tunnel_cfg", f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations", "GET"),
]:
body = None
if name == "zone_create":
body = {"name": "exposedgays.com", "account": {"id": ACCOUNT}, "type": "full"}
req = urllib.request.Request(
CF + path,
data=json.dumps(body).encode() if body else None,
method=method if name != "zone_list" else "GET",
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
data = json.load(r)
results[name] = ("OK", data.get("success"), len(data.get("result") or []))
except urllib.error.HTTPError as e:
err = json.loads(e.read().decode())
results[name] = (e.code, err.get("errors", [{}])[0].get("message", "")[:80])
print(label, results)
for label, tok in tokens.items():
if tok:
probe(label, tok)

10
deploy/read-cf-config.py Normal file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = c.exec_command("sudo cat /etc/cloudflared/config.yml", timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode())
c.close()

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for cmd in [
"sudo cat /etc/systemd/system/cloudflared.service",
"cloudflared --version",
"cloudflared tunnel run --help 2>&1 | head -40",
]:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:4000])
c.close()

9
deploy/read-tunnel.py Normal file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env python3
import paramiko
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password="Bbt9115xty9176!", timeout=20)
_, o, e = c.exec_command("sudo head -30 /etc/cloudflared/config.yml; echo '---'; sudo cat /etc/cloudflared/*.json 2>/dev/null | head -5; ls -la /etc/cloudflared/ 2>/dev/null", timeout=30)
print(o.read().decode())
print(e.read().decode())
c.close()

View File

@@ -1,16 +1,10 @@
#!/usr/bin/env python3
import io
import os
import tarfile
import paramiko
HOST = "10.10.0.10"
USER = "localadministrator"
PASS = "Bbt9115xty9176!"
GITEA_TOKEN = "5c323b8ea677d78b201341f6833d57156a607008"
APP = "ira4bw4nhbvm87o7s3fgqu6v"
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
GIT_AUTH = f"https://{USER}:{GITEA_TOKEN}@gitea.onsethost.com/localadministrator/exposedgays.git"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
@@ -22,58 +16,38 @@ def run(cmd, t=300):
_, o, e = c.exec_command(cmd, timeout=t)
code = o.channel.recv_exit_status()
out = (o.read() + e.read()).decode(errors="replace")
print(out[-10000:])
print(out[-5000:])
print("exit", code, "\n")
return code, out
# Upload updated tarball
buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode="w:gz") as tar:
for root, dirs, files in os.walk(PROJECT_ROOT):
dirs[:] = [d for d in dirs if d not in (".next", "node_modules", ".git")]
for f in files:
if f in (".env.local",):
continue
full = os.path.join(root, f)
arc = os.path.relpath(full, PROJECT_ROOT)
tar.add(full, arcname=arc)
buf.seek(0)
sftp = c.open_sftp()
sftp.putfo(buf, "/tmp/exposedgays.tar.gz")
sftp.close()
run("rm -rf /tmp/exposedgays && mkdir -p /tmp/exposedgays && tar -xzf /tmp/exposedgays.tar.gz -C /tmp/exposedgays")
run(
"cd /tmp/exposedgays && git add -A && "
"git commit -m 'Fix Dockerfile: npm install fallback when no lockfile' || true && "
f"git push origin main"
)
# Get latest deployment log
run(
f"TOKEN=$(cat /home/localadministrator/.coolify-token | tail -1); "
f"curl -sS 'http://127.0.0.1:8000/api/v1/applications/{APP}/deployments' "
f'-H "Authorization: Bearer $TOKEN" | python3 -c "import sys,json;d=json.load(sys.stdin); '
f'items=d if isinstance(d,list) else d.get(\\\"deployments\\\",d); '
f'[print(x.get(\\\"deployment_uuid\\\"),x.get(\\\"status\\\"),x.get(\\\"failure_reason\\\")) for x in items[:3]]" 2>/dev/null'
)
run(
"TOKEN=$(cat /home/localadministrator/.coolify-token | tail -1); "
f"curl -sS -X POST 'http://127.0.0.1:8000/api/v1/deploy?uuid={APP}&force_rebuild=true' "
'-H "Authorization: Bearer $TOKEN"'
)
for i in range(30):
success = False
for i in range(200):
_, out = run("docker exec coolify php artisan queue:work --queue=high,default --once --timeout=900 2>&1", t=920)
if "ApplicationDeploymentJob" in out and "DONE" in out:
print("DEPLOY DONE")
break
if "ApplicationDeploymentJob" in out and "FAIL" in out:
print("DEPLOY FAIL - fetching logs")
break
if "ApplicationDeploymentJob" in out:
if "DONE" in out:
success = True
print("=== DEPLOY SUCCESS ===")
break
if "FAIL" in out:
print("=== DEPLOY FAILED ===")
break
run("docker ps --format '{{.Names}} {{.Status}}' | grep -i ira4 || docker ps --format '{{.Names}} {{.Status}}' | grep -i exposed || true")
run(
"docker exec coolify-db psql -U coolify -d coolify -c "
"\"SELECT id,status FROM application_deployment_queues WHERE application_id='31' ORDER BY id DESC LIMIT 1;\""
)
run(
"docker exec coolify-db psql -U coolify -d coolify -c "
"\"SELECT status,name FROM applications WHERE uuid='ira4bw4nhbvm87o7s3fgqu6v';\""
)
run("ssh -o StrictHostKeyChecking=no localadministrator@10.10.0.115 'docker ps --format \"{{.Names}} {{.Status}}\" | grep ira4' 2>&1")
c.close()
print("SUCCESS" if success else "CHECK_LOGS")

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""Force GoDaddy nameservers back to domaincontrol."""
import json
import time
import urllib.error
import urllib.request
from datetime import datetime, timezone
KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo"
SECRET = "NvC3qexGAU7Rd5QExUTH6z"
DOMAIN = "exposedgays.com"
GD_NS = ["ns71.domaincontrol.com", "ns72.domaincontrol.com"]
GD = "https://api.godaddy.com/v1"
def gd(path, method="GET", body=None):
req = urllib.request.Request(
GD + path,
data=json.dumps(body).encode() if body is not None else None,
method=method,
headers={
"Authorization": f"sso-key {KEY}:{SECRET}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
raw = r.read().decode()
return r.status, json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
raw = e.read().decode()
try:
return e.code, json.loads(raw)
except Exception:
return e.code, {"raw": raw[:800]}
def show(label):
code, d = gd(f"/domains/{DOMAIN}")
print(f"{label}: HTTP {code} NS={d.get('nameServers') if code == 200 else d}")
show("Before")
bodies = [
{"nameServers": GD_NS},
{
"nameServers": GD_NS,
"consent": {
"agreedAt": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z"),
"agreedBy": "96.94.95.105",
"agreementKeys": ["DNRA"],
},
},
]
for i, body in enumerate(bodies, 1):
print(f"\nPATCH attempt {i}: {body}")
code, res = gd(f"/domains/{DOMAIN}", "PATCH", body)
print(f"HTTP {code}: {res}")
show(f"After attempt {i}")
if code in (200, 204):
ns = gd(f"/domains/{DOMAIN}")[1].get("nameServers", [])
if ns == GD_NS:
print("SUCCESS: NS reverted to GoDaddy")
break
time.sleep(3)

View File

@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Find a CF token that can edit tunnel ingress."""
import json
import re
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
tokens = set()
for host, user in [("10.10.0.1", "admin"), ("10.10.0.10", "localadministrator")]:
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(host, username=user, password=PASS, timeout=15)
cmd = (
f"echo '{PASS}' | sudo -S bash -c \"grep -rho 'cfut_[A-Za-z0-9_-]{{20,}}' "
"/etc/infra /etc/cloudflared /root /home/localadministrator 2>/dev/null | sort -u\""
if host == "10.10.0.10"
else "grep -o 'cfut_[A-Za-z0-9_-]\\{20,\\}' /usr/local/etc/wan-dns-failover.env"
)
_, o, e = c.exec_command(cmd, timeout=120)
o.channel.recv_exit_status()
for line in (o.read() + e.read()).decode().splitlines():
if line.startswith("cfut_"):
tokens.add(line.strip())
c.close()
print(f"Scanning {len(tokens)} tokens for tunnel config access...")
def probe(tok):
req = urllib.request.Request(
f"{CF}/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations",
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
data = json.load(r)
ingress = data.get("result", {}).get("config", {}).get("ingress", [])
eg = [i.get("hostname") for i in ingress if i.get("hostname") and "exposedgays" in i.get("hostname", "")]
return "OK", len(ingress), eg
except urllib.error.HTTPError as e:
err = json.loads(e.read().decode())
return e.code, err.get("errors", [{}])[0].get("message", "")[:60], []
for tok in sorted(tokens):
status, count, eg = probe(tok)
print(f"{tok[:20]}... -> {status} {count} exposedgays={eg}")

View File

@@ -0,0 +1,159 @@
#!/usr/bin/env python3
"""Add exposedgays.com to Cloudflare + tunnel CNAME records."""
import json
import re
import time
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
DOMAIN = "exposedgays.com"
TUNNEL_CNAME = "03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com"
CF_API = "https://api.cloudflare.com/client/v4"
ACCOUNT_ID = "2599c23bbb1255dbb73e8d34b4115fda"
TOKENS = []
def load_tokens():
# pfSense production token
try:
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
text = (o.read() + e.read()).decode()
c.close()
m = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text)
if m:
TOKENS.append(("pfsense", m.group(1)))
except Exception as ex:
print("pfsense token:", ex)
# onsethost env fallback
TOKENS.append(("apply_mail", "cfut_zlUt5lAKVsu7qIcRCHJnyhAvpJVF5jx24HlrIEn9a8fcd6a1"))
TOKENS.append(("onsethost", "cfut_IOm1DJW4pDkPCuQA6UoOjqO6Tcv2zU10y2PSw82m379f7db3"))
def cf(token, path, method="GET", data=None):
req = urllib.request.Request(
f"{CF_API}{path}",
data=json.dumps(data).encode() if data is not None else None,
method=method,
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=45) as r:
return json.load(r)
except urllib.error.HTTPError as e:
return json.loads(e.read().decode())
def find_zone(token):
for q in [f"name={DOMAIN}", f"name={DOMAIN}&status=active", f"name={DOMAIN}&status=pending"]:
res = cf(token, f"/zones?{q}")
if res.get("result"):
return res["result"][0]
# paginate all zones
page = 1
while page <= 10:
res = cf(token, f"/zones?per_page=50&page={page}")
for z in res.get("result", []):
if z["name"] == DOMAIN:
return z
if len(res.get("result", [])) < 50:
break
page += 1
return None
def create_zone(token):
return cf(
token,
"/zones",
"POST",
{"name": DOMAIN, "account": {"id": ACCOUNT_ID}, "type": "full", "jump_start": False},
)
def list_records(token, zid):
page, out = 1, []
while True:
res = cf(token, f"/zones/{zid}/dns_records?per_page=100&page={page}")
batch = res.get("result", [])
out.extend(batch)
if len(batch) < 100:
break
page += 1
return out
def upsert_tunnel_cname(token, zid, existing, name):
body = {"type": "CNAME", "name": name, "content": TUNNEL_CNAME, "ttl": 1, "proxied": True}
if existing:
if existing["type"] == "CNAME" and TUNNEL_CNAME in existing.get("content", "") and existing.get("proxied"):
print(f"OK {name} already on tunnel")
return True
res = cf(token, f"/zones/{zid}/dns_records/{existing['id']}", "PATCH", body)
action = "PATCH"
else:
res = cf(token, f"/zones/{zid}/dns_records", "POST", body)
action = "POST"
print(f"{action} {name}: success={res.get('success')} errors={res.get('errors')}")
return res.get("success")
def main():
load_tokens()
token = None
zone = None
for label, tok in TOKENS:
z = find_zone(tok)
print(f"[{label}] zone lookup:", "found" if z else "not found")
if z:
token, zone = tok, z
print(f" using {label} token, zone {z['id']} status={z['status']}")
break
if not zone:
for label, tok in TOKENS:
res = create_zone(tok)
print(f"[{label}] create zone:", res.get("success"), res.get("errors"))
if res.get("success"):
token, zone = tok, res["result"]
break
if not zone:
raise RuntimeError("Could not find or create exposedgays.com zone with available tokens")
zid = zone["id"]
nameservers = zone.get("name_servers", [])
status = zone.get("status")
records = list_records(token, zid)
apex = next((r for r in records if r["name"] == DOMAIN and r["type"] in ("A", "CNAME", "AAAA")), None)
www = next((r for r in records if r["name"] == f"www.{DOMAIN}" and r["type"] in ("A", "CNAME", "AAAA")), None)
print(f"Before apex: {apex and (apex['type'], apex.get('content'))}")
print(f"Before www: {www and (www['type'], www.get('content'))}")
upsert_tunnel_cname(token, zid, apex, DOMAIN)
upsert_tunnel_cname(token, zid, www, f"www.{DOMAIN}")
print("\n=== Cloudflare nameservers ===")
for ns in nameservers:
print(f" {ns}")
print(f"Zone status: {status}")
if status != "active":
print("\n>>> Update GoDaddy NS to the Cloudflare nameservers above <<<")
time.sleep(5)
for r in list_records(token, zid):
if "exposedgays" in r["name"]:
print(f"Final: {r['type']} {r['name']} -> {r['content']} proxied={r.get('proxied')}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for url in ["https://exposedgays.com/", "https://www.exposedgays.com/", "https://thepinkpulse.com/"]:
cmd = f"echo '{PASS}' | sudo -S cloudflared tunnel ingress rule '{url}' --config /etc/cloudflared/config.yml 2>&1"
print(f"\n=== {url} ===")
_, o, e = c.exec_command(cmd, timeout=20)
o.channel.recv_exit_status()
print((o.read()+e.read()).decode())
c.close()

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"curl -sSI --max-time 10 -k -H 'Host: exposedgays.onsethost.com' https://127.0.0.1/ | head -8",
"curl -sSI --max-time 10 https://exposedgays.onsethost.com/ 2>&1 | head -8",
"dig @1.1.1.1 exposedgays.onsethost.com A +short",
]
for cmd in cmds:
print(f"\n=== {cmd} ===")
_, o, e = c.exec_command(cmd, timeout=20)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode())
c.close()

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
import json
import urllib.error
import urllib.request
import paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
_, o, e = c.exec_command(
"grep CLOUDFLARE_API_TOKEN /home/localadministrator/coder/onsethost/.env.local",
timeout=20,
)
o.channel.recv_exit_status()
line = (o.read() + e.read()).decode().strip()
tok = line.split("=", 1)[1].strip()
c.close()
print("token prefix:", tok[:12], "len:", len(tok))
for path in [
"/user/tokens/verify",
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations",
"/zones?name=exposedgays.com",
]:
req = urllib.request.Request(
f"https://api.cloudflare.com/client/v4{path}",
headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
data = json.load(r)
print(path, "OK", data.get("success"), "errors=", data.get("errors"))
except urllib.error.HTTPError as e:
err = json.loads(e.read().decode())
print(path, e.code, err.get("errors"))

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env python3
import json, re, urllib.request, urllib.error, paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient(); c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read()+e.read()).decode()).group(1)
c.close()
def call(path, method="GET", body=None):
req = urllib.request.Request(CF+path, data=json.dumps(body).encode() if body else None, method=method,
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=30) as r: return r.status, json.load(r)
except urllib.error.HTTPError as e: return e.code, json.loads(e.read().decode())
for path in ["/accounts", f"/accounts/{ACCOUNT}/cfd_tunnel", f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/configurations"]:
code, res = call(path)
print(path, code, "success=", res.get("success"), "errors=", res.get("errors"))
if res.get("result") and path.endswith("configurations"):
ing = res["result"].get("config", {}).get("ingress", [])
print(" ingress count", len(ing))
print(" exposedgays", [i.get("hostname") for i in ing if i.get("hostname") and "exposed" in i.get("hostname","")])

40
deploy/try-cf-graphql.py Normal file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
import json, re, urllib.request, paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
c = paramiko.SSHClient(); c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read()+e.read()).decode()).group(1)
c.close()
query = """
query {
viewer {
accounts(filter: {accountTag: "%s"}) {
tunnels(filter: {uuid: "%s"}) {
id
name
config {
ingress {
hostname
service
}
}
}
}
}
}
""" % (ACCOUNT, TUNNEL)
req = urllib.request.Request(
"https://api.cloudflare.com/client/v4/graphql",
data=json.dumps({"query": query}).encode(),
method="POST",
headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=30) as r:
print(json.dumps(json.load(r), indent=2)[:5000])

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CREDS = f"/etc/cloudflared/{TUNNEL}.json"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
"cloudflared tunnel ingress --help 2>&1 | head -30",
f"echo '{PASS}' | sudo -S cloudflared tunnel --cred-file {CREDS} ingress validate /etc/cloudflared/config.yml 2>&1",
f"echo '{PASS}' | sudo -S cloudflared tunnel --cred-file {CREDS} ingress rule list 2>&1 | head -20",
"cloudflared tunnel --help 2>&1 | grep -i 'local\\|remote\\|config' | head -15",
]
for cmd in cmds:
print(f"\n=== {cmd[:85]} ===")
_, o, e = c.exec_command(cmd, timeout=60)
o.channel.recv_exit_status()
print((o.read()+e.read()).decode(errors="replace")[:3000])
c.close()

24
deploy/try-route-dns.py Normal file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CREDS = f"/etc/cloudflared/{TUNNEL}.json"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
cmds = [
f"echo '{PASS}' | sudo -S cloudflared tunnel --cred-file {CREDS} route dns --help 2>&1 | head -25",
f"echo '{PASS}' | sudo -S cloudflared tunnel --cred-file {CREDS} route dns {TUNNEL} www.exposedgays.com 2>&1",
f"echo '{PASS}' | sudo -S cloudflared tunnel --cred-file {CREDS} route dns {TUNNEL} exposedgays.com 2>&1",
"dig @1.1.1.1 www.exposedgays.com CNAME +short",
"curl -sSI --max-time 15 https://www.exposedgays.com/ | head -10",
]
for cmd in cmds:
print(f"\n=== {cmd[:90]} ===")
_, o, e = c.exec_command(cmd, timeout=60)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode(errors="replace")[:3000])
c.close()

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env python3
import json, re, urllib.request, urllib.error, paramiko
PASS = "Bbt9115xty9176!"
ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda"
TUNNEL = "03079a26-f14c-4622-b463-ba54a24f7472"
CF = "https://api.cloudflare.com/client/v4"
c = paramiko.SSHClient(); c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.1", username="admin", password=PASS, timeout=15)
_, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30)
o.channel.recv_exit_status()
TOK = re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", (o.read()+e.read()).decode()).group(1)
c.close()
endpoints = [
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/token",
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/connections",
f"/accounts/{ACCOUNT}/cfd_tunnel/{TUNNEL}/routes?per_page=50",
f"/accounts/{ACCOUNT}/teamnet/routes?per_page=50",
f"/accounts/{ACCOUNT}/teamnet/virtual_networks",
]
for path in endpoints:
req = urllib.request.Request(CF+path, headers={"Authorization": f"Bearer {TOK}", "Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=30) as r:
data = json.load(r)
print(path, "OK", json.dumps(data)[:400])
except urllib.error.HTTPError as e:
body = e.read().decode()
print(path, e.code, body[:200])

18
deploy/verify-ns.py Normal file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env python3
import paramiko
PASS = "Bbt9115xty9176!"
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30)
for cmd in [
"dig @1.1.1.1 exposedgays.com NS +short",
"dig @8.8.8.8 exposedgays.com NS +short",
"dig @ns71.domaincontrol.com exposedgays.com NS +short 2>&1 | head -5",
]:
print(f"$ {cmd}")
_, o, e = c.exec_command(cmd, timeout=30)
o.channel.recv_exit_status()
print((o.read() + e.read()).decode())
print()
c.close()

21
deploy/verify.py Normal file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env python3
import paramiko
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect("10.10.0.10", username="localadministrator", password="Bbt9115xty9176!", timeout=20)
cmds = [
"curl -sS -o /dev/null -w '%{http_code}' -k -H 'Host: exposedgays.com' https://127.0.0.1/",
"curl -sS -o /dev/null -w '%{http_code}' http://10.10.0.115:3000/ 2>/dev/null || echo no_direct",
"ssh -o StrictHostKeyChecking=no localadministrator@10.10.0.115 'docker inspect ira4bw4nhbvm87o7s3fgqu6v-214624968606 --format \"{{json .Config.Labels}}\"' 2>&1 | head -c 2000",
"docker exec coolify-db psql -U coolify -d coolify -t -A -c \"SELECT fqdn,status FROM applications WHERE uuid='ira4bw4nhbvm87o7s3fgqu6v';\"",
]
for cmd in cmds:
print("$", cmd[:120])
_, o, e = c.exec_command(cmd, timeout=30)
print(o.read().decode()[:2500])
print()
c.close()

24
public/logo.svg Normal file
View File

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" fill="none">
<defs>
<linearGradient id="eg-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ff2d6b"/>
<stop offset="50%" stop-color="#8b2fc9"/>
<stop offset="100%" stop-color="#ff2d6b"/>
</linearGradient>
<linearGradient id="eg-heart" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ff6b9d"/>
<stop offset="100%" stop-color="#ff2d6b"/>
</linearGradient>
</defs>
<circle cx="60" cy="60" r="56" fill="#1a1220" stroke="url(#eg-grad)" stroke-width="3"/>
<circle cx="60" cy="60" r="44" fill="url(#eg-grad)" opacity="0.15"/>
<!-- Cute heart with sparkle -->
<path d="M60 88 C60 88 28 68 28 48 C28 36 36 30 44 30 C50 30 56 34 60 40 C64 34 70 30 76 30 C84 30 92 36 92 48 C92 68 60 88 60 88Z" fill="url(#eg-heart)"/>
<path d="M48 42 C48 38 52 36 54 38" stroke="#fff" stroke-width="2.5" stroke-linecap="round" opacity="0.6"/>
<!-- EG monogram -->
<text x="60" y="72" text-anchor="middle" font-family="system-ui, sans-serif" font-weight="800" font-size="18" fill="#fff" opacity="0.9">EG</text>
<!-- Sparkle stars -->
<circle cx="88" cy="28" r="3" fill="#fff" opacity="0.9"/>
<circle cx="32" cy="32" r="2" fill="#fff" opacity="0.6"/>
<path d="M88 22 L89 26 L93 27 L89 28 L88 32 L87 28 L83 27 L87 26 Z" fill="#ffd700"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -6,17 +6,19 @@
"display": "standalone",
"background_color": "#0d0a0f",
"theme_color": "#ff2d6b",
"orientation": "portrait-primary",
"orientation": "any",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
"src": "/logo.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "any"
},
{
"src": "/icon-512.png",
"src": "/logo.svg",
"sizes": "512x512",
"type": "image/png"
"type": "image/svg+xml",
"purpose": "maskable"
}
]
}

View File

@@ -9,7 +9,7 @@ function ChatContent() {
const userId = searchParams.get("user");
return (
<div className="h-[calc(100vh-3.5rem)] md:h-[calc(100vh-3.5rem)]">
<div className="h-[calc(100dvh-7rem)] md:h-[calc(100dvh-3.5rem)]">
<ChatPanel dmUserId={userId} />
</div>
);

View File

@@ -92,5 +92,41 @@
/* PWA safe areas */
.safe-bottom {
padding-bottom: env(safe-area-inset-bottom, 0);
padding-bottom: max(env(safe-area-inset-bottom, 0px), 4px);
}
.safe-top {
padding-top: env(safe-area-inset-top, 0);
}
/* ID verification blur — nude photos in restricted states */
.id-blur img,
.id-blur .explicit-content {
filter: blur(28px) !important;
}
.id-blur:hover img,
.id-blur:hover .explicit-content {
filter: blur(20px) !important;
}
/* Mobile touch targets & scroll */
.touch-manipulation {
touch-action: manipulation;
}
@supports (height: 100dvh) {
.min-h-screen {
min-height: 100dvh;
}
}
/* Prevent iOS zoom on input focus */
@media (max-width: 767px) {
input, select, textarea {
font-size: 16px !important;
}
}
/* Overscroll containment for chat */
.overscroll-contain {
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
}

View File

@@ -10,6 +10,10 @@ export const metadata: Metadata = {
description:
"100% free gay cruising map, chat, spots directory, and sex shop. No subscriptions ever.",
manifest: "/manifest.json",
icons: {
icon: "/logo.svg",
apple: "/logo.svg",
},
appleWebApp: {
capable: true,
statusBarStyle: "black-translucent",
@@ -21,7 +25,7 @@ export const viewport: Viewport = {
themeColor: "#ff2d6b",
width: "device-width",
initialScale: 1,
maximumScale: 1,
viewportFit: "cover",
};
export default function RootLayout({

Some files were not shown because too many files have changed in this diff Show More