Skip to content

Architecture.de

Doug Blank edited this page Sep 20, 2026 · 1 revision

🌐 English

Architektur

Gramps Connect ist ein React-Frontend (app/), das über dessen REST-API mit einem gramps-web-api-Backend spricht — es forkt gramps-web-api nicht und ersetzt es nicht, es ist ein anderer Client für denselben Server. Zwei Mechanismen machen es möglich, dass sich dieses Frontend gleichzeitig schnell und kollaborativ anfühlt: ein lokaler Cache und ein leichtgewichtiges Live-Sync-Polling, aufgesetzt auf einen Endpunkt, den gramps-web-api bereits mitbringt.

Lokaler Cache

Ein WASM-Build von SQLite läuft im Browser, spiegelt Serverdaten in lokale Tabellen und speichert sie dauerhaft in OPFS (dem eigenen privaten Dateisystem des Browsers), sodass ein wiederholter Besuch das Netzwerk komplett überspringen kann. Daten werden über die schnellen, SQL-heruntergedrückten /api/<type>/query/-Endpunkte von gramps-web-api abgerufen — dieselben Endpunkte, zu denen GOQL-Bedingungen und Gramplet-where=-Klauseln kompilieren — statt sich durch vollständige, seitenweise REST-Antworten pro Objekt zu blättern.

Der Lohn ist das, was die Übersicht beschreibt: sobald ein Teil des Stammbaums einmal angesehen wurde, fühlen sich Durchsuchen, Sortieren und Suchen in diesem Teil danach sofort an, selbst bei Stammbäumen mit Zehntausenden von Personen, wo diese Art der Suche gegen ein schlichtes, unindiziertes REST-Backend sonst gut und gerne über eine Minute dauern kann.

Das ist auch, warum die feste admin/admin-Desktop-Version und eine echte Server-Bereitstellung genau denselben Frontend-Code ohne Sonderbehandlung teilen können: dem Cache ist nur wichtig, dass er mit einem gramps-web-api-förmigen /api/ spricht, nicht, wer es hostet.

Eine Falle, die man kennen sollte: der browserseitige Cache ist nach einem festen Dateinamen pro Ansicht geschlüsselt, nicht nach Backend-URL oder Stammbaum-ID. Zu wechseln, auf welches Backend man zeigt, oder einen Stammbaum gegen dasselbe Backend neu zu importieren/ neu anzulegen, kann ein Browser-Profil mit veralteten zwischen- gespeicherten Zeilen ohne automatische Invalidierung zurücklassen — die einzige durchgeführte Prüfung ist Schema-Kompatibilität, nicht Datenidentität. Sehen Daten nach einem Backend-Wechsel jemals veraltet aus, von Hand leeren: DevTools → Application → Storage → Website-Daten löschen (oder gezielt OPFS).

Live-Synchronisierung

Der Client fragt den bereits bestehenden Endpunkt GET /api/transactions/history/ von gramps-web-api ab (das Prüf-/Rückgängig-Protokoll für Objektänderungen, das er ohnehin schon mitbringt, nichts eigens dafür Hinzugefügtes) in kurzen Abständen. Für jedes Objekt, das dieser Endpunkt als geändert meldet, ruft Gramps Connect nur diese eine Zeile im lokalen Cache erneut ab und aktualisiert sie.

Das ist absichtlich einfach gehalten: keine dauerhafte, WebSocket- artige Verbindung und keine Postgres-spezifische Änderungserfassung nötig, nur ein schlichtes, authentifiziertes GET auf einem Timer — funktioniert also gegen jedes gramps-web-api-Backend, nicht nur eines mit Postgres dahinter. So funktioniert tatsächlich „jemand anderes korrigiert ein Datum und der eigene Bildschirm aktualisiert sich von selbst“ (siehe Übersicht), und so können auch die Dev-Fixtures Synchronisierung ausüben, ohne eine echte Postgres-Instanz zu brauchen — sogar die reinen SQLite-Varianten unterstützen es, da es nur eine Abfrage ist.

Was Live-Synchronisierung noch nicht tut: es gibt keine Präsenzschicht (wer gerade was ansieht oder bearbeitet) und keinen benutzerseitigen Verlaufs-Browser — siehe Roadmap und bekannte Einschränkungen.

