Vogelalbum 1.0.0
Eine Kamera am Vogelhaus, ein Rechner der erkennt wer da war — und eine Sammlung, die sich von allein fuellt. Zwei neuronale Netze: eines findet den Vogel im Bild, eines bestimmt die Art. 148 heimische Arten, auf Deutschland eingeschraenkt und neu normiert — ohne diese Einschraenkung wird aus einer Kohlmeise eine nordamerikanische Schwesterart. Daraus entsteht ein Sammelalbum: nummerierte Seiten, gefundene Arten in Farbe, fehlende als Schatten, dazu Erstfang, Punkte, Stufen und 15 Abzeichen. Laeuft vollstaendig auf eigener Hardware. Kein Cloud-Dienst, kein Konto, keine Telemetrie; die Bilder verlassen das eigene Netz nicht. Zwei getrennt deploybare Teile: die Sammlung als Django-Anwendung, die Erkennung als eigener Dienst. Der Erkennungs-Dienst fragt selbst an und braucht deshalb weder eine erreichbare Adresse noch einen offenen Port — er laeuft hinter einem gewoehnlichen Router. Dabei: Firmware fuer Raspberry Pi und ESP32-S3, Anbindung an Home Assistant ueber MQTT, Oberflaeche auf Deutsch und Englisch, Einstellungen je Kamera aus dem Browser, und ein Weg fuer Fotos von unterwegs. AGPL-3.0.
This commit is contained in:
commit
c11fe9ab04
189 files changed
+29806
No files matched your search
@@ -0,0 +1,170 @@
|
||||
# Die Web-App auf Coolify stellen
|
||||
|
||||
Für den Umzug der Sammlung von einem Rechner im Heimnetz auf eine öffentlich
|
||||
erreichbare Instanz. Der Detector zieht **nicht** mit um — er braucht
|
||||
Sichtverbindung zur Kamera und bleibt zu Hause; er bekommt am Ende nur eine
|
||||
neue Adresse.
|
||||
|
||||
Vorlage ist `docker-compose.yml` im Wurzelverzeichnis, als
|
||||
**Docker-Compose-Anwendung aus dem Git-Repository**.
|
||||
|
||||
## Was dieses Setup von einem Ein-Container-Deploy unterscheidet
|
||||
|
||||
Zwei Dienste, und der Eingang ist **nicht** die Django-App:
|
||||
|
||||
```
|
||||
Traefik ──▶ medien (nginx, Port 8080) ──┬──▶ /media/… direkt vom Volume
|
||||
└──▶ alles andere ▶ web (Gunicorn, 8000)
|
||||
```
|
||||
|
||||
🔑 **Die Domain muss auf `medien` zeigen, nicht auf `web`.** Django liefert
|
||||
unter `DJANGO_DEBUG=0` keine Medien aus, und Coolify stellt keinen Webserver
|
||||
davor, der das übernehmen könnte — Traefik routet auf einen Container und kennt
|
||||
das Volume nicht. Zeigt die Domain auf `web`, läuft die Seite scheinbar
|
||||
normal, aber **jedes Vogelfoto ist 404**: die Sammlung ist das Einzige, was
|
||||
dieses Projekt ist, und sie wäre leer. Die ausführliche Begründung steht in
|
||||
`nginx-media.conf`.
|
||||
|
||||
## 1. Anwendung anlegen
|
||||
|
||||
* Typ **Docker Compose**, Quelle das Git-Repository, Branch `main`,
|
||||
Compose-Datei `docker-compose.yml`.
|
||||
* Ist das Repository privat, muss Coolify über die Gitea-Anbindung
|
||||
(GitHub-App-Äquivalent bzw. Deploy-Key) darauf zugreifen können — dasselbe
|
||||
Verfahren wie bei den anderen Projekten auf derselben Instanz.
|
||||
|
||||
## 2. Domain auf den Dienst `medien`
|
||||
|
||||
In Coolify hat bei einer Compose-Anwendung **jeder Dienst sein eigenes
|
||||
Domain-Feld**. Die Domain gehört an `medien` (Port 8080). `web` bekommt
|
||||
**keine** Domain — der Dienst wird nur intern angesprochen.
|
||||
|
||||
Danach prüfen, dass der DNS-Eintrag der Domain auf die Coolify-Maschine zeigt.
|
||||
|
||||
## 3. Umgebungsvariablen
|
||||
|
||||
Pflicht:
|
||||
|
||||
| Variable | Wert |
|
||||
|---|---|
|
||||
| `DJANGO_ALLOWED_HOSTS` | die Domain, z. B. `vogelalbum.gaertner.onl` |
|
||||
| `DJANGO_CSRF_TRUSTED_ORIGINS` | dieselbe Domain **mit** `https://` |
|
||||
|
||||
Optional, aber meist gewollt:
|
||||
|
||||
| Variable | Wert |
|
||||
|---|---|
|
||||
| `DJANGO_SECRET_KEY` | leer lassen ist in Ordnung — der Entrypoint erzeugt einen und legt ihn aufs Volume, er bleibt über Neustarts stabil |
|
||||
| `VOGELALBUM_INGEST_TOKEN` | **leer lassen**, wenn jede Kamera im Admin registriert ist und ihren eigenen Token hat. Leer schaltet den Sammel-Token ab, und das ist für eine öffentliche Instanz das Richtige |
|
||||
| `VOGELALBUM_LESE_TOKEN` | nur nötig, wenn Home Assistant `/api/zustand/` abfragt. **Leer heißt: der Endpunkt antwortet mit 401** — wer die Einbindung zu Hause schon hat und das hier vergisst, sucht den Fehler im Heimnetz |
|
||||
|
||||
Nicht anfassen, die stehen fest in `docker-compose.yml`:
|
||||
`DJANGO_DEBUG=0`, `VOGELALBUM_LIVE_ERLAUBT=0`, `SQLITE_DIR=/data`,
|
||||
`MEDIA_ROOT=/data/media`.
|
||||
|
||||
⚠️ **Lässt man Coolify ein Geheimnis erzeugen, darf es kein `$` enthalten.**
|
||||
Docker Compose liest `$xxx` als Variable und zerstört den Wert dabei still.
|
||||
Selbst erzeugen mit `openssl rand -base64 32`.
|
||||
|
||||
🔒 `VOGELALBUM_LIVE_ERLAUBT=0` ist der Grund, warum auf der öffentlichen Instanz
|
||||
kein Livebild einschaltbar ist — auch nicht mit einem Fehlklick im Admin. Ein
|
||||
Bild aus dem eigenen Garten soll nicht an einem Häkchen hängen.
|
||||
|
||||
## 4. Volume
|
||||
|
||||
Das Compose-File deklariert das benannte Volume `vogelalbum-data` und hängt es in
|
||||
beide Dienste (`web` beschreibbar, `medien` nur lesend). Coolify übernimmt das;
|
||||
es muss nichts von Hand angelegt werden.
|
||||
|
||||
⚠️ **Dort liegen Datenbank *und* Fotos.** `SQLITE_DIR` und `MEDIA_ROOT` zeigen
|
||||
beide auf `/data`. Fehlt das Volume oder zeigt eine der beiden Variablen
|
||||
woandershin, ist die Sammlung nach jedem Deploy weg.
|
||||
|
||||
## 5. Erster Start
|
||||
|
||||
Der Entrypoint erledigt `migrate`, `collectstatic`, `seed_dex` und holt
|
||||
anschließend die Referenzfotos der Arten von Wikipedia.
|
||||
|
||||
⏱️ **Der erste Start dauert deshalb mehrere Minuten** — rund 150 Arten, und
|
||||
Wikimedia drosselt, weshalb zwischen den Abrufen gewartet wird. Das ist kein
|
||||
hängender Deploy. Jeder weitere Start überspringt alle Arten, die schon ein
|
||||
Foto haben, und kostet keine einzige Anfrage mehr.
|
||||
|
||||
Danach steht eine leere Sammlung: Artenkatalog vollständig, keine Sichtungen.
|
||||
|
||||
## 6. Eine bestehende Sammlung mitnehmen
|
||||
|
||||
Wer von einer Heiminstanz umzieht, bringt Datenbank und Fotos mit. Beides liegt
|
||||
beim Umzugspaket im selben Layout, in dem es auf dem Volume liegen muss:
|
||||
|
||||
```
|
||||
db.sqlite3
|
||||
media/sightings/…
|
||||
media/crops/…
|
||||
media/referenz/…
|
||||
```
|
||||
|
||||
🔑 **Eine 1:1-Kopie, keine Neueinlieferung über die API.** Nur so kommen die
|
||||
Kamera-Tokens, die Schwellwerte je Kamera, Handbestimmungen, Abzeichen und
|
||||
Erstfang-Vermerke unverändert mit. Die Kameras zu Hause liefern danach mit
|
||||
**ihrem bisherigen Token** weiter — es muss nichts neu registriert werden.
|
||||
|
||||
Auf der Heiminstanz ein konsistentes Paket bauen (`VACUUM INTO` kopiert die
|
||||
Datenbank sauber, auch während die App läuft):
|
||||
|
||||
```sh
|
||||
cd /pfad/zum/vogelalbum
|
||||
python - <<'PY'
|
||||
import sqlite3
|
||||
sqlite3.connect('web/db.sqlite3').execute("VACUUM INTO '/tmp/db.sqlite3'")
|
||||
PY
|
||||
mkdir -p /tmp/umzug/media
|
||||
mv /tmp/db.sqlite3 /tmp/umzug/
|
||||
cp -r web/media/sightings web/media/crops web/media/referenz /tmp/umzug/media/
|
||||
tar czf /tmp/vogelalbum-daten.tgz -C /tmp/umzug db.sqlite3 media
|
||||
```
|
||||
|
||||
Das Paket auf die Coolify-Maschine legen, dann dort:
|
||||
|
||||
```sh
|
||||
# Anwendung anhalten (in Coolify: Stop) — SQLite nicht im Betrieb tauschen.
|
||||
VOL=$(docker volume ls -q | grep vogelalbum-data | head -1)
|
||||
docker run --rm -v "$VOL":/data -v /root:/rein:ro alpine \
|
||||
sh -c 'tar xzf /rein/vogelalbum-daten.tgz -C /data && chown -R 0:0 /data && ls -la /data'
|
||||
# Anwendung wieder starten.
|
||||
```
|
||||
|
||||
Der Container läuft als `root`, daher `chown -R 0:0`.
|
||||
|
||||
## 7. Nach dem Umzug prüfen
|
||||
|
||||
```sh
|
||||
curl -sI https://DEINE-DOMAIN/ | head -1 # 200
|
||||
curl -sI https://DEINE-DOMAIN/sichtungen/ | head -1
|
||||
```
|
||||
|
||||
Und im Browser: **eine Sichtungskarte muss ihr Foto zeigen.** Ein 404 auf
|
||||
`/media/…` heißt, die Domain hängt am falschen Dienst (Schritt 2).
|
||||
|
||||
## 8. Die Kameras zu Hause umstellen
|
||||
|
||||
Jeder Detector und jede weitere Anbindung zeigt noch auf die alte Adresse im
|
||||
Heimnetz. Umzustellen ist:
|
||||
|
||||
* **jeder Detector**: `VOGELALBUM_API` auf `https://DEINE-DOMAIN` (in
|
||||
`~/.vogelalbum.env` bzw. `--api`), danach neu starten. Laufen zwei Kameras,
|
||||
gibt es zwei Detector-Prozesse — womöglich auf zwei verschiedenen Maschinen.
|
||||
* **Home Assistant**, falls eingebunden: neue Adresse und `VOGELALBUM_LESE_TOKEN`.
|
||||
|
||||
Dass eine Kamera angekommen ist, sieht man im Admin an `letzter_kontakt` —
|
||||
der Konfigurationsabruf setzt es, auch ohne einen einzigen Vogel.
|
||||
|
||||
## 9. Was bewusst nicht mit umzieht
|
||||
|
||||
* **Der Detector.** Er braucht die Modelle, Rechenzeit und das Kameranetz.
|
||||
* **Das Livebild.** `VOGELALBUM_LIVE_ERLAUBT=0`, siehe oben.
|
||||
* **Der Störungswächter** (`vogelalbum-stoerungen.service`). 🔑 Er darf nicht auf
|
||||
derselben Maschine laufen wie das, was er überwacht, und nicht an derselben
|
||||
Stromversorgung: beim Stromausfall am 30.09.2026 war er von 22:31 bis 06:19
|
||||
stumm, weil er selbst tot war. Wer das Ausbleiben von Sichtungen bemerken
|
||||
soll, muss von außen schauen.
|
||||
Reference in new issue
Block a user