refactor: move cloudflare domain list to dynamic state file

This commit is contained in:
Fredrick Amnehagen 2026-02-05 19:17:54 +01:00
parent 837bafba09
commit 064144134e
3 changed files with 74 additions and 11 deletions

View file

@ -62,10 +62,18 @@ infra dns add-host "aa:bb:cc:dd:ee:ff" "10.32.70.100" "new-app"
infra dns add-dns "api.loopaware.com" "10.32.70.100"
```
### 4. Public Ingress (HAProxy)
### 4. Cloudflare DDNS
The list of domains to update is managed dynamically on the server.
```bash
# Expose the service to the internet
infra ingress add "app.loopaware.com" "10.32.70.100" 80
# Add a domain to the update list
infra cloudflare add-ddns "my-new-domain.com"
# List all domains being updated
infra cloudflare list-ddns
# Run the update (usually via cron)
infra cloudflare update-ddns
```
## Advanced Workflows for AI Agents

View file

@ -1,16 +1,51 @@
import requests
import sys
import json
import os
class CloudflareManager:
def __init__(self, config):
self.token = config.get('cloudflare.token')
self.ddns_domains = config.get('cloudflare.ddns_domains', [])
# Path to store the dynamic list of domains
self.state_file = config.get('cloudflare.ddns_state_file', '/etc/haproxy/dynamic/ddns_domains.json')
self.api_url = "https://api.cloudflare.com/client/v4"
self.headers = {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
def _load_domains(self):
if not os.path.exists(self.state_file):
return []
try:
with open(self.state_file, 'r') as f:
return json.load(f)
except (json.JSONDecodeError, IOError):
return []
def _save_domains(self, domains):
os.makedirs(os.path.dirname(self.state_file), exist_ok=True)
with open(self.state_file, 'w') as f:
json.dump(list(set(domains)), f, indent=4)
def add_domain(self, domain):
domains = self._load_domains()
if domain not in domains:
domains.append(domain)
self._save_domains(domains)
return True
return False
def remove_domain(self, domain):
domains = self._load_domains()
if domain in domains:
domains.remove(domain)
self._save_domains(domains)
return True
return False
def list_domains(self):
return self._load_domains()
def get_external_ip(self):
try:
return requests.get("https://checkip.amazonaws.com").text.strip()
@ -30,8 +65,12 @@ class CloudflareManager:
if not current_ip:
return "Failed to determine current external IP."
domains = self._load_domains()
if not domains:
return "No domains configured for DDNS."
results = []
for domain in self.ddns_domains:
for domain in domains:
zone_id = self.get_zone_id(domain)
if not zone_id:
results.append(f"[{domain}] Zone not found.")
@ -42,7 +81,6 @@ class CloudflareManager:
records = res.json().get('result', [])
if not records:
# Create if missing? For now, just report
results.append(f"[{domain}] No A record found to update.")
continue
@ -68,6 +106,3 @@ class CloudflareManager:
results.append(f"[{domain}] Update failed: {u_res.text}")
return "\n".join(results)
def list_domains(self):
return self.ddns_domains

View file

@ -31,6 +31,26 @@ def cf_list_ddns(config):
for domain in mgr.list_domains():
click.echo(domain)
@cloudflare.command(name='add-ddns')
@click.argument('domain')
@click.pass_obj
def cf_add_ddns(config, domain):
mgr = CloudflareManager(config)
if mgr.add_domain(domain):
click.echo(f"Added {domain} to DDNS update list")
else:
click.echo(f"{domain} already in DDNS update list")
@cloudflare.command(name='remove-ddns')
@click.argument('domain')
@click.pass_obj
def cf_remove_ddns(config, domain):
mgr = CloudflareManager(config)
if mgr.remove_domain(domain):
click.echo(f"Removed {domain} from DDNS update list")
else:
click.echo(f"{domain} not found in DDNS update list")
@cloudflare.command(name='update-ddns')
@click.option('--force', is_flag=True, help='Force update even if IP matches')
@click.pass_obj