Repository-Aufbau

  • app/ — der produktive React-Client: alle zehn Objekttyp-Ansichten (Person, Familie, Ereignis, Ort, Aufbewahrungsort, Quelle, Fundstelle, Medien, Notiz, Etikett — siehe Datenmodell und Bearbeitung), where_expr/GOQL-Filterung, ein in OPFS gespeicherter WASM-SQLite- Cache und Live-Synchronisierung, hinter einer auf useSyncExternalStore basierenden Store-Schicht (app/src/store/) mit @tanstack/react-virtual fürs Scrollen.
  • dev-fixtures/ — echte gramps-web-api-Backends, um app/ lokal dagegen laufen zu lassen; nicht Teil des ausgelieferten Produkts, sondern das, was lokale Entwicklung ohne von Hand konfigurierten Server erst möglich macht. Siehe Entwicklung für die drei Varianten (layer2-local-cache/api-fixture, api-fixture-example und layer3-sync/api-fixture) und wofür jede da ist.
  • packages/gramps-date/ — ein TypeScript-Port von Gramps' eigenem Date-Modell, Kalenderumrechnung und lokalisierter Datumsanzeige, von app/ verwendet, um eine Gramps-Date-Struktur zu rendern oder zu bauen, ohne einen langsamen Netzwerk-Umweg pro Objekt über Gramps' eigenen Python-Datumsanzeiger für jede Zeile einer Tabelle. Es behandelt Kalenderumrechnung für fünf Kalender (Gregorianisch, Julianisch, Französischer Revolutionskalender, Islamisch, Schwedisch — Hebräisch und Persisch werden korrekt angezeigt, lassen sich aber bei der Eingabe noch nicht validieren, da ihre SDN-Umrechnung mehr Maschinerie braucht), strukturierte Datumseingabe und -validierung, sowie lokalisierbare Anzeige (registerLocale(); heute liefert nur Englisch aus). Es ist eine Übersetzung von Gramps' eigenem gramps/gen/lib/date.py/gcalendar.py/_datedisplay.py aus Python nach TypeScript, gegen die echte Python-Implementierung in seiner eigenen Testsuite gegengeprüft — siehe seine eigene README für die vollständige Herkunfts- und Lizenzgeschichte (es ist GPL-2.0-or-later- Code, eingefaltet in dieses AGPL-3.0-or-later-Projekt, genau wie es der eigene gcalendar.js-Port von gramps-web bereits tut).
  • standalone/ — der PyInstaller-basierte Build von gramps-connect-desktop: ein Python-Launcher (launcher.py), der das gebaute Frontend von app/ mit gramps-web-api und SQLite zu einer einzigen App mit nativem Fenster (oder Browser-Rückfall) bündelt. Siehe Installation.
  • deploy/ — die containerisierte Mehrbenutzer-Bereitstellung (app/ + gramps-web-api + Postgres + Caddy + Redis/Celery). Siehe Bereitstellung.
  • gramplet_examples/ und gramplet-store/ — Beispiel- Gramplets und der Quellinhalt für den Katalog des In-App- Gramplet-Stores. Siehe Gramplets.

Ein npm-Workspace auf oberster Ebene (packages/*, app) verbindet app/ und packages/gramps-date als echte Workspace-Abhängigkeiten. Die schnellen /query/-Endpunkte, von denen app/ lebt, liegen tatsächlich in gramps-web-api selbst (ein separates Repository, direkt darin erweitert, abwärtskompatibel), über gramps-object-query-language, GOQLs eigener Implementierung — nicht in diesem Repository.

Add-ons führen Python im Browser aus

Gramplets — die Add-ons der App — laufen unter Pyodide (nach WebAssembly kompiliertes CPython) direkt im Browser-Tab, gegen lokal gebaute Wheels von gramps.gen.lib (Gramps' eigenem Datenmodell, sodass der Python-Code eines Gramplets echte Person/Family/…-Objekte sieht, keine Neuimplementierung). Keine serverseitige Ausführung und nichts auf dem eigenen Rechner installiert — siehe Entwicklung dafür, wie diese Wheels gebaut werden, und Gramplets dafür, wie die sandboxed API (people(), filter(), db, row(), html(), …) zusammengesetzt ist.

Clone this wiki locally