Features: - Real-time Proxmox cluster monitoring (nodes, LXC containers) - Camera integration with go2rtc streams - Arr stack download progress monitoring - PBS backup status - Docker container status - Uptime Kuma service health - FastAPI backend with HTMX frontend
178 lines
12 KiB
Python
178 lines
12 KiB
Python
"""Enhanced Dashboard configuration v2."""
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional, List, Dict
|
|
import json
|
|
import os
|
|
|
|
@dataclass
|
|
class Service:
|
|
name: str
|
|
url: str
|
|
ip: str
|
|
port: int
|
|
category: str
|
|
icon: str = "server"
|
|
favorite: bool = False
|
|
critical: bool = False
|
|
group: Optional[str] = None
|
|
|
|
# Proxmox configuration
|
|
PROXMOX_NODES = [
|
|
{"name": "empire", "ip": "192.168.1.75", "port": 8006},
|
|
{"name": "republic", "ip": "192.168.1.80", "port": 8006},
|
|
{"name": "Jedi", "ip": "192.168.1.40", "port": 8006},
|
|
{"name": "hoth", "ip": "192.168.1.76", "port": 8006},
|
|
]
|
|
|
|
PROXMOX_API_TOKEN = "root@pam!dashboard"
|
|
PROXMOX_API_SECRET = "edb7e222-f45d-4b15-8089-0c7eee397ab5"
|
|
|
|
# PBS configuration
|
|
PBS_URL = "https://192.168.1.159:8007"
|
|
PBS_API_TOKEN = "root@pam!dashboard"
|
|
PBS_API_SECRET = "edb7e222-f45d-4b15-8089-0c7eee397ab5"
|
|
|
|
# OPNsense configuration
|
|
OPNSENSE_URL = "https://192.168.1.1"
|
|
OPNSENSE_API_KEY = ""
|
|
OPNSENSE_API_SECRET = ""
|
|
|
|
# Prometheus configuration
|
|
PROMETHEUS_URL = "http://192.168.1.163:9090"
|
|
|
|
# Camera configuration
|
|
GO2RTC_URL = "http://192.168.1.241:1985"
|
|
CAMERAS = ["FPE", "Front_Porch", "Driveway", "Driveway_door", "Backyard", "House_side", "Street_side", "Porch_Downstairs"]
|
|
|
|
# Sabnzbd configuration
|
|
SABNZBD_URL = "http://192.168.1.66:7777"
|
|
SABNZBD_API_KEY = ""
|
|
|
|
# Uptime Kuma configuration
|
|
UPTIME_KUMA_URL = "http://192.168.1.155:3001"
|
|
UPTIME_KUMA_STATUS_PAGE = "uptime"
|
|
|
|
# Docker hosts
|
|
DOCKER_HOSTS = [
|
|
{"name": "c3p0", "ip": "192.168.1.54", "port": 2375},
|
|
{"name": "frigate", "ip": "192.168.1.241", "port": 2375},
|
|
]
|
|
|
|
# Service groups
|
|
SERVICE_GROUPS = {
|
|
"Arr Stack": ["Radarr", "Sonarr", "Lidarr", "Prowlarr", "Sabnzbd"],
|
|
"Media Players": ["Jellyfin", "Jellyseerr", "Tdarr"],
|
|
"Cameras": ["Frigate", "go2rtc"],
|
|
}
|
|
|
|
# Icon SVG paths
|
|
SERVICE_ICONS = {
|
|
"shield": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/>',
|
|
"globe": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9"/>',
|
|
"archive": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"/>',
|
|
"lock": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>',
|
|
"film": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 4v16M17 4v16M3 8h4m10 0h4M3 12h18M3 16h4m10 0h4M4 20h16a1 1 0 001-1V5a1 1 0 00-1-1H4a1 1 0 00-1 1v14a1 1 0 001 1z"/>',
|
|
"star": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"/>',
|
|
"video": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"/>',
|
|
"tv": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>',
|
|
"music": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3"/>',
|
|
"search": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>',
|
|
"download": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/>',
|
|
"cog": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>',
|
|
"chart": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>',
|
|
"heartbeat": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>',
|
|
"key": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/>',
|
|
"git": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/>',
|
|
"workflow": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"/>',
|
|
"book": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>',
|
|
"image": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>',
|
|
"brain": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>',
|
|
"home": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>',
|
|
"camera": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"/>',
|
|
"message": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/>',
|
|
"server": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/>',
|
|
"database": '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"/>',
|
|
}
|
|
|
|
# Services
|
|
SERVICES = [
|
|
Service("OPNsense", "https://192.168.1.1", "192.168.1.1", 443, "Infrastructure", "shield", critical=True),
|
|
Service("NPM", "https://192.168.1.38:81", "192.168.1.38", 81, "Infrastructure", "globe", critical=True),
|
|
Service("PBS", "https://192.168.1.159:8007", "192.168.1.159", 8007, "Infrastructure", "archive", critical=True),
|
|
Service("Authentik", "https://auth.deathstar-home.one", "192.168.1.200", 9000, "Infrastructure", "lock"),
|
|
Service("Jellyfin", "https://jellyfin.deathstar-home.one", "192.168.1.49", 8096, "Media", "film", favorite=True, group="Media Players"),
|
|
Service("Jellyseerr", "https://request.deathstar-home.one", "192.168.1.12", 5055, "Media", "star", favorite=True, group="Media Players"),
|
|
Service("Radarr", "https://radarr.deathstar-home.one", "192.168.1.56", 7878, "Media", "film", group="Arr Stack"),
|
|
Service("Sonarr", "https://sonarr.deathstar-home.one", "192.168.1.62", 8989, "Media", "tv", group="Arr Stack"),
|
|
Service("Lidarr", "https://lidarr.deathstar-home.one", "192.168.1.23", 8686, "Media", "music", group="Arr Stack"),
|
|
Service("Prowlarr", "https://prowlarr.deathstar-home.one", "192.168.1.58", 9696, "Media", "search", group="Arr Stack"),
|
|
Service("Sabnzbd", "https://sabnzbd.deathstar-home.one", "192.168.1.66", 7777, "Media", "download", group="Arr Stack"),
|
|
Service("Tdarr", "https://tdarr.deathstar-home.one", "192.168.1.182", 8265, "Media", "cog", group="Media Players"),
|
|
Service("Grafana", "https://grafana.deathstar-home.one", "192.168.1.163", 3000, "Monitoring", "chart", favorite=True),
|
|
Service("Uptime Kuma", "https://status.deathstar-home.one", "192.168.1.155", 3001, "Monitoring", "heartbeat"),
|
|
Service("Vaultwarden", "https://vault.deathstar-home.one", "192.168.1.154", 80, "Apps", "key", favorite=True),
|
|
Service("Gitea", "https://git.deathstar-home.one", "192.168.1.135", 3000, "Apps", "git"),
|
|
Service("n8n", "https://n8n.deathstar-home.one", "192.168.1.254", 5678, "Apps", "workflow"),
|
|
Service("BookStack", "https://docs.deathstar-home.one", "192.168.1.194", 80, "Apps", "book"),
|
|
Service("Immich", "https://cloud.deathstar-home.one", "192.168.1.54", 2283, "Apps", "image", favorite=True),
|
|
Service("Open WebUI", "https://ai.deathstar-home.one", "192.168.1.63", 8080, "Apps", "brain"),
|
|
Service("Home Assistant", "https://astro.deathstar-home.one", "192.168.1.50", 8123, "Home", "home", favorite=True, critical=True),
|
|
Service("Frigate", "https://frigate.deathstar-home.one", "192.168.1.241", 5000, "Home", "camera", group="Cameras"),
|
|
Service("go2rtc", "http://192.168.1.241:1985", "192.168.1.241", 1985, "Home", "video", group="Cameras"),
|
|
Service("Matrix", "https://chat.deathstar-home.one", "192.168.1.162", 8080, "Home", "message"),
|
|
]
|
|
|
|
CATEGORIES = {
|
|
"Infrastructure": {"color": "blue", "icon": "server"},
|
|
"Media": {"color": "purple", "icon": "film"},
|
|
"Monitoring": {"color": "amber", "icon": "activity"},
|
|
"Apps": {"color": "emerald", "icon": "grid"},
|
|
"Home": {"color": "cyan", "icon": "home"},
|
|
}
|
|
|
|
SETTINGS_FILE = "/opt/dashboard/settings.json"
|
|
DEFAULT_SETTINGS = {
|
|
"refresh_interval": 30,
|
|
"theme": "dark",
|
|
"favorites": ["Jellyfin", "Jellyseerr", "Grafana", "Vaultwarden", "Immich", "Home Assistant"],
|
|
"collapsed_categories": [],
|
|
"show_response_times": True,
|
|
"show_icons": True,
|
|
}
|
|
|
|
def load_settings():
|
|
try:
|
|
if os.path.exists(SETTINGS_FILE):
|
|
with open(SETTINGS_FILE, 'r') as f:
|
|
return {**DEFAULT_SETTINGS, **json.load(f)}
|
|
except:
|
|
pass
|
|
return DEFAULT_SETTINGS.copy()
|
|
|
|
def save_settings(settings):
|
|
try:
|
|
with open(SETTINGS_FILE, 'w') as f:
|
|
json.dump(settings, f, indent=2)
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
def get_services_by_category():
|
|
categories = {}
|
|
for service in SERVICES:
|
|
if service.category not in categories:
|
|
categories[service.category] = []
|
|
categories[service.category].append(service)
|
|
return categories
|
|
|
|
def get_favorites():
|
|
settings = load_settings()
|
|
fav_names = settings.get("favorites", [])
|
|
return [s for s in SERVICES if s.name in fav_names or s.favorite]
|
|
|
|
def get_critical_services():
|
|
return [s for s in SERVICES if s.critical]
|
|
|
|
def get_service_icon(icon_name):
|
|
return SERVICE_ICONS.get(icon_name, SERVICE_ICONS["server"])
|