Files
moneyfy/README.md
T
Jonas MenzelandClaude Opus 5 c110340628
CI / backend (push) Successful in 2m49s
Images bauen / build (frontend) (push) Successful in 3m12s
Images bauen / build (backend) (push) Successful in 3m22s
CI / frontend (push) Successful in 6m20s
feat: Kommando zum Setzen des Passworts
Die Erstanlage beim Start greift nur, solange die Benutzertabelle leer ist. Wer
.env.example zuerst unverändert übernimmt, bekommt einen Benutzer mit dem
Platzhalter; ein späteres Ändern von MONEYFY_ADMIN_PASSWORD bleibt danach
wirkungslos. Das Kommando setzt das Passwort direkt, legt den Benutzer bei
Bedarf an und beendet alle offenen Sitzungen.

docs/deployment.md nennt jetzt die drei Ursachen einer fehlgeschlagenen
Anmeldung in der Reihenfolge ihrer Häufigkeit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
2026-09-09 23:40:42 +02:00

10 KiB
Raw Blame History

moneyfy

Selbst gehostete Web-Anwendung zur Planung monatlicher Kosten und Einkünfte. Single-User, deutschsprachige Oberfläche, ausgelegt für den Betrieb im Homelab hinter einem Reverse Proxy.

Version 0.1.0. Siehe CHANGELOG.md.

Screenshots

Die Bilder entstehen aus dem Demo-Datensatz (make seed-demo), damit sie ohne echte Kontostände auskommen. Ablegen unter docs/screenshots/.

Dashboard
Dashboard verfügbar nach Fixkosten, Kategorienring, Zwölf-Monats-Vorschau
Cashflow-Kalender
Cashflow-Kalender Fälligkeiten je Tag mit Firmenlogo, darunter der Kontostandsverlauf
Wiederkehrende Posten
Wiederkehrend Abos, Verträge und Raten mit Klartext-Rhythmus
RRULE-Editor
RRULE-Editor geführte Auswahl, Expertenmodus und Live-Vorschau der nächsten Termine
Firmen
Firmen Kachelgrid mit Logo, Markenfarbe und Jahreskosten
Auswertungen
Auswertungen laufende Kosten, Jahresvergleich und Export

Funktionsumfang

  • Wiederkehrende Zahlungen und Einkünfte über vollständige RFC-5545-RRULEs, inklusive Werktagsverschiebung nach NRW-Feiertagen.
  • Preishistorie je Vertrag, sodass vergangene Monate betragstreu bleiben.
  • Verträge mit Mindestlaufzeit, Kündigungsfrist und automatischer Verlängerung.
  • Ratenzahlungen mit Restschuld- und Restratenberechnung.
  • Rücklagenbildung für nicht-monatliche Posten.
  • Firmenlogos und Markenfarben, lokal zwischengespeichert (siehe unten).
  • Auswertungen: Monatsübersicht, Cashflow-Kalender, 12-Monats-Forecast, Kategorien, Abo-Übersicht, Jahresvergleich, Budgets, Sparziele.
  • Benachrichtigungen per SMTP und Apprise.

Technischer Stack

Bereich Technologie
Backend Python 3.12, FastAPI, SQLAlchemy 2 (async), Alembic, Pydantic v2
Datenbank PostgreSQL 17
Scheduler APScheduler (in-process)
Frontend React 18, TypeScript, Vite, TailwindCSS
Charts Recharts
Tests pytest + httpx, vitest + testing-library

Schnellstart (Betrieb)

Voraussetzungen: Docker mit Compose-Plugin und ein externes Netz proxy.

mkdir -p /opt/moneyfy && cd /opt/moneyfy
curl -fsSLO https://git.menzel.center/menzeljonas/moneyfy/raw/branch/main/docker-compose.yml
curl -fsSLO https://git.menzel.center/menzeljonas/moneyfy/raw/branch/main/.env.example
cp .env.example .env

# Mindestens setzen: POSTGRES_PASSWORD, SECRET_KEY, MONEYFY_ADMIN_PASSWORD
$EDITOR .env

docker login git.menzel.center
docker compose up -d

Das Backend wartet auf die Datenbank, spielt die Migrationen ein und legt beim allerersten Start den Kategoriebaum an. Danach läuft die Oberfläche auf http://127.0.0.1:8087. Die vollständige Einrichtung samt Reverse Proxy und Sicherung steht in docs/deployment.md.

