Skip to content

0x3e4/pflanzn

Repository files navigation

Pflanzn logo

Pflanzn

Self-hostable plant management — track your collection, watering schedules, and species identification. Built as a PWA with mobile-first design and offline read access for cached data.

Sponsor Status Python FastAPI React TypeScript Vite MariaDB PWA Docker Hecate


Pflanzn home — recently captured plants, species snapshot, and overall counts

More screenshots — plants list, plant detail, identification
Plants list with thumbnails and tags Plant detail with timeline and care advice
Plants list Plant detail · timeline
Species identification via PlantNet
Species identification

Table of contents


Highlights

Catalogue Plants with photos, species info, care notes; tags and locations with GPS coordinates and an interactive Leaflet map.
Tracking Watering, fertilizing, image, and care-advice timelines with calendar view and per-activity colour coding.
Identification One-shot PlantNet integration — accepts JPG / PNG / WebP / HEIC; the backend normalises uploads to JPEG before forwarding.
AI care advice Optional integration with OpenAI, Claude, Mistral, HuggingFace, or a local Ollama instance — provider auto-detected from whichever API key is set.
Auto-watering Outdoor plants with reaches_rain=true are auto-watered when Open-Meteo reports rainfall above your threshold. Optional OpenWeatherMap key for current-weather display.
PWA Installable on phone or desktop; Workbox runtime caching (CacheFirst for images, StaleWhileRevalidate for API, NetworkFirst for navigation).
Sharing Token-based public collection links with a dedicated read-only view.
Auth modes no (open), local (JWT + Argon2 + Redis sessions with theft detection), or oidc (external IdP).
No telemetry No tracking. No ads.

Architecture

flowchart LR
    User([User / Browser])

    subgraph Frontend["Frontend · React 19 / Vite / nginx :4173"]
        SPA[PWA SPA]
    end

    subgraph Backend["Backend · FastAPI / Python 3.13 :8000"]
        API[REST API]
        Weather[Weather loop]
        Image[Image pipeline]
    end

    Redis[("Redis<br/>sessions / OAuth state")]
    Maria[("MariaDB<br/>persistence")]
    Uploads[("Disk uploads<br/>plants/ identifications/ locations/<br/>WebP: thumb · medium · original")]

    PlantNet[("PlantNet<br/>identification")]
    LLM[("LLM providers (optional)<br/>OpenAI · Claude · Mistral<br/>HuggingFace · Ollama")]
    Meteo[("Open-Meteo<br/>rainfall (no key)")]
    OWM[("OpenWeatherMap<br/>(optional · current weather)")]

    User --> Frontend
    Frontend -->|/api| Backend
    Backend --> Redis
    Backend --> Maria
    Backend --> Uploads
    Backend --> PlantNet
    Backend --> LLM
    Weather --> Meteo
    Weather --> OWM
Loading

Quick start

Note

Requires Docker + Docker Compose. The compose file is gitignored — copy from the .example first.

cp .env.example .env
cp docker-compose.yml.example docker-compose.yml
# Edit .env with your settings (database, API keys, domain, etc.)
docker compose up -d --build

The app will be reachable at the domain you configured in DOMAIN. See .env.example for all available settings.

Prebuilt images are published to GHCR on every push to main and on every semver tag (X.Y.Z) by build-images.yml:

  • ghcr.io/0x3e4/pflanzn-backend
  • ghcr.io/0x3e4/pflanzn-frontend (multi-stage build → nginx alpine)

main builds get the latest and main-<sha> tags; release builds get X.Y.Z, X.Y, and X tags. For production, pin to an explicit semver tag — see SECURITY.md for the supported-version policy.


Local development

Frontend

cd frontend
pnpm install
pnpm run dev          # Vite dev server with hot reload
pnpm run build        # TypeScript check + production build
pnpm run lint         # ESLint
pnpm test             # Vitest
pnpm run test:watch   # Vitest in watch mode

Requires Node ≥ 24.

Backend

The backend runs in Docker with --reload. For manual setup:

cd backend
poetry install
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

Tests:

cd backend
poetry run pytest tests/ -v

Linting

Pre-commit hooks (husky + lint-staged) run ESLint and Prettier on staged frontend files. The backend uses ruff:

poetry run ruff check backend/app/ --fix

Configuration

Everything is driven through environment variables — see .env.example for the full list. Frontend runtime configuration is fetched from GET /api/config so prebuilt images can be reconfigured per deployment without rebuilding.

Category Key variables
General SECRET_KEY, TZ, LOCALE, DOMAIN
Auth AUTH_MODE (no / local / oidc), SHOW_PROTECTED_VIEW, ADMIN_USER, ADMIN_EMAIL, ADMIN_PASSWORD
OIDC OIDC_NAME, OIDC_PROVIDER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET
Database DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_ROOT_PASSWORD, DB_NAME
Redis REDIS_URL
Features ENABLE_LOCATIONS
Identification PLANTNET_API_KEY, PLANTNET_LANGUAGE
AI providers (optional) LLM_LANGUAGE, OPENAI_API_KEY / OPENAI_MODEL_NAME, CLAUDE_API_KEY / CLAUDE_MODEL_NAME, MISTRALAI_API_KEY / MISTRALAI_API_URL / MISTRALAI_MODEL_NAME, HUGGINGFACE_API_KEY / HUGGINGFACE_MODEL_NAME, OLLAMA_URL / OLLAMA_MODEL_NAME. Provider auto-detected from whichever key is set; priority openai > claude > mistralai > huggingface > ollama.
Weather WEATHER_ENABLED, WEATHER_CHECK_INTERVAL_HOURS, WEATHER_RAINFALL_THRESHOLD_MM, OPENWEATHERMAP_API_KEY (optional)

Note

Legacy VITE_* names (VITE_DOMAIN, VITE_AUTH_MODE, …) still resolve as fallbacks in the backend, but they're misleading — these vars are read by the backend at runtime and exposed to the frontend via /api/config. Vite itself never sees them, so the prefix carries no meaning. New deployments should use the unprefixed names.


Tech stack

Component Technology
Backend Python 3.13, FastAPI, SQLAlchemy ORM, Uvicorn, Poetry
Database MariaDB
Cache Redis (sessions, OAuth state)
Frontend React 19, TypeScript 6, Vite 8, React Router v7, pnpm
Production serving nginx alpine (multi-stage Dockerfile, served on port 4173)
Auth JWT (HS256), Argon2, OIDC (authlib), Redis-backed sessions with IP + User-Agent theft detection
Identification PlantNet (HEIC / WebP / PNG / JPG accepted; normalised to JPEG before forwarding)
AI care helper OpenAI · Claude · Mistral · HuggingFace · Ollama (factory-pattern, each optional)
Weather Open-Meteo (rainfall, no key); OpenWeatherMap (optional, current weather display)
PWA vite-plugin-pwa + Workbox (CacheFirst / StaleWhileRevalidate / NetworkFirst)
Image pipeline Pillow + pillow_heif → 3 WebP variants per upload (thumb 300px, medium 800px, original 2000px) served via ?size=…
Deployment Docker Compose; GHCR images via GitHub Actions

Demo

A public demo is available at pflanzn.app — login with admin / admin. Data resets every 4 hours.


Support

Pflanzn is built and maintained as a labour of love. If it makes your plant-care routine easier, a small tip keeps the lights on:

Buy me a coffee on Ko-fi

A star on the GitHub repo is just as welcome — bug reports and pull requests too.


Made with ❤ in Austria

About

A simple and efficient plant management system.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors