Zum Hauptinhalt springen

ZurĂĽck zum Devlog

Donnerstag, 02. April 2026

Feature

107 Commits

15 min Lesezeit

Unified API Server + 25 Apps archiviert + SSE Sync

17 Module-Server zu @manacore/api konsolidiert, 25 standalone Web-Apps archiviert, WebSocket-Sync durch SSE ersetzt, Partial-Sync mit lazy collection loading. Plus Tag-System, Detail-View Overlays und i18n.

T

Till Schneider

Autor

Highlights

  • Unified API Server (@manacore/api): 17 Module-Server zusammengefĂĽhrt, 25 standalone Web-Apps archiviert
  • WebSocket → SSE: ein einziger Stream pro User statt 27 Verbindungen
  • Partial Sync: Collections werden lazy beim ersten Modul-Visit geladen
  • Detail-View Overlays in 14 Modulen — Stack-fähig, mit inline-editing
  • Unified QuickInputBar mit context-aware Adapters pro Modul
  • i18n in 5 Phasen — 126 deutsche Strings extrahiert, Locale-Files konsolidiert
  • 2 DBs statt 20+: mana_platform + mana_sync mit pgSchema-Isolation
  • Shared Tag-System ĂĽber alle 23 Module mit Junction-Tables

Unified API Server: 17 Hono-Server zu einem

Gestern wurden die Frontends konsolidiert. Heute folgten die Backends.

Bisher hatte jedes Modul mit Server-Logik einen eigenen Hono-Container (calendar-server, contacts-server, todo-server, …). Jeder mit eigener Auth, eigener Health-Route, eigener Drizzle-Connection. Massiv viel Duplikation, massiv viel RAM, 17 Pipelines.

Neuer @manacore/api Server

