mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:21:09 +02:00
docs(devlog): add 2026-03-31 entry — Memoro, status.mana.how, Todo polish, infra
This commit is contained in:
parent
0a9c38161b
commit
6e0dd0c065
1 changed files with 351 additions and 0 deletions
|
|
@ -0,0 +1,351 @@
|
||||||
|
---
|
||||||
|
title: 'Memoro im Monorepo + Status-Page live + Todo-Polish'
|
||||||
|
description: 'Memoro vollständig ins Monorepo migriert (Web, Server, Audio-Server), status.mana.how live, Todo mit Paper-Cards und Detail-Modal, Auth-Email-Verification robustifiziert, Infra-Cleanup und UI-Vereinheitlichung.'
|
||||||
|
date: 2026-03-31
|
||||||
|
author: 'Till Schneider'
|
||||||
|
category: 'feature'
|
||||||
|
tags:
|
||||||
|
[
|
||||||
|
'memoro',
|
||||||
|
'migration',
|
||||||
|
'hono',
|
||||||
|
'bun',
|
||||||
|
'local-first',
|
||||||
|
'status-page',
|
||||||
|
'monitoring',
|
||||||
|
'todo',
|
||||||
|
'auth',
|
||||||
|
'infra',
|
||||||
|
'ui',
|
||||||
|
'azure-speech',
|
||||||
|
]
|
||||||
|
featured: false
|
||||||
|
commits: 98
|
||||||
|
readTime: 14
|
||||||
|
stats:
|
||||||
|
filesChanged: 2241
|
||||||
|
linesAdded: 340000
|
||||||
|
linesRemoved: 25171
|
||||||
|
contributors:
|
||||||
|
- name: 'Till Schneider'
|
||||||
|
handle: 'Till-JS'
|
||||||
|
commits: 98
|
||||||
|
workingHours:
|
||||||
|
start: '2026-03-31T09:00'
|
||||||
|
end: '2026-03-31T21:00'
|
||||||
|
---
|
||||||
|
|
||||||
|
## Highlights
|
||||||
|
|
||||||
|
- **Memoro** vollständig im Monorepo: Web-App (local-first, shared-auth-stores), Hono/Bun Server + Audio-Server ersetzen NestJS
|
||||||
|
- **status.mana.how** live: öffentliche Status-Page aus VictoriaMetrics-Daten, alle 60s aktualisiert
|
||||||
|
- **Todo**: Paper-Cards, Complete-Animation, Task-Detail-Modal redesigned, Inline-Subtasks im Kanban
|
||||||
|
- **Auth**: Email-Not-Verified Flow robust, Resend-Verification-Panel
|
||||||
|
- **Infra**: 4 Apps von GHCR auf lokale Builds, Arcade auf Hono/Bun
|
||||||
|
- **UI**: Elevation-System in 4 Apps, LanguageSelector/ConfirmDialog/AppSlider vereinheitlicht
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Memoro: Vollständige Monorepo-Integration
|
||||||
|
|
||||||
|
Memoro war bisher ein separates Repo (legacy NestJS-Stack). Heute wurde die App vollständig ins Monorepo überführt — in drei Schritten.
|
||||||
|
|
||||||
|
### Schritt 1: Legacy-Code importiert
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/memoro/apps/
|
||||||
|
├── backend/ # Legacy NestJS (importiert, nicht aktiv)
|
||||||
|
├── mobile/ # Expo App (importiert)
|
||||||
|
├── landing/ # Astro Landing (importiert)
|
||||||
|
└── web/ # → wird in Schritt 2 migriert
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Legacy-Code ist eingecheckt als Referenz. Die aktiven Services werden in Schritt 2 und 3 neu gebaut.
|
||||||
|
|
||||||
|
### Schritt 2: Web-App migriert (Phasen 1–6)
|
||||||
|
|
||||||
|
Die bestehende Web-App wurde vollständig auf den Monorepo-Stack portiert:
|
||||||
|
|
||||||
|
| Phase | Was passiert ist |
|
||||||
|
|-------|-----------------|
|
||||||
|
| 1 | Package-Struktur: `@memoro/web`, adapter-node, Tailwind v4 + `@manacore/shared-tailwind` |
|
||||||
|
| 2 | Auth: `createManaAuthStore()` aus `@manacore/shared-auth-stores`, Google/Apple OAuth entfernt |
|
||||||
|
| 3 | Local-First: `memoroStore` mit 7 Collections (memos, tags, spaces, invites, …) |
|
||||||
|
| 4 | Guest-Seed: Demo-Daten die sofort geladen werden ohne Login |
|
||||||
|
| 5 | Service Layer: `memoService` + `tagService` von Supabase direct auf local-store Collections |
|
||||||
|
| 6 | Workspace-Integration: `dev:memoro:*` Scripts, CLAUDE.md Eintrag |
|
||||||
|
|
||||||
|
**Wichtig:** Google/Apple OAuth komplett entfernt — Memoro nutzt ausschließlich mana-auth JWT, wie alle anderen Apps im Ökosystem. Keine externen OAuth-Provider.
|
||||||
|
|
||||||
|
### Schritt 3: Hono/Bun Server + Audio-Server
|
||||||
|
|
||||||
|
Zwei neue Services ersetzen das komplette NestJS-Backend (Server + Audio-Backend):
|
||||||
|
|
||||||
|
#### `apps/memoro/apps/server/` (Port 3015) — Business Logic
|
||||||
|
|
||||||
|
```
|
||||||
|
- Memo-CRUD, Transkriptions-Orchestrierung
|
||||||
|
- AI: Headline-Generierung, Q&A aus Memos
|
||||||
|
- Space + Invite-Management, Credits, Settings, Cleanup
|
||||||
|
- Auth: @manacore/shared-hono authMiddleware (mana-auth JWT)
|
||||||
|
- DB: Service-Role Supabase Client mit expliziten user_id Filtern
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `apps/memoro/apps/audio-server/` (Port 3016) — Audio Processing
|
||||||
|
|
||||||
|
Das war die technisch interessanteste Komponente. Azure Speech hat Ausfälle und Rate-Limits — der Audio-Server löst das mit einem **4-stufigen Fallback**:
|
||||||
|
|
||||||
|
```
|
||||||
|
Tier 1: Primärer Azure-Key (schnell, < 200ms)
|
||||||
|
↓ (bei Fehler/Timeout)
|
||||||
|
Tier 2: Gleicher Key, Retry (transiente Fehler)
|
||||||
|
↓ (bei anhaltenden Problemen)
|
||||||
|
Tier 3: FFmpeg-Konvertierung (PCM 16kHz Mono WAV)
|
||||||
|
→ Löst Codec-Inkompatibilitäten
|
||||||
|
↓ (bei Batch-Overflow)
|
||||||
|
Tier 4: Azure Batch Transcription
|
||||||
|
→ Für lange Aufnahmen (> 10 Minuten)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Load Balancing:** Bis zu 4 Azure-Speech-Keys rotieren für parallele Transkriptionen.
|
||||||
|
|
||||||
|
**Intern only:** Der Audio-Server akzeptiert nur Requests mit `X-Service-Key` Header — kein direkter Zugriff von außen.
|
||||||
|
|
||||||
|
### Vergleich: Vorher vs. Nachher
|
||||||
|
|
||||||
|
| Aspekt | NestJS (alt) | Hono/Bun (neu) |
|
||||||
|
|--------|-------------|----------------|
|
||||||
|
| Services | 2 (Backend + Audio-Backend) | 2 (Server + Audio-Server) |
|
||||||
|
| LOC (gesamt) | ~12.000 | ~1.800 |
|
||||||
|
| Dependencies | 20+ (@nestjs/*, class-validator, …) | 4 (hono, zod, fluent-ffmpeg, …) |
|
||||||
|
| Cold Start | ~2-3s | ~50ms |
|
||||||
|
| RAM | ~400MB | ~80MB |
|
||||||
|
| Auth | Supabase JWT | mana-auth JWT |
|
||||||
|
| OAuth | Google + Apple | Keiner (self-sovereign) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## status.mana.how: Öffentliche Status-Page
|
||||||
|
|
||||||
|
ManaCore betreibt ~37 Services. Nutzer hatten bisher keine Möglichkeit zu prüfen ob ein Ausfall bekannt ist. Das ist jetzt behoben.
|
||||||
|
|
||||||
|
### Architektur
|
||||||
|
|
||||||
|
```
|
||||||
|
VictoriaMetrics (interne Metrics)
|
||||||
|
↓ alle 60s
|
||||||
|
scripts/generate-status-page.sh
|
||||||
|
↓ liest probe_success + response_times
|
||||||
|
statisches HTML
|
||||||
|
↓ schreibt nach
|
||||||
|
/Volumes/ManaData/landings/status/
|
||||||
|
↓ served von
|
||||||
|
Nginx → status.mana.how
|
||||||
|
```
|
||||||
|
|
||||||
|
**Kein separater Service.** Ein Alpine-Container mit `jq` und `curl` fragt VictoriaMetrics ab, generiert eine HTML-Datei und schreibt sie ins Landings-Volume. Nginx dient das statische HTML.
|
||||||
|
|
||||||
|
### Was angezeigt wird
|
||||||
|
|
||||||
|
- Status pro Service (Operational / Degraded / Down)
|
||||||
|
- Response Times der letzten Messungen
|
||||||
|
- Uptime über die letzten 24h / 7 Tage
|
||||||
|
|
||||||
|
### Blackbox Exporter
|
||||||
|
|
||||||
|
Parallel wurde der **Prometheus Blackbox Exporter** eingerichtet — er probt alle Services von außen per HTTP und liefert `probe_success` Metriken. Das ist die Grundlage für die Status-Page und für Grafana-Alerts.
|
||||||
|
|
||||||
|
### ManaScore Integration
|
||||||
|
|
||||||
|
Die Live-Uptime-Badges wurden direkt in die ManaScore-Seite integriert. Jede App zeigt jetzt ihre aktuellen Uptime-Werte aus `status.mana.how/status.json`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Todo: Weiteres Design-Polish
|
||||||
|
|
||||||
|
Nach dem Notepad-Redesign von gestern gab es heute mehrere Iteration-Loops auf Details.
|
||||||
|
|
||||||
|
### Paper-Style Task-Cards
|
||||||
|
|
||||||
|
Die Task-Cards wurden weiter bereinigt:
|
||||||
|
|
||||||
|
| Vorher | Nachher |
|
||||||
|
|--------|---------|
|
||||||
|
| Border + Hintergrundfarbe | Kein Border, kein Hintergrund |
|
||||||
|
| Checkbox vertikal zentriert | Checkbox am Anfang der ersten Zeile (flex-start) |
|
||||||
|
| Titel abgeschnitten (truncate) | Mehrzeilig (word-break) |
|
||||||
|
| Hover: translateY + Hintergrund | Kein Hover-Effekt |
|
||||||
|
|
||||||
|
**Resultat:** Die Task-Liste liest sich wie echtes Papier — nur der Text, keine UI-Noise.
|
||||||
|
|
||||||
|
### Complete Animation + "Heute erledigt"
|
||||||
|
|
||||||
|
```
|
||||||
|
Task abgehakt → Checkbox fill-Animation (0.3s)
|
||||||
|
→ Task faded + durchgestrichen
|
||||||
|
→ Bewegt sich nach unten in "Heute erledigt"
|
||||||
|
→ "Heute erledigt" Section erscheint automatisch
|
||||||
|
```
|
||||||
|
|
||||||
|
Subtasks erben die Animation vom Parent-Task wenn dieser abgehakt wird.
|
||||||
|
|
||||||
|
### Task-Detail-Modal: Komplett redesigned
|
||||||
|
|
||||||
|
Das alte Detail-Modal war ein simples Formular. Das neue ist ein **page-like Layout**:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Großer, borderloser Titel (Textarea, autoGrow) │
|
||||||
|
├─────────────────────────────────┬───────────────────┤
|
||||||
|
│ Beschreibung │ Subtasks │
|
||||||
|
│ (linke Spalte, fließend) │ □ Subtask 1 │
|
||||||
|
│ │ □ Subtask 2 │
|
||||||
|
│ │ + Links │
|
||||||
|
├─────────────────────────────────┴───────────────────┤
|
||||||
|
│ [Status] [Priorität] [Fällig: 15. Apr] [Projekt] │
|
||||||
|
│ [Label] [Dauer: 30min] [Spaß ★★★] [Story Pts] │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
Props-Strip: alle Metadaten als horizontal-wrappende Chips. Kein Scrolling für Grundfelder nötig.
|
||||||
|
|
||||||
|
### Inline Subtasks im Kanban
|
||||||
|
|
||||||
|
KanbanTaskCards zeigen jetzt Subtasks direkt in der Card — mit Checkbox-Toggle und DnD-Safe-Fix (DnD blockierte vorher Subtask-Klicks via `stopPropagation`).
|
||||||
|
|
||||||
|
### Fokus-View Vereinfachung
|
||||||
|
|
||||||
|
Die zwei Tabs "Übersicht" und "Matrix" wurden entfernt. Es gibt nur noch den **Fokus-View** (Inbox/Heute/Übermorgen/Projekte). Die Matrix-Ansicht war kaum genutzt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Auth: Email-Verification Flow robustifiziert
|
||||||
|
|
||||||
|
Der Email-Verification-Flow hatte mehrere Edge-Cases die in der Praxis aufgetreten sind:
|
||||||
|
|
||||||
|
### Problem 1: EMAIL_NOT_VERIFIED nicht erkannt
|
||||||
|
|
||||||
|
Better-Auth wirft keinen strukturierten Error-Code — der Error kommt als Freitext. Die Erkennung war zu fragil (`message.includes("verify")`). Jetzt mehrere Heuristiken kombiniert:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Vorher
|
||||||
|
if (error.message.includes('verify')) { ... }
|
||||||
|
|
||||||
|
// Nachher
|
||||||
|
const isNotVerified =
|
||||||
|
error.code === 'EMAIL_NOT_VERIFIED' ||
|
||||||
|
error.message?.toLowerCase().includes('not verified') ||
|
||||||
|
error.message?.toLowerCase().includes('verify your email') ||
|
||||||
|
error.status === 403 && error.message?.includes('email');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem 2: callbackURL vs. redirectTo
|
||||||
|
|
||||||
|
Better-Auth's `sendVerificationEmail` verwendet `callbackURL` — nicht `redirectTo`. Der Link in der E-Mail landete auf der falschen Seite. Fix: Parameter-Name korrigiert.
|
||||||
|
|
||||||
|
### Problem 3: Kein Resend-Panel
|
||||||
|
|
||||||
|
Wer sich mit einer existierenden, aber nicht verifizierten E-Mail registriert hatte, steckte fest — kein Weg um die Verification-Mail erneut zu senden.
|
||||||
|
|
||||||
|
**Neues Resend-Verification-Panel:** Erscheint automatisch wenn beim Login `EMAIL_NOT_VERIFIED` erkannt wird — mit Button zum erneuten Senden der E-Mail und Countdown-Timer (60s Cooldown).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## UI: Elevation-System & Komponenten-Vereinheitlichung
|
||||||
|
|
||||||
|
### Elevation-System in 4 Apps
|
||||||
|
|
||||||
|
Todo, Contacts, Calendar und Clock nutzen jetzt CSS Custom Properties statt hardcodierten Hex-/RGBA-Farben:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Vorher (überall) */
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
|
||||||
|
/* Nachher */
|
||||||
|
background: var(--surface-elevated);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Alle `:global(.dark)` Overrides entfernt** — die CSS-Variablen regeln Light/Dark automatisch. Das war der letzte Schritt um hardcoded Theme-Code aus diesen Apps zu eliminieren.
|
||||||
|
|
||||||
|
### Komponenten vereinheitlicht
|
||||||
|
|
||||||
|
Drei Komponenten die über viele Apps hinweg dupliziert waren:
|
||||||
|
|
||||||
|
| Komponente | Apps (vorher) | Lösung |
|
||||||
|
|-----------|--------------|--------|
|
||||||
|
| `LanguageSelector` | contacts, zitare: Custom-Dropdown mit eigenem State | → `PillDropdown` aus shared-ui (wie die anderen 8 Apps) |
|
||||||
|
| `ConfirmDialog` | context (4 Stellen), times (3 Stellen): lokale Kopien | → `ConfirmationModal` aus `@manacore/shared-ui` |
|
||||||
|
| `AppSlider` | todo, calendar, chat, contacts, presi: statisches `MANA_APPS` | → `getActiveManaApps()` (filtert inaktive Apps) |
|
||||||
|
|
||||||
|
**–228 Zeilen Netto**, 2 lokale Komponenten gelöscht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Infra: GHCR → Lokale Builds + Arcade Migration
|
||||||
|
|
||||||
|
### 4 Apps von GHCR migriert
|
||||||
|
|
||||||
|
Die letzten 4 Apps die noch GitHub Container Registry (GHCR) als Image-Quelle nutzten wurden auf das einheitliche lokale Build-Pattern umgestellt:
|
||||||
|
|
||||||
|
| App | Vorher | Nachher |
|
||||||
|
|-----|--------|---------|
|
||||||
|
| chat-web | `ghcr.io/...` | lokaler Build |
|
||||||
|
| clock-web | `ghcr.io/...` | lokaler Build |
|
||||||
|
| presi-web | `ghcr.io/...` | lokaler Build |
|
||||||
|
| nutriphi-web | `ghcr.io/...` | lokaler Build |
|
||||||
|
|
||||||
|
**Einzige Ausnahme:** Umami (externes Projekt) bleibt bei GHCR.
|
||||||
|
|
||||||
|
Damit nutzen jetzt **alle 37 eigenen Apps** lokale Builds über das gemeinsame `sveltekit-base:local` Image.
|
||||||
|
|
||||||
|
### Arcade: NestJS → Hono/Bun
|
||||||
|
|
||||||
|
Das Arcade-Backend (AI Browser Games) war noch auf NestJS. Migration auf Hono/Bun abgeschlossen — damit ist NestJS nun auch im Games-Bereich komplett eliminiert.
|
||||||
|
|
||||||
|
### Weitere Fixes
|
||||||
|
|
||||||
|
| Fix | Beschreibung |
|
||||||
|
|-----|-------------|
|
||||||
|
| Port-Konflikt | calc-web: Port 5026 → 5031 (kollidierte mit zitare-web) |
|
||||||
|
| Cloudflared | Config mit tatsächlichen Container-Ports synchronisiert |
|
||||||
|
| Landings Nginx | `mkdir snippets` vor Copy, status.mana.how vhost hinzugefügt |
|
||||||
|
| Prerender 404 | favicon.png 404s bei skilltree + nutriphi unterdrückt |
|
||||||
|
| inventar-web | Browser-Error-Tracking Import repariert |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dokumentation
|
||||||
|
|
||||||
|
Drei neue Planungs-/Guidelines-Dokumente:
|
||||||
|
|
||||||
|
**Auth UX Patterns** (`.claude/guidelines/authentication.md`): Regeln für den Email-Verification-Flow — wann welcher Banner/Dialog/Redirect erscheint. Verhindert die Edge-Cases von heute in Zukunft.
|
||||||
|
|
||||||
|
**Mana Bundle Format** (Plan-Dokument): Konzept für ein portables App-Bundle-Format — ermöglicht App-Exports als einzelne Datei (IndexedDB-Snapshot + Assets). Basis für zukünftige Backup/Restore-Features.
|
||||||
|
|
||||||
|
**Memoro Backend Migration** (Plan-Dokument): Schritt-für-Schritt-Plan der heute ausgeführten Migration — dokumentiert als Referenz für ähnliche Migrationen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zusammenfassung
|
||||||
|
|
||||||
|
| Bereich | Commits | Highlights |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| Memoro Integration | ~25 | Web, Server, Audio-Server ins Monorepo |
|
||||||
|
| Status-Page | ~12 | status.mana.how live, Blackbox Exporter, ManaScore-Badges |
|
||||||
|
| Todo Polish | ~20 | Paper-Cards, Complete-Animation, Detail-Modal, Kanban-Subtasks |
|
||||||
|
| Auth | ~10 | EMAIL_NOT_VERIFIED robust, Resend-Panel, callbackURL Fix |
|
||||||
|
| Infra | ~15 | GHCR→lokal, Arcade, Port-Fix, Cloudflared |
|
||||||
|
| UI Unification | ~8 | Elevation-System, LanguageSelector, ConfirmDialog, AppSlider |
|
||||||
|
| Docs | ~5 | Auth UX Patterns, Bundle Format, Memoro Plan |
|
||||||
|
| Fixes | ~3 | Prerender, inventar-web, manacore-web Login-Redirect |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nächste Schritte
|
||||||
|
|
||||||
|
- Memoro Production-Deploy + DNS
|
||||||
|
- Status-Page: Alert-Integration (Matrix-Benachrichtigung bei Downtime)
|
||||||
|
- Todo: Keyboard-Navigation-Pattern auf andere Apps übertragen (Zitare, Contacts)
|
||||||
|
- Debug-Borders als globales Shared-Package
|
||||||
Loading…
Add table
Add a link
Reference in a new issue