import urllib.request import ssl import json import sys sys.stdout.reconfigure(encoding='utf-8') ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE token = "SnarfNetPermanentAutomationToken2026" headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } def api_call(url, method="GET", payload=None): data = json.dumps(payload).encode('utf-8') if payload else None req = urllib.request.Request(url, headers=headers, data=data, method=method) try: with urllib.request.urlopen(req, context=ctx, timeout=8) as resp: return json.loads(resp.read().decode('utf-8')) except urllib.error.HTTPError as e: err = e.read().decode('utf-8') print(f"API Error {e.code} on {method} {url}: {err}") return None print("Updating Pi-hole launch URLs in Authentik App Library...") apps_update = [ { "slug": "pihole1", "meta_launch_url": "https://pihole1.snarfnet.net/admin/" }, { "slug": "pihole2", "meta_launch_url": "https://pihole2.snarfnet.net/admin/" } ] for app in apps_update: slug = app['slug'] res = api_call(f"https://authentik.snarfnet.net/api/v3/core/applications/{slug}/", method="PATCH", payload=app) if res: print(f" ✓ Updated {slug} meta_launch_url to {res.get('meta_launch_url')}") print("\nVerifying final state in Authentik:") final_apps = api_call("https://authentik.snarfnet.net/api/v3/core/applications/?page_size=100") for a in sorted(final_apps.get('results', []), key=lambda x: x['name']): if a['slug'] in ['pihole1', 'pihole2', 'dns1', 'dns2']: print(f" [{a['group']}] {a['name']}") print(f" Slug: {a['slug']} | Launch URL: {a['meta_launch_url']}")