Schnellstart (lokale Entwicklung)

Voraussetzungen: Python 3.12, uv, Node 20+, eine erreichbare PostgreSQL-17-Instanz.

git clone https://git.menzel.center/menzeljonas/moneyfy.git
cd moneyfy
cp .env.example backend/.env      # DATABASE_URL und SECRET_KEY anpassen

make install                      # Backend-Abhängigkeiten in backend/.venv
make migrate                      # Schema anlegen
make seed                         # Kategoriebaum und Standardregeln
make dev                          # http://localhost:8000/api/docs

In einem zweiten Terminal das Frontend, es leitet /api an das Backend weiter:

make fe-install
make fe-dev                       # http://localhost:5173

Für einen gefüllten Stand:

make seed-demo        # realistischer Beispielhaushalt, Termine relativ zu heute
make seed-demo-reset  # setzt Konten, Firmen, Posten und Buchungen vorher zurück

Der Demo-Seed legt drei Konten an, zehn Firmen samt Logos, zwölf wiederkehrende Posten (Miete, Strom, Netflix, Spotify, jährliche Kfz-Versicherung, Handyvertrag mit Mindestlaufzeit, Kredit über 36 Raten, Gehalt), bestätigt die vergangenen Fälligkeiten, ergänzt Buchungen, Budgets und Sparziele.

Nützliche Ziele: make check (alle Prüfungen), make test, make lint, make format, make fe-lint, make fe-test, make fe-build.

Bedienung

Kürzel Wirkung
n Neue Buchung
r Neuer wiederkehrender Posten
/ Suchfeld der aktuellen Seite
? Übersicht der Kürzel
Esc Dialog schließen

Kürzel greifen nur, solange kein Eingabefeld den Fokus hat.

Barrierefreiheit

Die Farbtoken sind gegen die WCAG-Kontraste geprüft in beiden Modi erreicht jeder Text mindestens 4,5:1, jedes Bedienelement 3:1. Zwei Werte weichen deshalb bewusst von den naheliegenden Tailwind-Stufen ab: slate-400 als Hilfstext käme auf hellem Grund nur auf 2,56:1, und Weiß auf green-600 nur auf 3,30:1.

Die Serienfarben der Diagramme sind zusätzlich auf Farbfehlsichtigkeit geprüft (siehe Diagramme). Jeder Zustand Budget-Ampel, Versandstatus, Vertragswarnung trägt neben der Farbe ein Symbol und einen Wortlaut.

Konfiguration

Alle Einstellungen kommen aus Umgebungsvariablen; .env.example enthält die vollständige, kommentierte Liste. Die wichtigsten:

Variable Standard Bedeutung
DATABASE_URL PostgreSQL-DSN, postgresql:// wird auf asyncpg umgestellt
SECRET_KEY Signaturschlüssel für JWTs, mind. 32 Zeichen (openssl rand -hex 32)
MONEYFY_ADMIN_USER / MONEYFY_ADMIN_PASSWORD admin / Beim Erststart angelegter Benutzer. Es gibt kein eingebautes Standardpasswort; ohne diesen Wert wird niemand angelegt. Die Anlage greift nur, solange die Benutzertabelle leer ist danach hilft app.scripts.reset_password.
TIMEZONE Europe/Berlin Zeitzone der gesamten Anwendung
HOLIDAY_REGION DE-NW Feiertagsregion für Werktagsverschiebungen
LOGO_STORAGE_DIR /data/logos Verzeichnis des Logo-Caches
LOGODEV_API_KEY / BRANDFETCH_API_KEY leer Optionale Logo-Provider, ohne Schlüssel übersprungen
SMTP_* leer Mailversand für Benachrichtigungen
APPRISE_URLS leer Komma-separierte Apprise-Ziele
COOKIE_SECURE true Hinter HTTPS true, für lokales HTTP false
SCHEDULER_ENABLED true Täglicher Benachrichtigungslauf um 07:00

Benachrichtigungen

Ein Job läuft täglich um 07:00 Uhr (Europe/Berlin) im Anwendungsprozess ohne Redis, ohne Celery. Er prüft vier Anlässe: bald fällige Posten, Kündigungsfristen 30, 14 und 7 Tage vor dem Termin, überschrittene Budgets und Vertragsverlängerungen im kommenden Monat.

