Autonomous Publishing System — full stack: orchestrator, 8 vertical sites, admin dashboard, analytics, cron pipeline

This commit is contained in:
drjones
2026-08-03 21:44:26 -07:00
commit 493776b9f8
16 changed files with 2962 additions and 0 deletions

184
core/cloudflare_setup.py Normal file
View 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}")