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
cc9e0786c2
189 files changed
+29792
No files matched your search
@@ -0,0 +1,86 @@
|
||||
# Eingang der oeffentlichen Instanz: liefert die Medien selbst aus und reicht
|
||||
# alles andere an Gunicorn weiter.
|
||||
#
|
||||
# 🔑 Warum es diesen Dienst ueberhaupt gibt: Django liefert unter
|
||||
# DJANGO_DEBUG=0 keine Medien aus (siehe web/vogelalbum/urls.py). Im Betrieb soll
|
||||
# das der Webserver tun -- nur stellt Coolify keinen: Traefik routet auf einen
|
||||
# Container und kennt das Volume nicht. Ohne diesen Dienst antwortet die
|
||||
# oeffentliche Instanz auf jedes /media/... mit 404, und jede Karte der
|
||||
# Sammlung haette ein kaputtes Bild.
|
||||
#
|
||||
# ⚠️ Whitenoise waere hier die falsche Loesung, obwohl es im Projekt schon
|
||||
# steckt: es liest seinen Bestand beim Start ein. Vogelfotos kommen aber
|
||||
# laufend dazu -- jede neue Sichtung waere bis zum naechsten Neustart
|
||||
# unsichtbar.
|
||||
|
||||
# 🔴 **Das Schema kommt von Traefik, nicht von uns.**
|
||||
#
|
||||
# Hier stand `proxy_set_header X-Forwarded-Proto $scheme;` — und `$scheme` ist
|
||||
# das Schema, mit dem jemand DIESEN nginx anspricht. Der lauscht auf einfachem
|
||||
# HTTP hinter Traefik, also stand dort immer `http`, und zwar überschreibend:
|
||||
# Traefik hatte den Kopf schon richtig auf `https` gesetzt, wir haben ihn
|
||||
# wieder kaputtgemacht.
|
||||
#
|
||||
# Folge: Django hielt sich trotz SECURE_PROXY_SSL_HEADER für unverschlüsselt
|
||||
# und baute **jede** absolute Adresse mit `http://` — Vogelfotos, Abzeichen-
|
||||
# Pins, Links in Mails. Im Browser unsichtbar, weil Traefik auf `https`
|
||||
# umleitet. **Auf dem Telefon ein Ausfall:** iOS blockt einfaches HTTP, ohne
|
||||
# der Umleitung zu folgen; das Album bleibt bilderlos.
|
||||
#
|
||||
# Die Abbildung nimmt den Kopf von vorne, wenn es einen gibt, und fällt sonst
|
||||
# auf das eigene Schema zurück — damit funktioniert dieselbe Konfiguration
|
||||
# auch ohne Traefik davor (`docker compose` zu Hause).
|
||||
#
|
||||
# ⚠️ `map` gehört auf die http-Ebene, nicht in den server-Block. Diese Datei
|
||||
# liegt als conf.d/default.conf genau dort.
|
||||
map $http_x_forwarded_proto $eingangs_schema {
|
||||
default $http_x_forwarded_proto;
|
||||
"" $scheme;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
server_name _;
|
||||
|
||||
# ⚠️ nginx erlaubt von Haus aus nur 1 MB pro Anfrage. Der Detector liefert
|
||||
# seine Bilder per POST ein; bei hoher Aufloesung ist ein Vogelfoto
|
||||
# schnell groesser, und die Kamera bekaeme ein 413 zurueck, waehrend im
|
||||
# Django-Protokoll nichts stuende.
|
||||
client_max_body_size 20m;
|
||||
|
||||
# Die Medien direkt vom Volume. Dieselbe Platte, die der Web-Dienst unter
|
||||
# MEDIA_ROOT=/data/media beschreibt.
|
||||
location /media/ {
|
||||
alias /data/media/;
|
||||
access_log off;
|
||||
expires 7d;
|
||||
add_header Cache-Control "public";
|
||||
|
||||
# ⚠️ **Hier liegen fremde Dateien auf unserer Herkunft.** Die Medien
|
||||
# kommen von Kameras und aus Uploads; sie werden zwar beim Annehmen
|
||||
# neu geschrieben (web/dex/bilder.py), aber dieser Pfad liefert aus,
|
||||
# was auf der Platte liegt — auch Altbestand von vorher.
|
||||
#
|
||||
# nosniff: Der Browser soll den Inhaltstyp nicht erraten. Ohne das
|
||||
# kann eine Datei, die wie ein Bild heißt, als etwas anderes
|
||||
# ausgeführt werden, und zwar same-origin mit dem Admin-Cookie.
|
||||
# Content-Disposition: notfalls herunterladen statt anzeigen.
|
||||
add_header X-Content-Type-Options "nosniff";
|
||||
add_header X-Frame-Options "SAMEORIGIN";
|
||||
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://web:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# ⚠️ Ohne diesen Kopf haelt Django sich fuer unverschluesselt und weist
|
||||
# jedes Formular mit einem CSRF-Fehler ab -- der Login im Admin
|
||||
# waere nicht benutzbar. Siehe die Abbildung oben: hier stand
|
||||
# $scheme, und das war genau die Luege, die es zu vermeiden galt.
|
||||
proxy_set_header X-Forwarded-Proto $eingangs_schema;
|
||||
proxy_redirect off;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user