Der Duplikatsschutz hängt am Zieltag des Ereignisses, nicht am Versandtag: Eine Fälligkeit am 15.03. wird genau einmal gemeldet, unabhängig davon, an welchem Tag des Vorlaufs der Job läuft. Ein fehlgeschlagener Versand gilt als offen und wird beim nächsten Lauf erneut versucht.

Als Kanäle stehen SMTP (HTML-Mail mit eingebetteten Firmenlogos) und Apprise zur Verfügung. Über Einstellungen → Benachrichtigungen lassen sich Regeln pflegen, eine Testnachricht verschicken und das Versandprotokoll einsehen.

Diagramme

Die Serienfarben stammen aus einer Palette, die gegen die hellen und dunklen Flächen der Anwendung auf Kontrast und Farbfehlsichtigkeit geprüft wurde; ihre Reihenfolge ist Teil dieser Absicherung. Grün und Rot bleiben den Vorzeichen im Text vorbehalten als Serienpaar wären sie bei Deuteranopie nicht zu unterscheiden. Jedes Diagramm mit mehr als einer Serie führt eine Legende, und zu den Balkendiagrammen lässt sich die Wertetabelle aufklappen.

Einnahmen, Ausgaben und der kumulierte Kontostand stehen bewusst in zwei getrennten Diagrammen untereinander: eine gemeinsame Größenachse würde bei so verschiedenen Größenordnungen einen Zusammenhang vortäuschen.

Logos und Markenfarben

Jede Firma bekommt ein Bild. Die Provider-Kette arbeitet der Reihe nach:

  1. simple-icons rund 3.500 Marken, im Repository unter backend/app/assets/simple_icons.json.gz mitgeliefert. Kein Schlüssel, kein Netzzugriff. Neu erzeugen mit make vendor-icons.
  2. logo.dev nur mit LOGODEV_API_KEY.
  3. Brandfetch nur mit BRANDFETCH_API_KEY und hinterlegter Domain.
  4. Favicon über den Google-Dienst, wenn eine Domain bekannt oder aus dem Namen ableitbar ist.
  5. Generierter Avatar Buchstaben-Monogramm mit fester Farbe aus dem Namens-Hash. Schlägt nie fehl.

Findet die Kette einen eindeutigen Treffer im lokalen Katalog, unterbleiben die Anfragen nach außen vollständig die Firmenliste verlässt den Server nicht. Im Auswahldialog (POST /api/merchants/{id}/logo/search) läuft dagegen bewusst die ganze Kette, damit Alternativen zur Wahl stehen.

Ausgewählte Logos landen unter LOGO_STORAGE_DIR, benannt nach ihrem SHA-256-Hash, und werden nur über GET /api/logos/{id} ausgeliefert. Beim Seitenaufruf entsteht dadurch kein Zugriff auf fremde Dienste. Ein Upload oder eine bewusste Auswahl setzt den Status auf manual und wird von der Automatik nie überschrieben.

Projektstruktur

backend/     FastAPI-Anwendung, Modelle, Services, Alembic-Migrationen, Tests
frontend/    React-Oberfläche (Vite)
docs/        Betriebs- und Runner-Dokumentation
.gitea/      Gitea-Actions-Workflows

Alternativ lässt sich alles aus dem Quellcode bauen:

cp docker-compose.override.yml.example docker-compose.override.yml
docker compose up -d --build

Die Override-Datei baut beide Images lokal, bindet den Quellcode ein und öffnet die Ports für Backend (8000) und Vite (5173).

Bauen und Ausrollen

Zwei Gitea-Workflows unter .gitea/workflows/:

  • ci.yml läuft bei jedem Push und Pull Request: ruff, pytest, tsc --noEmit, eslint, vitest und der Bundle-Bau.
  • build.yml läuft auf main und bei Tags v* und lädt beide Images für linux/amd64 in die Gitea-Registry. Push auf main erzeugt :main und :sha-<kurz>, ein Tag v1.2.3 zusätzlich :1.2.3, :1.2 und :latest.

Ausgerollt wird nicht automatisch. Die Einrichtung des Runners beschreibt docs/runner.md, das Ausrollen docs/deployment.md.

Lizenz

MIT