- Vue 44.2%
- Python 39.2%
- TypeScript 11.8%
- Dockerfile 1.8%
- Shell 1.4%
- Other 1.6%
The onboarding checklist was a raw downloadable .md; render it in-app instead with copy-to-clipboard code blocks (falling back to execCommand when the Clipboard API is unavailable/denied, since DevHub may be served over plain HTTP). Also fixes a real bug where the SPA fallback route swallowed actual static files (e.g. the logo) by returning index.html for every non-API path. Adds the dgit helper script (wraps git operations in a throwaway container with persistent safe.directory config), downloadable from Settings, to replace manually typing the full docker run incantation on the NAS for every project. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| backend | ||
| frontend | ||
| scripts | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| Entwicklungsauftrag.txt | ||
| NEUES-PROJEKT.md | ||
| Projektbeschreibung.txt | ||
| README.md | ||
DevHub
Zentrale interne Weboberfläche zum Deployen und Überwachen der eigenen Docker-Compose-Projekte auf der Synology NAS. Ersetzt den manuellen Prozess (Ordner kopieren, Container Manager Projekt neu anlegen) durch einen Deploy-Klick (git pull → docker compose pull → docker compose up -d --build), inklusive Live-Logs, Deployment-Historie und einfacher Benutzerverwaltung.
Architektur- und Umsetzungsdetails: siehe Entwicklungsauftrag.txt / Projektbeschreibung.txt (ursprüngliche Spezifikation) sowie die Commit-Historie für die tatsächlichen Entscheidungen.
Stack
- Backend: FastAPI (Python 3.12), SQLAlchemy 2.0 + SQLite (kein Alembic, handgeschriebene
_ensure_column()-Migrationen) - Frontend: Vue 3 + Vite + TypeScript + Tailwind, als statische Assets in dasselbe Docker-Image gebaut
- Ein Container, ein
docker-compose.yml— kein separater Nginx/Reverse-Proxy-Container - Git-Server: Forgejo (eigener Container auf derselben NAS)
Lokale Entwicklung
Voraussetzungen: Python 3.12+, Node.js 22+.
# Backend
cd backend
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
cp ../.env.example ../.env # ausfüllen, siehe unten
.venv/bin/uvicorn app.main:app --reload --port 8000
# Frontend (separates Terminal)
cd frontend
npm install
npm run dev # http://localhost:5173, proxied auf Backend :8000
Ersten Admin-Nutzer anlegen
python3 -c "import bcrypt; print(bcrypt.hashpw(b'DEIN_PASSWORT', bcrypt.gensalt()).decode())"
Den Hash in .env als ADMIN_PASSWORD_HASH eintragen. Beim ersten Start wird daraus automatisch ein Admin-Nutzer angelegt (nur wenn die users-Tabelle noch leer ist — danach ist dieser Wert wirkungslos). Achtung: bcrypt-Hashes enthalten $-Zeichen, die Docker Compose in .env-Dateien als Variablen interpretiert — jedes $ muss dort als $$ geschrieben werden, sonst wird der Hash beim Start stillschweigend zerstört (siehe Abschnitt "Bekannte Stolperfallen").
Für das Einrichten eines neuen Projekts (nicht DevHub selbst) siehe die Schritt-für-Schritt-Checkliste in NEUES-PROJEKT.md.
Erst-Deployment auf die Synology NAS
- Forgejo aufsetzen (falls noch nicht vorhanden): eigener Container, z. B.
codeberg.org/forgejo/forgejo:15, SQLite reicht. - Deploy-Key anlegen (einmalig, manuell): SSH-Keypair erzeugen, öffentlichen Schlüssel in Forgejo pro Repo als Read-only Deploy Key hinterlegen. Privaten Schlüssel nach
./data/ssh/id_ed25519legen (wird ins Repo nicht eingecheckt). - Repo nach
/volume1/docker/projects/DevHub/klonen (DSM hat kein nativesgit— dafür einen Wegwerf-Container nutzen, siehe unten). Das ist ein einmaliger manueller Schritt, der klassische Henne-Ei-Fall: DevHub kann sich nicht selbst deployen, solange es noch nicht läuft. .envauf der NAS befüllen (siehe.env.example), insbesondereADMIN_PASSWORD_HASH(mit$$-Escaping!) undGIT_SSH_KEY_PATH=/app/data/ssh/id_ed25519. Falls Port 8000 auf der NAS schon belegt ist (z. B. durch Portainer),DEVHUB_HOST_PORT=<anderer Port>setzen.docker compose up -d --build.- Danach DevHub selbst als Projekt in der UI anlegen (Pfad
/volume1/docker/projects/DevHub) — mit der Checkbox "Dies ist DevHub selbst" aktiviert. Das zeigt Status/Git-Historie/Logs für DevHub mit an, deaktiviert aber den Deploy-Button. Warum, siehe nächster Abschnitt.
DevHub selbst aktualisieren (immer manuell, nie über den eigenen Deploy-Button)
Der Deploy-Button für DevHub selbst ist absichtlich deaktiviert (Checkbox "Dies ist DevHub selbst" beim Anlegen des Projekts). Grund: Der Prozess, der den Deploy ausführt, läuft innerhalb des Containers, der dabei gerade ersetzt wird. Docker Compose tauscht den Container mitten in der laufenden Anfrage aus — dabei wurde in der Praxis beobachtet, dass der neue Container zwar gebaut, aber nie gestartet wurde, während der alte schon beendet war (beide Container standen anschließend auf "off"). Kein Datenverlust (SQLite-DB liegt im Bind-Mount), aber ein manueller Eingriff war nötig, um wieder hochzufahren.
Andere Projekte (z. B. ooo-manager) sind davon nicht betroffen — dort ersetzt der Deploy-Vorgang einen anderen Container als den, der den Befehl ausführt.
So aktualisierst du DevHub selbst (per SSH auf der NAS, mit der dgit-Hilfsfunktion — siehe NEUES-PROJEKT.md für die einmalige Einrichtung):
cd /volume1/docker/projects/DevHub
dgit pull
# Neu bauen und starten — das läuft hier sicher durch, weil es direkt vom
# NAS-eigenen Docker-Client kommt, nicht von DevHub selbst aus
sudo docker compose up -d --build
Falls dabei mal ein alter, gestoppter Container übrig bleibt (z. B. nach einem unterbrochenen Vorgang):
sudo docker compose ps -a # Zustand ansehen
sudo docker rm devhub # falls ein gestoppter "devhub"-Container blockiert
sudo docker compose up -d # Compose übernimmt/startet den bereits gebauten Container
Bekannte Stolperfallen (aus echtem NAS-Betrieb)
- Kein natives
gitauf DSM. Statt bei jedem Aufruf einen Wegwerf-Container von Hand zu bauen, gibt's diedgit-Shell-Funktion — Einrichtung perscripts/setup-dgit.sh(auch herunterladbar direkt aus DevHub → Einstellungen → "Setup-Hilfen", sieheNEUES-PROJEKT.md). Kapselt Container,safe.directoryundhost.docker.internalweg, danach reichtdgit pull,dgit status,dgit clone ...wie normales Git. Das Skript schreibt bewusst in mehrere Profil-Dateien (.bash_profile,.profile,.bashrc), weil SSH-Login-Shells nicht zuverlässig.bashrcallein einlesen. - "detected dubious ownership": Git verweigert die Arbeit, wenn Datei-Owner ≠ ausführender Nutzer (üblich bei Bind-Mounts). Im DevHub-Image dauerhaft über
git config --system --add safe.directory '*'im Dockerfile gelöst.dgitlöst das für NAS-seitige Aufrufe über eine gemountete, persistente~/.dgit/gitconfig. - Container erreicht den eigenen Host nicht über dessen LAN-/Tailscale-/öffentliche IP ("Hairpin"-NAT, insbesondere auf Synology beobachtet — tritt sowohl auf Docker- als auch auf Router-Ebene auf). Git-Remote-URLs für Repos, die vom DevHub-Container selbst gepullt werden, müssen
http://host.docker.internal:3000/...verwenden, nicht die NAS-eigene IP oder gar die öffentliche Domain.docker-compose.ymlsetzt dafürextra_hosts: host.docker.internal:host-gateway;dgitmacht dasselbe für Wegwerf-Container. docker compose up -d --buildmerged Listen wieports:additiv statt sie zu ersetzen — einedocker-compose.override.ymlfür einen anderen Port funktioniert deshalb nicht wie erwartet. Stattdessen:${DEVHUB_HOST_PORT:-8000}indocker-compose.yml, Wert einfach in.envsetzen.- bcrypt-Hashes und
.env:$-Zeichen inADMIN_PASSWORD_HASHmüssen als$$geschrieben werden, sonst interpretiert Docker Compose sie als (nicht existierende) Variablen und zerstört den Hash lautlos. scpvon macOS auf DSM kann mit "subsystem request failed" scheitern (DSMs SSH fehlt oft ein funktionierendes SFTP-Subsystem) —scp -Oerzwingt das ältere SCP-Protokoll.- Zeitstempel wirken falsch (UTC statt lokal): SQLite/SQLAlchemy verlieren die Zeitzone beim Round-Trip. Frontend parst Backend-Zeitstempel deshalb explizit als UTC (
parseUtcTimestamp()infrontend/src/utils/datetime.ts). - Forgejo öffentlich über Reverse Proxy (
https://git.plus-immobilien.at): sobald 2FA aktiv ist, funktioniert das normale Account-Passwort fürgit push/pullüber HTTPS nicht mehr — ein Personal Access Token (Forgejo → Einstellungen → Anwendungen) muss als Passwort verwendet werden.ROOT_URLmuss in Forgejosdocker-compose.ymlauf die öffentliche Domain gesetzt sein, sonst generiert Forgejo falsche Klon-URLs.
Reverse Proxy / externer Zugriff
DevHub soll auch von außen über VPN/Reverse-Proxy erreichbar sein. TLS wird vom vorgeschalteten Proxy terminiert (z. B. Synology Reverse Proxy, Caddy oder Traefik), nicht von DevHub selbst:
- Uvicorn läuft mit
--proxy-headersund vertrautX-Forwarded-Proto. .env:COOKIE_SECURE=truesetzen, sobald HTTPS vor DevHub terminiert wird (sonst werden Session-Cookies mitSecure-Flag vom Browser verworfen).- Der Reverse Proxy muss WebSocket-Upgrade-Header weiterleiten (
Upgrade,Connection: upgrade) — relevant für den Live-Log-Endpoint.
Docker-Socket-Zugriff
Der Container mountet /var/run/docker.sock und läuft als root — das ist notwendig, damit DevHub Docker-Compose-Befehle für andere Container auf dem Host ausführen kann (dieselbe Technik wie Portainer/Dockge). Das gewährt root-äquivalenten Host-Zugriff; für ein internes Tool auf einer LAN-only bzw. VPN-abgesicherten NAS ist das ein akzeptierter Trade-off. Optionale spätere Härtung: ein docker-socket-proxy-Sidecar, der die API-Oberfläche einschränkt (nicht umgesetzt).
Projektstruktur
backend/app/ FastAPI-Anwendung (routes/, services/, plugins/, utils/)
frontend/src/ Vue 3 SPA (views/, components/, stores/, api/)
data/ bind-gemountet: SQLite-DB + SSH-Deploy-Key (gitignored)
Status
Umgesetzt: Auth mit Rollen (Admin/Entwickler), Benutzerverwaltung inkl. Passwort-Änderung, Projekt-CRUD mit Verzeichnis-Browser, Deploy/Restart/Stop/Start (Deploy läuft als Hintergrund-Task mit Fortschritts-Toast statt einer minutenlang offenen Anfrage), Deployment-Historie, Live-Logs per WebSocket, Projekt-Detailseite mit README (Markdown-gerendert), Git-Historie, maskierter .env-Ansicht, Plugin-Hook-Seam (noch ohne echtes Plugin), Plus-Immobilien-Branding, herunterladbare Setup-Hilfen (Einstellungen → "Setup-Hilfen"). Läuft produktiv auf der Synology NAS, verwaltet DevHub selbst sowie ooo-manager, Forgejo öffentlich über Reverse Proxy erreichbar.
Offen: echtes Beispiel-Plugin, Kategorien-Filter im Dashboard, optionales Client-seitiges Auto-Refresh der Docker-Metriken.