Ein einziger Hono/Bun-Server unter apps/api/. Module registrieren ihre Routen unter /api/v1/{module}/*:

apps/api/
├── src/
│   ├── modules/
│   │   ├── calendar/   # Routes + Service
│   │   ├── contacts/
│   │   ├── todo/
│   │   ├── memoro/
│   │   └── … (17 Module)
│   ├── middleware/     # auth, rate-limit, errors (von shared-hono)
│   ├── db/             # 1 Drizzle-Connection für alle Module
│   └── index.ts        # Routes-Mounting

Migration in zwei Schritten

SchrittWas passiert
1API-Server mit 3 Modulen erstellt (calendar, contacts, todo)
2Restliche 12 Module portiert

Jedes Modul behält seine Service-Layer fast unverändert — nur das Hono-App-Setup fällt weg. Auth, Errors, Rate-Limiting kommen aus @manacore/shared-hono.

Was jetzt weg ist

apps-archived/
├── calendar-server/
├── contacts-server/
├── todo-server/
├── chat-server/
├── … (17 Server total)
└── README.md  # erklärt warum

RAM-Footprint: ~2.4 GB → ~280 MB. Cold-Start: ~45s zusammen → ~120 ms.

Auch 25 standalone Web-Apps archiviert

Mit der Unified Web-App von gestern und dem Unified API von heute haben die einzelnen apps/{name}/apps/web/ Verzeichnisse keinen Zweck mehr. Alle 25 nach apps-archived/ verschoben. wisekeep ist mit dabei.

.eslintrc ignored web-archived/ Pattern damit Lint nicht durch totes Holz läuft.


Sync: WebSocket → SSE + Partial Sync

Der Sync-Layer hatte zwei Probleme:

  1. 27 WebSocket-Verbindungen pro User (eine pro App). Connection-Pool-Limit hit.
  2. Voll-Sync beim Start — alle 120+ Collections, auch wenn der User nur 2 Module benutzt.

Unified WebSocket — eine Verbindung pro User

Vorher: User Ă— 27 Apps Ă— WebSocket = 27 Verbindungen
Nachher: User Ă— 1 manacore-web Ă— WebSocket = 1 Verbindung

mana-sync (Go) multiplext alle App-Streams ĂĽber einen Channel. Server-Side: einfach RLS-gefilterter LISTEN/NOTIFY.

Dann: WebSocket → SSE

Ein paar Stunden später wurde der WebSocket-Code komplett rausgerissen und durch Server-Sent Events ersetzt:

AspektWebSocketSSE
ReconnectManuellBrowser-built-in
Proxy-ProblemeHäufig (CF, NGINX)Keine (HTTP)
BidirektionalJaNein (irrelevant — Writes gehen via REST)
Code~600 LOC~180 LOC

SSE-Endpoint: GET /api/v1/sync/stream → ein Heartbeat alle 25s, Push bei jedem Server-side Change.

Partial Sync: Lazy Loading

Beim Login werden jetzt nur Core-Collections synchronisiert (settings, profile, dashboard-state). Alle Modul-Collections werden erst beim ersten Visit des Moduls gepullt.

Login → core (5 collections, ~50 KB)
   ↓
Visit /todo → todo collections (12, ~200 KB)
   ↓
Visit /calendar → calendar collections (8, ~150 KB)

Plus Pull-Pagination mit hasMore Flag, damit ein Modul mit 10.000 Items nicht in einem 8 MB JSON-Blob ankommt.

Live-Update Bugs (zwei)

E2E-Tests fanden zwei Bugs in der SSE-Reconnect-Logik:

  1. Doppel-Subscribe nach Reconnect → doppelte Updates
  2. Pending-Changes wurden nach Reconnect nicht resync’d

Beide gefixt, mit Test-Coverage.


Detail-View Overlay-System

Vorher öffnete jedes Modul Detail-Views als eigene Route. Mit der Unified App wäre das Navigation-Hell. Lösung: Overlay-Stack.

Workbench
   ↓ click on task
   Overlay #1 (TaskDetail)
      ↓ click on linked event
      Overlay #2 (EventDetail)
         ↓ click on contact
         Overlay #3 (ContactDetail)
            [ESC] schlieĂźt Overlay #3

Live in 14 Modulen heute

cards, storage, presi, calendar, contacts, todo, picture, chat, mukke, memoro, planta, inventar, times, dreams.

Jedes Detail-View hat:

  • Inline editing — kein Edit-Modus mehr
  • Animated Open/Close
  • ESC-Key Support
  • Stack-Awareness — z-index korrekt, Backdrop nur unter dem obersten

Routes fĂĽr die alten Detail-Pages konsolidiert: 14 Routen weg, 1 Layout-Component da.

Module wie Contacts haben jetzt einen Page-Carousel — zwischen “Liste”, “Mein Profil”, “Statistik” wird per Swipe/Tab gewechselt, nicht per Route. Schneller, kein Layout-Flash.


Tag-System: Eines fĂĽr alle 23 Module

Jedes Modul hatte bisher seine eigenen Tags, in eigenen Tabellen. Sinnloser Duplicate-State.

globalTags Tabelle

Eine zentrale globalTags Tabelle mit {id, name, color, icon}. Jedes Modul nutzt Junction-Tables wie taskTags, contactTags, eventTags mit (itemId, tagId).

Neuer createTagLinkOps Factory

const taskTagOps = createTagLinkOps({
	table: db.taskTags,
	itemKey: 'taskId',
	tagKey: 'tagId',
});

await taskTagOps.add({ taskId, tagId });
await taskTagOps.removeAll({ taskId });

23 Module nutzen denselben Factory, ein einziges Test-Set, eine einzige Sync-Code-Pfad.

Shared Components

  • <TagChip> — die einheitliche Tag-Pille
  • <TagSelector> — Multi-Select mit Inline-Erstellung
  • <TagField> — Form-Field-Wrapper

todo + photos sind die ersten Migranten weg von ihren lokalen Kopien.

Tag-DnD im Workbench

Tags können aus dem Tag-Strip gezogen und auf Items in jedem Modul gedroppt werden. Reaktiv, mit .value statt .subscribe() (das war ein Bug).


Unified QuickInputBar

Ein einziger Input am unteren Bildschirmrand. Was er erstellt, hängt vom aktuellen Kontext ab:

ModulQuickInput erstellt
TodoTask
CalendarEvent
NotesNote
ContactsContact
HabitsHabit
……

Adapter-Pattern: jedes Modul registriert sich mit registerQuickInputAdapter('todo', { create, parse, …}).

Statt einer statischen Dashboard-Page hat der Workbench-Home einen horizontalen Carousel mit allen aktiven App-Pages. Swipe/Tab/Cmd+1..9 wechselt zwischen ihnen.

2D-Resize

Pages im Workbench sind jetzt in beide Achsen resizable (Width + Height), mit Memory pro Page.

Bottom-Chrome dynamisch

bottom-chrome-height ist eine CSS-Variable die sich an PillNav, Tabs, Notifications anpasst. Main-Content schiebt sich entsprechend.

AppView → ListView Rename

In allen 24 Modulen wurde AppView.svelte zu ListView.svelte umbenannt. Klarer Begriff.


i18n: 5 Phasen

PhaseWas passiert
1Locale-Files in per-Modul-Struktur splitten
2User-Settings Locale wired, Nav-Translations
3Alle 22 Module-Translations konsolidiert
4126 hardcoded deutsche Strings durch $_() ersetzt
5Help-Content nach Locale-Files migriert

Resultat: Es gibt jetzt keine einzige hardcoded Sprache mehr im Unified-App-Code (Help-Articles inklusive).


Datenbanken: 20+ → 2

Bisher hatte jeder Modul-Server seine eigene PostgreSQL-DB. Mit dem Unified API macht das keinen Sinn mehr.

Neue Struktur

mana_platform   # alle Service-Daten (auth, todo, calendar, …)
                # via pgSchema('todo'), pgSchema('calendar'), …
                # je Modul ein Schema, eine logische Trennung

mana_sync       # write-heavy, das eine Ding fĂĽr mana-sync (Go)

pgSchema() statt pgTable() ist jetzt Pflicht. Alte DBs (calendar_db, contacts_db, …) gedumpt + gelöscht.


Analytics: Module-Context + Web Vitals

Konsolidierung

Umami wurde von allen archivierten Apps entfernt — nur die Unified App trackt jetzt. Aber: jedes Event bekommt einen module Context.

analytics.track('item.created', {
	module: 'todo',
	itemType: 'task',
	hasDueDate: true,
});

Event-Tracking in 19 Module-Stores

Erste Welle: 7 Core-Module. Zweite Welle: 12 weitere. Tracked werden Create/Update/Delete/Move/Reorder Events.

Web Vitals + Funnel

CLS, LCP, FID, TTFB werden jetzt zu Umami gesendet. Plus Funnel-Events: funnel.signup.start, funnel.signup.complete, funnel.first_action. GlitchTip bekommt User-Context (anonymisierte ID + Tier).


Reminder System

Neuer shared reminder system in @manacore/shared-stores — ein Background-Worker scheduled lokale Notifications für Tasks, Events, Habits. Wird im App-Layout gemountet.

reminderService.schedule({
	module: 'todo',
	itemId: task.id,
	fireAt: task.dueDate.minusMinutes(15),
	payload: { title: task.title },
});

Notification kommt via Service Worker, klickbar, öffnet das Detail-View.


Auth & Stores Refactor

  • shared-auth-stores aufgelöst → in shared-auth-ui absorbiert. Ein Package weniger.
  • createGuestMode Composable + Unified AuthGate — der Guest-Modus ist jetzt ĂĽberall einheitlich, statt 23 lokale Implementierungen.
  • createArchiveOps / createViewStore Factories — wiederverwendbare CRUD-Patterns fĂĽr alle Module.
  • Shared Python Auth + Shared Go Auth Packages — extrahiert aus mana-stt, mana-tts, und 3 Go-Services.

Monitoring

  • Strukturiertes Logging in allen Services (JSON-Format, korrelierbar)
  • Promtail-Alignment — Labels einheitlich, damit Loki-Queries nicht ĂĽber inkompatible Schemas stolpern
  • GlitchTip Config updated fĂĽr unified app
  • Status-Page zeigt jetzt unified app statt 27 einzelne Apps

Sonstiges

ItemDetail
Self-ContactContacts erstellt fĂĽr jeden User automatisch einen Eigenen-Profil-Contact, auch im Guest-Mode
ColorPickerGenerischer <ColorPicker> mit Standard-Paletten in shared-ui
FavoriteButtonGeneric <FavoriteButton> + toggleField Utility
Vitest CoverageThreshold von 50 → 70 %
jest.config.jsOrphan entfernt
Stub-ServicesArchived

Zusammenfassung

BereichCommitsHighlights
Unified API~1517 Server zu einem, 25 Apps archiviert, 2 DBs
Sync~10WebSocket→SSE, partial sync, pull pagination
Detail Overlays~1014 Module, stack-fähig, inline editing
Tag System~12globalTags, junction tables, factory, DnD
QuickInput + Workbench~8Adapter-Pattern, Page-Carousel, 2D-Resize
i18n~65 Phasen, 126 Strings extrahiert
Analytics~10Module-Context, Web Vitals, 19 Stores
Refactor~12Auth-Stores absorbiert, Factories, Python/Go Auth
Monitoring~5Logging, Promtail, GlitchTip
Fixes~19Diverse Sync- + Build-Bugs

Nächste Schritte

  • Habits-Modul aufbauen (das einzige offene Modul)
  • Notes & Finance Module ins Unified pattern ziehen
  • Cross-Module-Drag-Targets ausbauen (Tags → Tasks → Events fertig, fehlt Karten/Locations)
  • Stalwart als interner Mail-Server fĂĽr mana-notify

Tags

#manacore #unified-api #hono #archive #sse #sync #tags #i18n #analytics #workbench