# Gardomatic Gardomatic ist eine mobile, mehrbenutzerfähige Webanwendung zur gemeinsamen Organisation von Gärten. Sie bündelt Pflanzenwissen, Pflanzorte, anstehende Arbeiten, Bilder und ein Gartentagebuch in einer Anwendung. Das Projekt besteht aus einer JSON-API und einer serverseitig gerenderten Webanwendung in Go. PostgreSQL speichert alle Fachdaten. Das Frontend ist mobile-first, bleibt in den wesentlichen Abläufen ohne JavaScript nutzbar und verwendet htmx für komfortable Teilaktualisierungen. Eine installierbare PWA stellt eine Offline-App-Shell bereit; die eigentlichen Gartendaten bleiben serverseitig. > **Projektstatus:** Gardomatic wird aktiv entwickelt. Datenmodell, API und > Bedienoberfläche können sich noch verändern. ## Funktionen - mehrere voneinander isolierte Gärten pro Benutzer - gemeinsame Gartenpflege mit Einladungen, Rollen und Berechtigungen - Verwaltung von Arten, Sorten, konkreten Pflanzen und Pflanzorten - Pflegehinweise, Kategorien, Tags und Bilder - Aufgaben mit Fälligkeitsfenstern, Prioritäten und Wiederholungen - Aufgabenvorlagen pro Art und automatische, idempotente Aufgabenerzeugung - Aufgabenansicht und Kalenderdarstellung - Gartentagebuch, Pinnwand und Bildbibliothek - Benutzerkonto, Aktivierung, Sitzungsverwaltung und Passwortänderung - administrative Einladung, Anonymisierung und Verwaltung von Benutzern, Rollen und Anwendungseinstellungen inklusive maskierter Laufzeitkonfiguration und Testmailversand - Kommandozeilenwerkzeug zur Benutzer- und Datenbankadministration ## Architektur ```text Browser │ ▼ Webanwendung :4040 ──► typisierter API-Client │ ▼ JSON-API :4000 │ ▼ Storage-Schnittstellen │ ▼ PostgreSQL 16 ``` Die API ist die maßgebliche Sicherheits- und Fachgrenze. Sie verwaltet Authentifizierung, Sitzungen, Autorisierung, Validierung und Datenzugriff. Die Webanwendung greift nicht direkt auf die Datenbank zu, sondern leitet das Session-Cookie eines Requests über `lib/client` an die API weiter. Gartenbezogene Browserrouten beginnen mit `/g/{gardenID}/`, die entsprechenden API-Ressourcen mit `/v1/gardens/{gardenID}/`. Ein Garten bildet eine abgeschlossene Daten- und Berechtigungsgrenze. ### Programme | Programm | Aufgabe | Standardadresse | | --- | --- | --- | | `cmd/api` | JSON-API und Fachlogik | `http://localhost:4000` | | `cmd/web` | serverseitig gerenderte Weboberfläche | `http://localhost:4040` | | `cmd/cli` | Administration direkt über PostgreSQL | keine | ## Voraussetzungen Für den vollständigen lokalen Betrieb werden benötigt: - Go gemäß der Version in `go.mod` (aktuell Go 1.26 oder neuer) - PostgreSQL 16 - optional Docker oder Podman mit Compose-Unterstützung - [`golang-migrate`](https://github.com/golang-migrate/migrate) für lokale Migrationen und Integrationstests - `make` für die bereitgestellten Entwicklungsbefehle Die Compose-Variante bringt PostgreSQL und `golang-migrate` bereits als Container mit. Go wird dort nur benötigt, wenn das Administrations-CLI auf dem Host verwendet werden soll. ## Schnellstart mit Compose 1. Konfiguration anlegen und mindestens das Datenbankpasswort ändern: ```sh cp .env.example .env ``` `POSTGRES_PASSWORD` und das Passwort in `GARDOMATIC_DB_DSN` sollten übereinstimmen, damit auch lokale CLI- und Testbefehle dieselbe Datenbank erreichen können. `.env` ist von Git ausgeschlossen. 2. Datenbank, Migrationen, API und Webanwendung starten: ```sh docker compose up --build ``` Bei Podman kann je nach Installation stattdessen `podman compose` oder `podman-compose` verwendet werden. 3. Einen ersten aktiven Benutzer anlegen. Dazu in einem zweiten Terminal die DSN aus der lokalen `.env` setzen und das CLI starten: ```sh export GARDOMATIC_DB_DSN='postgres://gardomatic:change-me@localhost:5432/gardomatic?sslmode=disable' go run ./cmd/cli users create \ --name 'Admin' \ --email 'admin@example.com' \ --role application:admin \ --active \ --generate-password ``` Das generierte Passwort wird einmalig ausgegeben. 4. [http://localhost:4040](http://localhost:4040) öffnen und anmelden. Die API-Gesundheitsprüfung ist unter [http://localhost:4000/v1/healthcheck](http://localhost:4000/v1/healthcheck), die Web-Gesundheitsprüfung unter [http://localhost:4040/ping](http://localhost:4040/ping) erreichbar. Eine lesbare Statusseite mit API- und Serverinformationen steht unter [http://localhost:4040/healtcheck](http://localhost:4040/healtcheck) bereit. Den Stack beendet `docker compose down`. Die PostgreSQL-Daten liegen im benannten Volume `gardomatic-postgres-data` und bleiben dabei erhalten. `docker compose down -v` löscht dieses Volume und damit die lokale Datenbank dauerhaft. ## Lokale Entwicklung ohne Anwendungscontainer PostgreSQL und die Migrationen können weiterhin über Compose laufen: ```sh cp .env.example .env docker compose up -d postgres migrate ``` Der `Makefile` bindet eine lokale, nicht versionierte `.envrc` ein. Mindestens die Datenbankverbindung muss darin für API-, CLI- und Datenbankbefehle exportiert sein: ```sh export GARDOMATIC_DB_DSN='postgres://gardomatic:change-me@localhost:5432/gardomatic?sslmode=disable' ``` Danach API und Webanwendung in getrennten Terminals starten: ```sh make run/api ``` ```sh make run/web ``` Alternativ können beide Programme direkt mit `go run ./cmd/api` und `go run ./cmd/web` gestartet werden, sofern die benötigten Umgebungsvariablen in der Shell gesetzt sind. Die API benötigt zwingend `GARDOMATIC_DB_DSN`; alle anderen Entwicklungswerte besitzen sinnvolle Standardwerte. ## Konfiguration `.env.example` dokumentiert eine vollständige lokale Konfiguration. Geheimnisse gehören ausschließlich in `.env`, `.envrc`, einen Secret Store oder die Produktionsumgebung und dürfen nicht eingecheckt werden. ### Compose-Variablen | Variable | Standard/Beispiel | Beschreibung | | --- | --- | --- | | `POSTGRES_DB` | `gardomatic` | Datenbankname des PostgreSQL-Containers | | `POSTGRES_USER` | `gardomatic` | Datenbankbenutzer des Containers | | `POSTGRES_PASSWORD` | `change-me` | Datenbankpasswort; lokal unbedingt ändern | | `POSTGRES_PORT` | `5432` | auf dem Host veröffentlichter PostgreSQL-Port | | `API_PORT` | `4000` | auf dem Host veröffentlichter API-Port | | `WEB_PORT` | `4040` | auf dem Host veröffentlichter Web-Port | ### Laufzeitvariablen | Variable | Standard | Verwendung | | --- | --- | --- | | `GARDOMATIC_ENV` | `development` | `development`, `test` oder `production` | | `GARDOMATIC_DB_DSN` | erforderlich | PostgreSQL-Verbindungszeichenfolge für API und CLI | | `GARDOMATIC_DB_MAX_OPEN_CONNS` | `25` | maximale offene DB-Verbindungen | | `GARDOMATIC_DB_MAX_IDLE_CONNS` | `25` | maximale ungenutzte DB-Verbindungen | | `GARDOMATIC_DB_MAX_IDLE_TIME` | `15m` | maximale Leerlaufzeit einer DB-Verbindung | | `GARDOMATIC_API_HOST` | leer | Bind-Adresse der API | | `GARDOMATIC_API_PORT` | `4000` | Listener-Port der API | | `GARDOMATIC_WEB_HOST` | leer | Bind-Adresse der Webanwendung | | `GARDOMATIC_WEB_PORT` | `4040` | Listener-Port der Webanwendung | | `GARDOMATIC_API_BASE_URL` | `http://localhost:4000` | API-Adresse für den Web-Client | | `GARDOMATIC_WEB_BASE_URL` | `http://localhost:4040` | öffentliche Basis-URL für Links aus API und CLI | | `GARDOMATIC_SESSION_COOKIE_NAME` | `gardomatic_session` | gemeinsamer Name des Session-Cookies | | `GARDOMATIC_SESSION_LIFETIME` | `12h` | absolute Lebensdauer einer Sitzung | | `GARDOMATIC_SESSION_IDLE_TIMEOUT` | `30m` | Ablaufzeit bei Inaktivität | | `GARDOMATIC_COOKIE_SECURE` | `false` | nur HTTPS-Cookies; in Produktion zwingend `true` | | `GARDOMATIC_RATE_LIMIT_ENABLED` | `true` | API-Ratenbegrenzung aktivieren | | `GARDOMATIC_RATE_LIMIT_RPS` | `10` | erlaubte Requests pro Sekunde | | `GARDOMATIC_RATE_LIMIT_BURST` | `40` | kurzfristig erlaubte Request-Spitze | | `GARDOMATIC_CORS_TRUSTED_ORIGINS` | leer/lokal gesetzt | kommaseparierte erlaubte Origins | ### E-Mail-Versand | Variable | Standard | Beschreibung | | --- | --- | --- | | `GARDOMATIC_SMTP_MODE` | `file` | `file` für Entwicklung oder `smtp` | | `GARDOMATIC_SMTP_HOST` | leer | SMTP-Server | | `GARDOMATIC_SMTP_PORT` | `25` | SMTP-Port | | `GARDOMATIC_SMTP_USERNAME` | leer | SMTP-Benutzername | | `GARDOMATIC_SMTP_PASSWORD` | leer | SMTP-Passwort | | `GARDOMATIC_SMTP_SENDER` | `gardomatic@localhost` | Absenderadresse | | `GARDOMATIC_SMTP_FILE_PATH` | `/tmp/gardomatic-mails.log` | Ausgabe im `file`-Modus | Im Entwicklungsmodus schreibt der voreingestellte `file`-Mailer E-Mails in die angegebene Datei. In der Compose-API liegt diese Datei innerhalb des Containers; sie kann beispielsweise mit `docker compose exec api cat /tmp/gardomatic-mails.log` gelesen werden. ## Datenbankmigrationen Migrationen liegen als fortlaufend nummerierte Up-/Down-Paare in `internal/storage/postgres/migrations`. Eine neue Migration anlegen: ```sh make db/migrations/new name=describe_change ``` Alle ausstehenden Migrationen anwenden: ```sh make db/migrations/up ``` Alle angewendeten Migrationen zurücknehmen: ```sh make db/migrations/down ``` `migrate down` ohne Schrittzahl setzt das gesamte Schema zurück und kann sämtliche Anwendungsdaten löschen. Die Make-Ziele verwenden `GARDOMATIC_DB_DSN`; vor Up- und besonders Down-Befehlen daher immer prüfen, auf welche Datenbank die Variable zeigt. Bereits veröffentlichte Migrationen sollten nicht verändert werden; Schemaänderungen erhalten eine neue Migration. ## Tests und Qualitätsprüfungen Alle Unit- und Handler-Tests ausführen: ```sh go test ./... ``` Die PostgreSQL-Integrationstests werden ohne `GARDOMATIC_TEST_DB_DSN` automatisch übersprungen. Für einen vollständigen Integrationslauf eine separate Testdatenbank konfigurieren und zuerst migrieren: ```sh export GARDOMATIC_TEST_DB_DSN='postgres://user:password@localhost/gardomatic_test?sslmode=disable' migrate -path ./internal/storage/postgres/migrations -database "$GARDOMATIC_TEST_DB_DSN" up go test -count=1 ./internal/storage/postgres ``` Alternativ migriert folgendes Ziel die in `GARDOMATIC_DB_DSN` konfigurierte Datenbank und führt die Integrationstests dagegen aus: ```sh make test/integration ``` Dieses Ziel ausschließlich mit einer entbehrlichen Testdatenbank verwenden. Der vollständige lokale Qualitätslauf umfasst Modulprüfung, `go vet`, `staticcheck` und Tests mit Race Detector: ```sh make audit ``` Quelltext modernisieren und formatieren: ```sh make tidy ``` `make tidy` kann `go.mod`, `go.sum`, den Vendor-Bestand und Go-Quelltext verändern. Den resultierenden Diff deshalb immer prüfen. Die CI unter `.github/workflows/ci.yml` ist dafür konfiguriert, PostgreSQL zu migrieren, Module und Quelltext zu prüfen, alle Tests mit Race Detector auszuführen und alle drei Programme zu bauen. ## Administration mit dem CLI Das CLI benötigt für Datenbankbefehle `GARDOMATIC_DB_DSN`. Globale Optionen stehen vor dem Unterbefehl. Mit `--json` liefert es maschinenlesbare Ausgabe; `--yes` bestätigt bewusst konfigurierte Produktionsoperationen. Häufige Befehle: ```sh go run ./cmd/cli users list go run ./cmd/cli users show --email alice@example.com go run ./cmd/cli users invite --email alice@example.com --send-email go run ./cmd/cli users activate --email alice@example.com go run ./cmd/cli users deactivate --email alice@example.com go run ./cmd/cli users reset-password --email alice@example.com --generate-password go run ./cmd/cli db ping ``` Passwörter werden absichtlich nicht als Kommandozeilenargument angenommen. Sie werden sicher abgefragt, generiert oder mit `--password-stdin` von der Standardeingabe gelesen. Eine ausführliche Referenz mit Beispielen enthält [`doc/cli.md`](doc/cli.md). ## Build Einzelne Programme für das lokale System und Linux/AMD64 bauen: ```sh make build/api make build/web make build/cli ``` Alle Programme bauen: ```sh make build/all ``` Artefakte landen unter `bin/` und werden nicht versioniert. Der `Dockerfile` enthält getrennte, minimale Laufzeit-Targets für API und Web. ## Projektstruktur ```text cmd/ api/ Start und Konfiguration der JSON-API web/ Start und Konfiguration der Webanwendung cli/ Administrations-CLI internal/ api/ API-Handler, Middleware und Fachabläufe auth/ Passwort- und Tokenfunktionen mailer/ Mailversand und Vorlagen platform/ kleine technische Basispakete storage/ Modelle und Storage-Schnittstellen postgres/ PostgreSQL-Implementierungen und Migrationen web/ Handler, Templates und statische Assets lib/ client/ typisierter Go-Client für die JSON-API doc/ Planung und weiterführende Dokumentation remote/ Produktionsbeispiele für systemd request/ manuelle HTTP-Beispielanfragen ``` ## Entwicklungskonventionen Bei Änderungen sind insbesondere folgende Grundsätze verbindlich: - idiomatischer, mit `gofmt` formatierter Go-Code - klare Verantwortlichkeiten und bestehende Schichtengrenzen - Wiederverwendung vorhandener Helfer und Fachlogik statt Codeduplizierung - Tests für neues oder korrigiertes Verhalten, soweit technisch möglich - neue Migrationen statt Änderungen an bereits veröffentlichten Migrationen - keine Zugangsdaten oder lokalen Umgebungsdateien im Repository Ausführliche Arbeitsregeln für Coding Agents und Beitragende stehen in [`AGENTS.md`](AGENTS.md). ## Beiträge Beiträge sind willkommen. Der vollständige Ablauf, Qualitätsanforderungen und der Umgang mit Fremdmaterial sind in [`CONTRIBUTING.md`](CONTRIBUTING.md) beschrieben. Vor dem ersten Pull Request müssen Beitragende die [`Contributor License Agreement`](CLA.md) lesen und im Pull Request selbst akzeptieren. Beitragende behalten ihr Copyright, räumen dem Projektinhaber jedoch die notwendigen Rechte ein, den Beitrag sowohl unter der öffentlichen Projektlizenz als auch unter separaten kommerziellen oder proprietären Lizenzen zu verwenden. Beiträge im Namen eines Unternehmens müssen vorab abgestimmt werden. ## Deployment Der `Dockerfile` kann Images für API und Web erzeugen. Unter `remote/production` liegen außerdem Beispielkonfigurationen für systemd. Die Präfixe `production/*`, `testserver/*` und `demo/*` verwenden denselben Deployment-Ablauf, aber getrennte Zielhosts und Laufzeitkonfigurationen. Sie sind auf die vorhandene Gardomatic-Infrastruktur zugeschnitten, führen Migrationen sowie Dienstneustarts aus und sollten vor jeder Verwendung geprüft werden. Die drei SSH-Verbindungen werden gemeinsam in `config.mk` konfiguriert. Eine kommentierte Vorlage mit Zielhost, SSH-Admin, Port, optionalem privaten Schlüssel und Zielarchitektur steht in `config.mk.example`. Die Präfixe der Variablen sind `PRODUCTION_`, `TESTSERVER_` und `DEMO_`. Der SSH-Admin ist der vom Hoster oder bei der LXC-Erstellung bereitgestellte Benutzer (`root`, `ubuntu` oder ähnlich) und benötigt Root-Rechte oder passwortloses `sudo`. Das Server-Setup unter `remote/setup/provision-server.sh` liest seine Konfiguration aus einer zum Ziel gehörenden Datei. Alle drei Dateien werden von `make config/init` mit Modus `0600` angelegt und bleiben von Git ausgeschlossen: | Make-Präfix | SSH-Einstellungen | Laufzeitkonfiguration | | --- | --- | --- | | `production/` | `PRODUCTION_*` in `config.mk` | `remote/setup/.env` | | `testserver/` | `TESTSERVER_*` in `config.mk` | `remote/setup/.env.testserver` | | `demo/` | `DEMO_*` in `config.mk` | `remote/setup/.env.demo` | Jede Laufzeitdatei benötigt eine eigene Datenbank-DSN, eigene Passwörter und die URL des jeweiligen Servers. Für Produktion wird `GARDOMATIC_ENV=production` verwendet. Der Testserver kann `GARDOMATIC_ENV=test` und dateibasierten Mailversand nutzen. Die öffentliche Demo läuft gehärtet mit `GARDOMATIC_ENV=production`, aber mit `GARDOMATIC_DEMO_RESET_ENABLED=true`, `GARDOMATIC_DEMO_ACCOUNT_EMAIL=demo@example.com` und `GARDOMATIC_SMTP_MODE=file`. Ein gemeinsames Runtime-Environment wird absichtlich nicht verwendet, damit ein Demo-Deployment keine Produktionsdatenbank erreichen kann. `*/provision` überträgt das Setup und streamt ausschließlich die zugehörige Konfiguration über SSH, ohne die Quelldatei dauerhaft auf dem Server abzulegen. Das Provisioning legt den gesperrten Servicebenutzer `gardomatic` ohne Login, SSH-Schlüssel oder sudo-Rechte an und installiert die Laufzeitwerte auf dem jeweiligen Server als `/etc/gardomatic/gardomatic.env`. Ein vollständiger Erstbetrieb besteht aus: ```sh make config/init # Die benötigten lokalen Konfigurationsdateien bearbeiten, dann: make production/provision make production/deploy make production/create-admin # Entsprechend für die anderen Instanzen: make testserver/provision make testserver/deploy make testserver/create-admin make demo/provision make demo/deploy ``` Jedes Präfix bietet `connect`, `provision`, `deploy` und `create-admin`. `*/deploy` überträgt Binärdateien, Migrationen, systemd-Units und die Admin-Hilfe, wendet Migrationen an und startet die Dienste. `*/create-admin` erstellt interaktiv einen aktivierten Anwendungsadministrator und gibt das generierte Passwort einmalig aus. Für Produktion gelten mindestens folgende Anforderungen: - `GARDOMATIC_ENV=production` - HTTPS am Reverse Proxy - `GARDOMATIC_COOKIE_SECURE=true` - starke, extern verwaltete Zugangsdaten - eingeschränkter Datenbankzugriff und regelmäßige Backups - korrekte öffentliche Web-URL und vertrauenswürdige CORS-Origins - SMTP statt dateibasiertem Mailversand, sofern E-Mails zugestellt werden sollen ### Öffentliche Demo-Instanz Für eine öffentliche Demo empfiehlt sich eine eigene Gardomatic-Installation mit eigener PostgreSQL-Datenbank und eigener Domain, nicht ein Demo-Garten in einer Produktivdatenbank. Die Anwendung sollte weiterhin mit `GARDOMATIC_ENV=production`, HTTPS und sicheren Cookies laufen. Ausschließlich auf dieser wegwerfbaren Instanz wird `GARDOMATIC_DEMO_RESET_ENABLED=true` gesetzt. `GARDOMATIC_DEMO_ACCOUNT_EMAIL=demo@example.com` schützt das gemeinsame Konto serverseitig: Profil, E-Mail-Adresse, Passwort, Passwort-Zurücksetzen und die globale Sitzungsverwaltung können von Besuchern nicht verändert werden. Die Garteninhalte bleiben vollständig bedienbar. Der Befehl `gardomatic-cli --yes demo reset --password-stdin` ersetzt alle Benutzer- und Gartendaten atomar durch einen zeitlich aktuellen Beispieldatensatz. Er erstellt den Garten `Sonnengarten` mit drei Mitgliedern, Einladung und eigener Gartenrolle, acht detaillierten Orten, zwölf Arten, Pflegehinweisen, Aufgabenvorlagen, sechzehn Pflanzen in unterschiedlichen Zuständen, fälligen, wiederkehrenden und erledigten Aufgaben, Tags, längeren Tagebuch- und Pinnwandeinträgen sowie einer bebilderten Medienbibliothek. Das gewünschte Login lautet standardmäßig `demo@example.com`; das Passwort wird nicht im Repository gespeichert. Die mitgelieferte systemd-Konfiguration setzt die Demo jede Nacht um 04:00 Uhr zurück. Für einen frisch aufgesetzten Demo-Server: 1. In `remote/setup/.env.demo` `GARDOMATIC_DEMO_RESET_ENABLED='true'`, `GARDOMATIC_DEMO_ACCOUNT_EMAIL='demo@example.com'` und `GARDOMATIC_SMTP_MODE='file'` setzen. 2. Den Server mit `make demo/provision` provisionieren. 3. Auf dem Demo-Server das Passwort verdeckt einlesen und mit `Enter` bestätigen: ```sh make demo/connect sudo install -d -m 0750 -o root -g gardomatic /etc/gardomatic systemd-ask-password 'Demo-Passwort:' | \ sudo tee /etc/gardomatic/demo-password >/dev/null sudo chown root:gardomatic /etc/gardomatic/demo-password sudo chmod 0640 /etc/gardomatic/demo-password exit ``` 4. `make demo/deploy` ausführen. Das Deployment befüllt die Demo sofort und aktiviert `gardomatic-demo-reset.timer`. Mit `systemctl list-timers gardomatic-demo-reset.timer` lässt sich der nächste Lauf prüfen; `journalctl -u gardomatic-demo-reset.service` zeigt die Reset-Läufe. Ein Reverse Proxy sollte zusätzlich Request-Größen begrenzen und die vorhandene Rate-Limitierung aktiviert lassen. Für die Demo sollte der Mailversand im `file`-Modus bleiben, damit Besucher keine E-Mails an Dritte auslösen können. Da Besucher Schreibrechte besitzen, dürfen auf diesem Host keine anderen schützenswerten Anwendungen oder Datenbanken mit denselben Zugangsdaten betrieben werden. ## Weiterführende Dokumentation - [`doc/planung.md`](doc/planung.md) – Produktbeschreibung, Leitplanken und Fahrplan - [`doc/cli.md`](doc/cli.md) – vollständige Bedienung des Administrations-CLI - [`doc/issues.md`](doc/issues.md) – bekannte Themen und Arbeitsnotizen - [`doc/ideen.md`](doc/ideen.md) – mögliche spätere Erweiterungen ## Lizenz Gardomatic steht unter der [PolyForm Noncommercial License 1.0.0](LICENSE). Sie erlaubt Nutzung, Veränderung und Weitergabe für nichtkommerzielle Zwecke unter den Bedingungen der Lizenz. Kommerzielle Nutzung ist von dieser Lizenz nicht abgedeckt und erfordert eine separate, kostenpflichtige Lizenzvereinbarung. Anfragen können an [alex@kleiax.de](mailto:alex@kleiax.de) gerichtet werden. Für Beiträge Dritter gilt zusätzlich die [`Gardomatic Contributor License Agreement`](CLA.md). Wegen des Ausschlusses kommerzieller Nutzung ist Gardomatic „source-available“, aber keine Open-Source-Software nach der Definition der Open Source Initiative.