Files
family_home_lab/portal/tools.py

327 lines
17 KiB
Python

"""Tool catalogue — the Family Home Lab "home dash".
Each tool maps to a category (colour sticker), a friendly name/description and a
launch mode: "embed" (inline iframe) or "window" (new tab / external link).
This is a curated home-dash across .13 and .35 plus Proxmox / Home Assistant.
Most of these are external apps that keep their OWN secondary login — we link
out to them and never try to pass credentials (no SSO passthrough).
Flags:
external : link out in a new tab (opens URL directly, no /tool/ wrapper)
login : the target has its own login the user must sign into
lan : only resolves on the LAN (*.home.lab), not from the internet
admin : admin-only (infra / backups / monitoring)
"""
from __future__ import annotations
from dataclasses import dataclass
from .config import settings
@dataclass(frozen=True)
class Tool:
id: str
name: str
category: str
description: str
url: str
mode: str = "window" # "embed" | "window"
status: str = "online"
external: bool = True
login: bool = False
lan: bool = False
admin: bool = False
private: bool = False # per-user private app (e.g. dsh instance)
def section_label(category: str) -> str:
return {
"chat": "Chat & AI",
"image": "Image Lab",
"video": "Video Lab",
"audio": "Audio Lab",
"docs": "Notes & Docs",
"ai": "AI Tools",
"media": "Media & Fun",
"data": "Data & Links",
"storage": "Storage & Backups",
"home": "Home & Lab",
"infra": "Infrastructure",
"network": "Home Network",
"home-mgmt": "Home Management",
"dev": "Development",
"portfolio": "Portfolio",
}.get(category, category.title())
def section_color(category: str) -> str:
return settings.SECTION_COLORS.get(category, "#a39e98")
# Every category a user may have tools in; simple tooling produces all of these.
ALL_CATEGORIES = [
"chat", "ai", "image", "video", "audio", "media", "docs", "data",
"home-mgmt", "storage", "dev", "portfolio", "home", "infra", "network",
]
def _cat() -> list[Tool]:
"""Curated home-dash catalogue across .13 / .35 / Proxmox / Home Assistant."""
return [
# ---- Chat & AI -----------------------------------------------------
Tool("dsh", "DeepSeek Harness", "chat",
"Your personal assistant instance", "https://dsh-sam.lab.audasmedia.com.au",
private=True, login=True),
Tool("chat-lab", "Family Chat", "chat",
"Group chat — available to everyone", "https://chat.lab.audasmedia.com.au", login=True),
Tool("pb-chat", "PB Chat (Admin)", "chat",
"Chat admin console (admins only)", "https://pb.chat.lab.audasmedia.com.au", login=True, admin=True),
Tool("ai-router", "AI Router", "ai",
"OmniRoute LLM gateway dashboard", "https://omniroute.lab.audasmedia.com.au", login=True),
Tool("n8n", "n8n", "ai",
"Workflow automation", "https://n8n.lab.audasmedia.com.au", login=True),
Tool("langfuse", "Langfuse", "ai",
"LLM tracing & observability", "https://langfuse.home.lab", login=True, lan=True, admin=True),
Tool("prefect", "Prefect", "ai",
"Workflow orchestration", "https://prefect.lab.audasmedia.com.au", login=True),
Tool("archon", "Archon", "ai",
"Autonomous coding agent", "https://archon.home.lab", login=True, lan=True),
Tool("supabase", "LangChain (Supabase)", "ai",
"LangChain dev service", "https://supabase.home.lab", login=True, lan=True, admin=True),
Tool("openweb", "OpenWebUI", "ai",
"Chat UI frontend", "https://openweb.lab.audasmedia.com.au", login=True),
# ---- Image ---------------------------------------------------------
Tool("photopea", "Photopea", "image",
"Browser photo & PSD editor", "https://photo.lab.audasmedia.com.au", login=True),
Tool("penpot", "Penpot", "image",
"Open-source design & prototyping", "https://penpot.lab.audasmedia.com.au", login=True),
Tool("photo-filter", "Photo Filter", "image",
"Photo utility / filtering", "https://photo-filter.lab.audasmedia.com.au", login=True),
# ---- Video ---------------------------------------------------------
Tool("video-editor", "Video Editor", "video",
"Desktop video editing in your browser", "https://video.lab.audasmedia.com.au", login=True),
Tool("jellyfin", "Jellyfin", "media",
"Movies, TV & music server", "https://jellyfin.lab.audasmedia.com.au", login=True),
Tool("jellyseerr", "Jellyseerr", "media",
"Request movies & shows", "https://jellyseerr.lab.audasmedia.com.au", login=True),
Tool("games", "Games", "media",
"Retro games (emulatorjs, LAN)", "https://games.home.lab", lan=True),
Tool("sonarr", "Sonarr", "media",
"TV series manager", "https://sonarr.lab.audasmedia.com.au", login=True, admin=True),
Tool("radarr", "Radarr", "media",
"Movie manager", "https://radarr.lab.audasmedia.com.au", login=True, admin=True),
Tool("readarr", "Readarr", "media",
"Ebook manager", "https://readarr.lab.audasmedia.com.au", login=True, admin=True),
Tool("lidarr", "Lidarr", "media",
"Music manager", "https://lidarr.lab.audasmedia.com.au", login=True, admin=True),
Tool("sabnzbd", "SABnzbd", "media",
"Usenet downloader", "https://sabnzbd.lab.audasmedia.com.au", login=True, admin=True),
Tool("qbittorrent", "qBittorrent", "media",
"Torrent client", "https://qbittorrent.lab.audasmedia.com.au", login=True, admin=True),
Tool("spotweb", "Spotweb", "media",
"Spotnet indexer", "https://spotweb.lab.audasmedia.com.au", login=True, admin=True),
Tool("headphones", "Headphones", "media",
"Music manager", "https://headphones.lab.audasmedia.com.au", login=True, admin=True),
# ---- Audio ---------------------------------------------------------
Tool("audio-editor", "Audio Editor", "audio",
"Multi-track audio editing", "https://audio.lab.audasmedia.com.au", login=True),
Tool("lmms", "LMMS Music Studio", "audio",
"Beats, melodies & full arrangements", "https://lmms.lab.audasmedia.com.au", login=True),
Tool("snapcast", "Snapcast", "audio",
"Sync audio to speakers around the house", "https://snapcast.home.lab", lan=True),
Tool("mopidy", "Mopidy", "audio",
"Music player (LAN only)", "http://mopidy.home.lab", lan=True),
# ---- Notes & Docs --------------------------------------------------
Tool("silverbullet", "SilverBullet", "docs",
"Markdown notes & knowledge", "https://silverbullet.lab.audasmedia.com.au", login=True),
Tool("affine", "AFFiNE", "docs",
"Docs + whiteboard + database", "https://affine.lab.audasmedia.com.au", login=True),
Tool("hedgedoc", "HedgeDoc", "docs",
"Real-time collaborative markdown", "https://hedgedoc.lab.audasmedia.com.au", login=True),
Tool("trilium", "Trilium", "docs",
"Privacy-focused notes", "https://trillium.lab.audasmedia.com.au", login=True),
Tool("flatnotes", "Flatnotes", "docs",
"Minimal markdown notes", "https://flatnotes.lab.audasmedia.com.au", login=True),
Tool("vikunja", "Vikunja", "docs",
"Tasks & to-dos", "https://vikunja.lab.audasmedia.com.au", login=True),
Tool("bookstack", "BookStack", "docs",
"Documentation chains", "https://bookstack.home.lab", login=True, lan=True),
Tool("dokuwiki", "DokuWiki", "docs",
"Lightweight wiki", "https://dokuwiki.lab.audasmedia.com.au", login=True),
Tool("kanboard", "Kanboard", "docs",
"Kanban boards", "https://kanboard.lab.audasmedia.com.au", login=True),
# ---- Data & Links --------------------------------------------------
Tool("nocodb", "NocoDB", "data",
"Turn spreadsheets into apps", "https://nocodb.lab.audasmedia.com.au", login=True),
Tool("linkace", "LinkAce", "data",
"Bookmark manager", "https://linkace.lab.audasmedia.com.au", login=True),
Tool("wherewoof", "Where-Woof", "data",
"Family location tracking", "https://where-woof.com", login=True),
Tool("wherewoof-admin", "Where-Woof Admin", "data",
"Manage family location tracking", "https://admin.where-woof.com", login=True),
Tool("freshrss", "FreshRSS", "data",
"RSS reader", "https://freshrss.lab.audasmedia.com.au", login=True),
Tool("firefly", "Firefly III", "data",
"Personal finance", "https://firefly.lab.audasmedia.com.au", login=True, admin=True),
Tool("vaultwarden", "Vaultwarden", "data",
"Password manager", "https://vaultwarden.lab.audasmedia.com.au", login=True, admin=True),
# ---- Home Management ----------------------------------------------
Tool("homebox", "Homebox", "home-mgmt",
"Home inventory & items", "https://homebox.lab.audasmedia.com.au", login=True),
Tool("toora", "Toora Wiki", "home-mgmt",
"Home wiki", "https://toora.lab.audasmedia.com.au", login=True),
Tool("sams", "Sams Files", "home-mgmt",
"Home file server (LAN)", "http://sams.home.lab", lan=True),
Tool("owl", "Owl Recipes", "home-mgmt",
"Kitchen recipes & household", "https://owl.lab.audasmedia.com.au", login=True),
# ---- Storage & Files ----------------------------------------------
Tool("nextcloud", "Nextcloud", "storage",
"Files, calendar, contacts", "https://nextcloud.home.lab", login=True, lan=True),
Tool("immich", "Immich", "storage",
"Self-hosted photo gallery", "https://immich.lab.audasmedia.com.au", login=True),
Tool("paperless", "Paperless-ngx", "storage",
"Scan, OCR & archive documents", "https://paperless.lab.audasmedia.com.au", login=True),
Tool("filebrowser", "File Browser", "storage",
"File management web UI", "https://filebrowser.lab.audasmedia.com.au", login=True),
Tool("garage", "Garage S3", "storage",
"Object storage (buckets)", "http://192.168.20.13:3902",
login=True, lan=True, admin=True),
Tool("pingvin", "Pingvin Share", "storage",
"Share files & notes", "https://pingvin.lab.audasmedia.com.au", login=True),
Tool("kopia", "Kopia", "storage",
"Backups & restore", "https://kopia.home.lab", login=True, lan=True, admin=True),
Tool("backrest", "Backrest", "storage",
"Restic backup UI", "https://restic.home.lab", login=True, lan=True, admin=True),
# ---- Development ---------------------------------------------------
Tool("gitea", "Gitea", "dev",
"Git repositories", "https://gitea.lab.audasmedia.com.au", login=True),
# ---- Portfolio (Sam's resume sites) --------------------------------
Tool("sam-developer", "Developer", "portfolio",
"Resume — software development", "https://sam-developer.lab.audasmedia.com.au"),
Tool("sam-devops", "DevOps", "portfolio",
"Resume — DevOps & infrastructure", "https://sam-devops.lab.audasmedia.com.au"),
Tool("sam-iot-electronics", "IoT & Electronics", "portfolio",
"Resume — IoT & electronics", "https://sam-iot-electronics.lab.audasmedia.com.au"),
Tool("sam-pursuits", "Pursuits", "portfolio",
"Resume — projects & pursuits", "https://sam-pursuits.lab.audasmedia.com.au"),
Tool("wikijs", "Wiki.js", "portfolio",
"Resume wiki — machines & projects", "https://wikijs.lab.audasmedia.com.au", login=True),
Tool("lynx", "Lynx", "portfolio",
"Resume — projects gallery", "https://lynx.lab.audasmedia.com.au", login=True),
Tool("sequence", "Sequence", "portfolio",
"Resume — sequence", "https://sequence.lab.audasmedia.com.au"),
# ---- Home & Lab ----------------------------------------------------
Tool("home-assistant", "Home Assistant", "home",
"Home automation", "https://homeassistant.home.lab", login=True, lan=True),
Tool("proxmox", "Proxmox VE", "home",
"Virtualisation & containers", "https://proxmox.home.lab", login=True, lan=True, admin=True),
Tool("proxmox-backup", "Proxmox Backup", "home",
"VM/CT backup server", "https://proxmox_backup.home.lab", login=True, lan=True, admin=True),
# ---- Infrastructure & Network -------------------------------------
Tool("grafana", "Grafana", "infra",
"Metrics dashboards", "https://grafana.home.lab", login=True, lan=True, admin=True),
Tool("uptimekuma", "Uptime Kuma", "infra",
"Uptime monitoring & alerts", "https://uptimekuma.lab.audasmedia.com.au", login=True),
Tool("apprise", "Apprise", "infra",
"Notification & messaging API", "https://apprise.lab.audasmedia.com.au"),
Tool("portainer", "Portainer", "infra",
"Docker management", "https://portainer.home.lab", login=True, lan=True, admin=True),
Tool("dozzle", "Dozzle", "infra",
"Container logs (LAN)", "https://dozzle.home.lab", login=True, lan=True, admin=True),
Tool("watchyourlan", "WatchYourLAN", "infra",
"LAN device scanning (LAN)", "https://watchyourlan.home.lab", login=True, lan=True, admin=True),
Tool("phpipam", "phpIPAM", "infra",
"IP address manager (LAN)", "https://phpipam.home.lab", login=True, lan=True, admin=True),
Tool("flare", "Flare", "infra",
"Start page (LAN)", "https://flare.home.lab", login=True, lan=True),
Tool("ntfy", "Ntfy", "infra",
"Push notifications (LAN)", "https://ntfy.home.lab", login=True, lan=True),
Tool("worldmonitor", "World Monitor", "infra",
"Global monitoring", "https://worldmonitor.lab.audasmedia.com.au", login=True, admin=True),
Tool("homepage", "Homepage", "infra",
"Start page", "https://homepage.lab.audasmedia.com.au", login=True),
Tool("librenms", "LibreNMS", "network",
"Network monitoring", "https://librenms.home.lab", login=True, lan=True, admin=True),
Tool("pihole", "Pi-hole", "network",
"Network-wide ad blocking", "https://pihole.home.lab", login=True, lan=True),
]
TOOLS = _cat()
def tools_for_user(user) -> list[tuple[str, list[Tool]]]:
"""Return tools the user is permitted to see, grouped into category sections.
Family categories (media/fun, data, storage, home) are open to everyone.
Infra/network are gated by the admin flag on each tool. Existing per-user
capabilities (chat/image/video/audio/docs) still apply to those categories.
"""
# Admins see every category regardless of per-user capability flags.
if user.is_admin:
allowed = {cat: True for cat in ALL_CATEGORIES}
else:
allowed = {
"chat": user.can_chat,
"ai": user.can_chat, # ai stack broadly tied to chat entitlement
"image": user.can_image,
"video": user.can_video,
"audio": user.can_audio,
"docs": user.can_docs,
# open family-facing categories
"media": True,
"data": True,
"storage": True,
"home": True,
"home-mgmt": True,
"dev": True,
"portfolio": True,
"infra": True, # filtered by per-tool admin flag below
"network": True,
}
sections: dict[str, list[Tool]] = {}
for tool in TOOLS:
if tool.admin and not user.is_admin:
continue
if not allowed.get(tool.category, True):
continue
# Specialise the dsh chat link to the logged-in user's own instance.
if tool.id == "dsh":
tool = _dsh_for_user(user)
sections.setdefault(tool.category, []).append(tool)
ordered = []
for cat in ALL_CATEGORIES:
if cat in sections:
ordered.append((cat, sections[cat]))
return ordered
def _dsh_for_user(user) -> Tool:
base = next(t for t in TOOLS if t.id == "dsh")
host = user.username.strip().lower()
return Tool(
id=base.id,
name=base.name,
category=base.category,
description=base.description,
url=f"https://dsh-{host}.lab.audasmedia.com.au",
mode=base.mode,
status=base.status,
external=True,
login=True,
private=True,
)