Autonomous Publishing System — full stack: orchestrator, 8 vertical sites, admin dashboard, analytics, cron pipeline
This commit is contained in:
184
core/cloudflare_setup.py
Normal file
184
core/cloudflare_setup.py
Normal file
@@ -0,0 +1,184 @@
|
||||
"""
|
||||
Wire auto-publisher sites to Cloudflare home tunnel.
|
||||
Adds ingress rules to the existing home tunnel for each site subdomain.
|
||||
"""
|
||||
import json
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
# Home tunnel ID (the existing tunnel that routes ~38 services)
|
||||
HOME_TUNNEL_ID = "d2871458-1737-4844-b114-9a44b9d71e25"
|
||||
ACCT_ID = "895479ab3540daa6d61ae702a5164475"
|
||||
ZONE_ID = "93634d23ff8138fbd795763b681158ac"
|
||||
DOMAIN = "thetempleofdoom.com"
|
||||
|
||||
# Site IPs
|
||||
SITES = {
|
||||
"ai": "10.30.20.240",
|
||||
"tech": "10.30.20.241",
|
||||
"science": "10.30.20.242",
|
||||
"crypto": "10.30.20.243",
|
||||
"linux": "10.30.20.244",
|
||||
"gaming": "10.30.20.246",
|
||||
"diy": "10.30.20.247",
|
||||
"guides": "10.30.20.248",
|
||||
}
|
||||
|
||||
|
||||
def get_home_tunnel_config():
|
||||
"""Get current home tunnel ingress configuration."""
|
||||
result = subprocess.run(
|
||||
["cloudflared", "tunnel", "info", "--output", "json", HOME_TUNNEL_ID],
|
||||
capture_output=True, text=True, timeout=10
|
||||
)
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
return json.loads(result.stdout)
|
||||
return None
|
||||
|
||||
|
||||
def add_ingress_rules():
|
||||
"""Add ingress rules for all 8 publisher sites to the home tunnel."""
|
||||
print("Adding Cloudflare tunnel ingress rules...")
|
||||
|
||||
for name, ip in SITES.items():
|
||||
hostname = f"{name}.{DOMAIN}"
|
||||
|
||||
# Check if DNS record already exists
|
||||
print(f"\n {hostname}:")
|
||||
|
||||
# Use cloudflared CLI to add route
|
||||
cmd = [
|
||||
"cloudflared", "tunnel", "route", "dns",
|
||||
"--overwrite-dns",
|
||||
HOME_TUNNEL_ID, hostname
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
||||
if result.returncode == 0:
|
||||
print(f" ✅ DNS route created: {hostname} → tunnel")
|
||||
else:
|
||||
error = result.stderr.strip()
|
||||
if "already exists" in error.lower() or "conflict" in error.lower():
|
||||
print(f" ⚠️ DNS already exists: {hostname}")
|
||||
else:
|
||||
print(f" ❌ Failed: {error[:200]}")
|
||||
# Fallback: manual DNS via API
|
||||
_add_dns_manual(name, hostname)
|
||||
|
||||
# Add local ingress mapping using config file approach
|
||||
_add_config_ingress(name, hostname, ip)
|
||||
|
||||
|
||||
def _add_dns_manual(name, hostname):
|
||||
"""Manual DNS CNAME creation via API."""
|
||||
# This requires a working CF token
|
||||
CF_TOKEN = "cfat_M62Ke6eLXPZnb26qW4FCRL4T4h8l4KrRVLFIRWad9455c3e0"
|
||||
|
||||
result = subprocess.run([
|
||||
"curl", "-s", "-X", "POST",
|
||||
f"https://api.cloudflare.com/client/v4/zones/{ZONE_ID}/dns_records",
|
||||
"-H", f"Authorization: Bearer {CF_TOKEN}",
|
||||
"-H", "Content-Type: application/json",
|
||||
"-d", json.dumps({
|
||||
"type": "CNAME",
|
||||
"name": name,
|
||||
"content": f"{HOME_TUNNEL_ID}.cfargotunnel.com",
|
||||
"proxied": True,
|
||||
"ttl": 1,
|
||||
}),
|
||||
], capture_output=True, text=True, timeout=15)
|
||||
|
||||
try:
|
||||
resp = json.loads(result.stdout)
|
||||
if resp.get("success"):
|
||||
print(f" ✅ Manual DNS created: {hostname}")
|
||||
else:
|
||||
errors = resp.get("errors", [])
|
||||
for e in errors:
|
||||
if "already exists" in str(e).lower() or e.get("code") == 81053:
|
||||
print(f" ⚠️ DNS already exists: {hostname}")
|
||||
return
|
||||
print(f" ❌ Manual DNS failed: {errors}")
|
||||
except Exception:
|
||||
print(f" ❌ API error: {result.stdout[:200]}")
|
||||
|
||||
|
||||
def _add_config_ingress(name, hostname, ip):
|
||||
"""Add ingress rule to cloudflared config file."""
|
||||
config_path = "/usr/local/etc/cloudflared/config.yml"
|
||||
|
||||
# Check if cloudflared is running locally
|
||||
result = subprocess.run(
|
||||
["pgrep", "-f", "cloudflared"], capture_output=True, text=True
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f" ℹ️ cloudflared not running locally — tunnel is on Proxmox CT680")
|
||||
print(f" ℹ️ Add manually: cloudflared tunnel route dns {HOME_TUNNEL_ID} {hostname}")
|
||||
return
|
||||
|
||||
# Read existing config
|
||||
try:
|
||||
with open(config_path) as f:
|
||||
config = f.read()
|
||||
except FileNotFoundError:
|
||||
print(f" ℹ️ No local cloudflared config — tunnel managed elsewhere")
|
||||
return
|
||||
|
||||
# Check if entry exists
|
||||
if f"hostname: {hostname}" in config:
|
||||
print(f" ⚠️ Ingress already in config for {hostname}")
|
||||
return
|
||||
|
||||
# Add ingress rule before the catch-all
|
||||
new_rule = f"""
|
||||
- hostname: {hostname}
|
||||
service: http://{ip}:80"""
|
||||
|
||||
if "service: http_status:404" in config:
|
||||
config = config.replace(
|
||||
"service: http_status:404",
|
||||
f"{new_rule}\n - service: http_status:404"
|
||||
)
|
||||
|
||||
with open(config_path, "w") as f:
|
||||
f.write(config)
|
||||
|
||||
print(f" ✅ Ingress added locally for {hostname}")
|
||||
|
||||
# Restart cloudflared
|
||||
subprocess.run(["brew", "services", "restart", "cloudflared"],
|
||||
capture_output=True, timeout=10)
|
||||
print(f" ✅ cloudflared restarted")
|
||||
|
||||
|
||||
def verify_all():
|
||||
"""Verify all sites are accessible via Cloudflare tunnel."""
|
||||
print("\n\nVerifying all sites...")
|
||||
time.sleep(5)
|
||||
|
||||
for name in SITES:
|
||||
url = f"https://{name}.{DOMAIN}"
|
||||
result = subprocess.run(
|
||||
["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", url],
|
||||
capture_output=True, text=True, timeout=10
|
||||
)
|
||||
code = result.stdout.strip()
|
||||
status = "✅" if code in ("200", "301", "302") else "❌"
|
||||
print(f" {status} {url} → HTTP {code}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("CLOUDFLARE TUNNEL SETUP — Auto Publisher Sites")
|
||||
print("=" * 60)
|
||||
print(f"\nHome Tunnel: {HOME_TUNNEL_ID}")
|
||||
print(f"Domain: {DOMAIN}")
|
||||
print()
|
||||
|
||||
add_ingress_rules()
|
||||
verify_all()
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print("TUNNEL SETUP COMPLETE")
|
||||
print(f"{'='*60}")
|
||||
Reference in New Issue
Block a user