r"""Customize Authentik application dashboard with high-resolution icons, groups, and launch URLs. Full homelab catalog covering all active services across: - CI/CD & Automation - Developer Tools - Infrastructure & Hypervisors - Storage & Media - Smart Home & IoT - Observability & Operations - Networking & Edge - Hardware & Server BMC - Portals & Dashboards """ import json import ssl import sys import urllib.error import urllib.request ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE AK_BASE = "https://authentik.snarfnet.net" AK_TOKEN = "SnarfNetPermanentAutomationToken2026" APPLICATIONS = [ # CI/CD & Automation { "slug": "argo-cd", "name": "Argo CD", "group": "CI/CD & Automation", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/argo-cd.png", "meta_description": "Declarative GitOps Continuous Delivery", "meta_launch_url": "https://argo.snarfnet.net", "open_in_new_tab": True, }, { "slug": "awx", "name": "Ansible AWX", "group": "CI/CD & Automation", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/ansible.png", "meta_description": "Ansible Automation Controller & Playbook Execution", "meta_launch_url": "https://awx.snarfnet.net", "open_in_new_tab": True, }, { "slug": "jenkins", "name": "Jenkins", "group": "CI/CD & Automation", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/jenkins.png", "meta_description": "Continuous Integration & Build Server", "meta_launch_url": "https://jenkins.snarfnet.net", "open_in_new_tab": True, }, { "slug": "semaphore", "name": "Semaphore UI", "group": "CI/CD & Automation", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/semaphore.png", "meta_description": "Modern Web UI for Ansible & Task Automation", "meta_launch_url": "https://semaphore.snarfnet.net", "open_in_new_tab": True, }, # Developer Tools { "slug": "coder", "name": "Coder", "group": "Developer Tools", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/coder.png", "meta_description": "Self-Hosted Cloud Development Environments", "meta_launch_url": "https://coder.snarfnet.net", "open_in_new_tab": True, }, { "slug": "gitea", "name": "Gitea", "group": "Developer Tools", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/gitea.png", "meta_description": "Git Version Control & Repository Hosting", "meta_launch_url": "https://git.snarfnet.net", "open_in_new_tab": True, }, { "slug": "vscode", "name": "VS Code Server", "group": "Developer Tools", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/vscode.png", "meta_description": "Browser-Based Code Editor & Remote Terminal", "meta_launch_url": "http://code.snarfnet.net", "open_in_new_tab": True, }, # Infrastructure & Hypervisors { "slug": "rancher", "name": "Rancher", "group": "Infrastructure & Hypervisors", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/rancher.png", "meta_description": "Enterprise Multi-Cluster Kubernetes Management", "meta_launch_url": "https://aerodrome.snarfnet.net", "open_in_new_tab": True, }, { "slug": "harvester", "name": "Harvester HCI", "group": "Infrastructure & Hypervisors", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/harvester.png", "meta_description": "Hyperconverged Infrastructure & Virtualization", "meta_launch_url": "https://harvester.spacedock.snarfnet.net", "open_in_new_tab": True, }, { "slug": "portainer", "name": "Portainer EE", "group": "Infrastructure & Hypervisors", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/portainer.png", "meta_description": "Docker Swarm & Container Management (OAuth Active)", "meta_launch_url": "https://shipyard.snarfnet.net", "open_in_new_tab": True, }, { "slug": "meshcentral", "name": "MeshCentral", "group": "Infrastructure & Hypervisors", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/meshcentral.png", "meta_description": "Remote Device Management & Web KVM", "meta_launch_url": "https://mesh.snarfnet.net", "open_in_new_tab": True, }, { "slug": "longhorn-aerodrome", "name": "Longhorn (Aerodrome)", "group": "Infrastructure & Hypervisors", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/longhorn.png", "meta_description": "Distributed Block Storage (Aerodrome Cluster)", "meta_launch_url": "https://longhorn.aerodrome.snarfnet.net", "open_in_new_tab": True, }, { "slug": "longhorn-spaceport", "name": "Longhorn (Spaceport)", "group": "Infrastructure & Hypervisors", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/longhorn.png", "meta_description": "Distributed Block Storage (Spaceport Cluster)", "meta_launch_url": "https://longhorn.spaceport.snarfnet.net", "open_in_new_tab": True, }, # Storage & Media { "slug": "snarfnas", "name": "Synology GA (SnarfNAS)", "group": "Storage & Media", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/synology.png", "meta_description": "Primary Network Attached Storage (DSM 7)", "meta_launch_url": "https://snarfnas.snarfnet.net", "open_in_new_tab": True, }, { "slug": "sn-nas", "name": "Synology SC (SN-NAS)", "group": "Storage & Media", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/synology.png", "meta_description": "Secondary Storage & Backup NAS (DSM 7)", "meta_launch_url": "https://sn-nas.snarfnet.net", "open_in_new_tab": True, }, { "slug": "plex", "name": "Plex Media Server", "group": "Storage & Media", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/plex.png", "meta_description": "Home Streaming Media, Movies & TV Shows", "meta_launch_url": "https://plex.snarfnet.net", "open_in_new_tab": True, }, { "slug": "kavita", "name": "Kavita", "group": "Storage & Media", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/kavita.png", "meta_description": "Digital Comic, Manga & Book Library", "meta_launch_url": "https://kavita.snarfnet.net", "open_in_new_tab": True, }, # Smart Home & IoT { "slug": "home-assistant", "name": "Home Assistant", "group": "Smart Home & IoT", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/home-assistant.png", "meta_description": "Smart Home Automation Hub & Sensors", "meta_launch_url": "https://snarfhome.snarfnet.net", "open_in_new_tab": True, }, { "slug": "wled", "name": "WLED Lights", "group": "Smart Home & IoT", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/wled.png", "meta_description": "Office Addressable LED Lighting Controller", "meta_launch_url": "https://lights.snarfnet.net", "open_in_new_tab": True, }, # Observability & Operations { "slug": "grafana", "name": "Grafana", "group": "Observability & Operations", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/grafana.png", "meta_description": "Dashboards, Metrics & Visualizations", "meta_launch_url": "https://grafana.snarfnet.net", "open_in_new_tab": True, }, { "slug": "uptime-kuma", "name": "Uptime Kuma", "group": "Observability & Operations", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/uptime-kuma.png", "meta_description": "Service Uptime & Status Monitoring", "meta_launch_url": "https://uptime.snarfnet.net", "open_in_new_tab": True, }, { "slug": "zabbix", "name": "Zabbix", "group": "Observability & Operations", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/zabbix.png", "meta_description": "Enterprise Network & Infrastructure Monitoring", "meta_launch_url": "https://zabbix.snarfnet.net", "open_in_new_tab": True, }, { "slug": "peanut", "name": "PeaNUT", "group": "Observability & Operations", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/nut.png", "meta_description": "UPS Power & Server Closet Battery Monitoring", "meta_launch_url": "https://peanut.snarfnet.net/device/server-closet", "open_in_new_tab": True, }, # Networking & Edge { "slug": "pihole", "name": "Pi-hole Web", "group": "Networking & Edge", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/pi-hole.png", "meta_description": "Network DNS Sinkhole & Ad Blocker", "meta_launch_url": "https://pihole3.snarfnet.net", "open_in_new_tab": True, }, { "slug": "nginx-proxy", "name": "Nginx Proxy Manager", "group": "Networking & Edge", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/nginx-proxy-manager.png", "meta_description": "Edge Reverse Proxy & SSL Certificate Admin", "meta_launch_url": "https://nginx.snarfnet.net", "open_in_new_tab": True, }, { "slug": "gotify", "name": "Gotify", "group": "Networking & Edge", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/gotify.png", "meta_description": "Self-Hosted Push Notification Server", "meta_launch_url": "https://gotify.snarfnet.net", "open_in_new_tab": True, }, { "slug": "unifi", "name": "UniFi Cloud", "group": "Networking & Edge", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/unifi.png", "meta_description": "Ubiquiti UniFi Network & Gateway Controller", "meta_launch_url": "https://unifi.ui.com", "open_in_new_tab": True, }, # Hardware & Server BMC { "slug": "idrac-1", "name": "iDRAC 1 (Spacedock 1)", "group": "Hardware & Server BMC", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/dell.png", "meta_description": "Dell PowerEdge Server 1 Out-of-Band BMC", "meta_launch_url": "https://spacedock1.snarfnet.net", "open_in_new_tab": True, }, { "slug": "idrac-2", "name": "iDRAC 2 (Spacedock 2)", "group": "Hardware & Server BMC", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/dell.png", "meta_description": "Dell PowerEdge Server 2 Out-of-Band BMC", "meta_launch_url": "https://spacedock2.snarfnet.net", "open_in_new_tab": True, }, { "slug": "idrac-3", "name": "iDRAC 3 (Spacedock 3)", "group": "Hardware & Server BMC", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/dell.png", "meta_description": "Dell PowerEdge Server 3 Out-of-Band BMC", "meta_launch_url": "https://spacedock3.snarfnet.net", "open_in_new_tab": True, }, { "slug": "idrac-4", "name": "iDRAC 4 (Spacedock 4)", "group": "Hardware & Server BMC", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/dell.png", "meta_description": "Dell PowerEdge Server 4 Out-of-Band BMC", "meta_launch_url": "https://spacedock4.snarfnet.net", "open_in_new_tab": True, }, # Portals & Dashboards { "slug": "snarfnet-homelab", "name": "Homepage", "group": "Portals & Dashboards", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/homepage.png", "meta_description": "SnarfNet Homelab Central Portal", "meta_launch_url": "https://homepage.snarfnet.net", "open_in_new_tab": True, }, { "slug": "homebox", "name": "Homebox", "group": "Portals & Dashboards", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/homebox.png", "meta_description": "Physical Asset & Inventory Management", "meta_launch_url": "https://homebox.snarfnet.net", "open_in_new_tab": True, }, { "slug": "gaia", "name": "Gaia Web App", "group": "Portals & Dashboards", "meta_icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/open-webui.png", "meta_description": "Swarm Services & AI Stack Portal", "meta_launch_url": "https://gaia.snarfnet.net", "open_in_new_tab": True, }, { "slug": "dakboard", "name": "Dakboard Display", "group": "Portals & Dashboards", "meta_icon": "https://dakboard.com/favicon.ico", "meta_description": "Custom Digital Wall Calendar & Display", "meta_launch_url": "https://dakboard.com/site", "open_in_new_tab": True, }, ] def ak_request(endpoint, method="GET", data=None): url = f"{AK_BASE.rstrip('/')}/{endpoint.lstrip('/')}" body = json.dumps(data).encode("utf-8") if data else None headers = { "Authorization": f"Bearer {AK_TOKEN}", "Content-Type": "application/json", "Accept": "application/json", } req = urllib.request.Request(url, data=body, headers=headers, method=method) try: with urllib.request.urlopen(req, timeout=10, context=ctx) as r: res_body = r.read().decode("utf-8") return r.getcode(), json.loads(res_body) if res_body else {} except urllib.error.HTTPError as e: err_msg = e.read().decode("utf-8") return e.code, err_msg def fetch_all_apps(): apps = [] page = 1 while True: code, res = ak_request(f"/api/v3/core/applications/?page={page}") if code != 200 or not res.get("results"): break apps.extend(res["results"]) if not res.get("pagination", {}).get("next"): break page += 1 return {a["slug"]: a for a in apps} def main(): print("=" * 60, flush=True) print("SnarfNet Authentik Application Customizer (Full Catalog)", flush=True) print("=" * 60, flush=True) # 1. Fetch current applications existing_apps = fetch_all_apps() print(f"Found {len(existing_apps)} existing applications in Authentik.\n", flush=True) for app in APPLICATIONS: slug = app["slug"] if slug in existing_apps: # Preserve existing provider if assigned patch_data = { "name": app["name"], "group": app["group"], "meta_icon": app["meta_icon"], "meta_description": app["meta_description"], "meta_launch_url": app["meta_launch_url"], "open_in_new_tab": app["open_in_new_tab"], } code, r = ak_request(f"/api/v3/core/applications/{slug}/", method="PATCH", data=patch_data) if code == 200: print(f"[UPDATED] {slug:20} -> {app['name']:28} ({app['group']})", flush=True) else: print(f"[FAIL] {slug:20} -> Error {code}: {r}", flush=True) else: # POST new application without provider (standalone launcher) post_data = { "name": app["name"], "slug": app["slug"], "group": app["group"], "meta_icon": app["meta_icon"], "meta_description": app["meta_description"], "meta_launch_url": app["meta_launch_url"], "open_in_new_tab": app["open_in_new_tab"], } code, r = ak_request("/api/v3/core/applications/", method="POST", data=post_data) if code == 201: print(f"[CREATED] {slug:20} -> {app['name']:28} ({app['group']})", flush=True) else: print(f"[FAIL] {slug:20} -> Error {code}: {r}", flush=True) print("\n" + "=" * 60, flush=True) print("Verifying updated applications catalog:", flush=True) print("=" * 60, flush=True) all_apps = fetch_all_apps() for slug, a in sorted(all_apps.items(), key=lambda x: (x[1].get("group") or "", x[1].get("name") or "")): prov_name = a.get("provider_obj", {}).get("name") if a.get("provider_obj") else "Direct" print(f"[{a.get('group', 'Default'):28}] {a['name']:28} (Auth: {prov_name:18}) -> {a.get('launch_url')}", flush=True) print("\nFull catalog update complete!", flush=True) if __name__ == "__main__": main()