managarten/services
Till JS 795b39e065 feat(forms): M10d headless wave-cron — server-worker + private internal_meta
Echter Server-Cron für recurring forms — wave-send läuft jetzt
unabhängig von Owner-Tab-State. Bisheriger M10c webapp-side scheduler
bleibt als Belt-and-suspenders aktiv (idempotent).

Architektur:
1. **Owner-private internal_meta auf unlisted snapshots**
   - Drizzle: neue jsonb-column `internal_meta` (Drizzle migration
     0001_internal_meta.sql).
   - public-routes.ts strippt sie strukturell — die explicit select()-
     projection enthält sie nicht (recipients + sender würden sonst
     via share-link leaken).
   - publish-route akzeptiert sie im Body, persistiert auf insert +
     update.
   - ALLOWED_COLLECTIONS um 'lasts' und 'forms' erweitert (war ein
     latenter Bug — formsStore.setVisibility('unlisted') hätte ohne
     diese Ergänzung 400 zurückbekommen; M4b lief vermutlich nie
     end-to-end durch).

2. **shared-privacy publishUnlistedSnapshot**
   - PublishUnlistedOptions erweitert um optionales `internalMeta`.
     Forwarded an /api/v1/unlisted/:collection/:recordId body.

3. **Webapp formsStore**
   - lib/wave-mail.ts: buildFormInternalMeta(form, broadcastSettings)
     baut den Owner-Private-Blob: { kind, recurrence: {frequency,
     recipientEmails, lastSentAt}, sender: {fromEmail, fromName,
     replyTo, legalAddress}, formMeta: {title, description} }.
     Returns null wenn Voraussetzungen fehlen (kein recurrence, keine
     recipients, fehlende broadcast-settings).
   - stores/forms.svelte.ts: setVisibility / regenerateUnlistedToken /
     setUnlistedExpiry laden broadcastSettings via Dexie + decrypt,
     bauen internalMeta, übergeben an publishUnlistedSnapshot. Form
     wird vor dem buildFormInternalMeta-Call dekrypted.

4. **mana-mail internal bulk-send route**
   - createInternalRoutes(accountService, broadcastOrchestrator,
     maxRecipients) — Signature erweitert.
   - Neue POST /api/v1/internal/mail/bulk-send: gleicher Payload-shape
     wie user-facing /v1/mail/bulk-send aber userId aus Body statt
     JWT. X-Service-Key-gate sitzt bei /api/v1/internal/* prefix.
     Audit-trail trägt principalId aus Body. Cap = 5000 (gleicher
     Wert wie user-facing).

5. **apps/api forms wave-worker**
   - 5-min setInterval, advisory-lock-gated (key 0x464f5257 'FORW').
   - Tick: select snapshots WHERE collection='forms' AND
     internal_meta IS NOT NULL AND revoked_at IS NULL. Filter auf
     kind='forms-recurrence' + isWaveDue (lastSentAt + period <= now,
     never-sent fires sofort). Pro fälligem snapshot: build HTML/text
     mailbody (mirror webapp wave-mail-render), POST an mana-mail
     internal-bulk-send mit X-Service-Key + userId, dann jsonb_set
     auf internal_meta.recurrence.lastSentAt. Per-snapshot errors
     werden als console.warn geloggt, Tick läuft weiter.
   - Disable via FORMS_WAVE_WORKER_DISABLED=true (tests / multi-
     replica deployments).
   - Wired in apps/api/src/index.ts neben startArticleImportWorker().

Trade-offs:
- internal_meta wird beim setVisibility/regenerate/setExpiry frisch
  aus broadcast-settings gebaut — wenn der User später broadcast-
  settings ändert (zB neuer fromEmail) muss er das Form re-publishen
  damit die snapshot-internal_meta aktualisiert wird. Doc-it: zukünftiger
  Patch könnte ein "settings drift"-Warning ins UI surfacen.
- Worker-Update von lastSentAt geht NICHT zurück in den webapp-form
  (settings.recurrence.lastSentAt ist verschlüsselt, server kann
  nicht schreiben). Owner-UI zeigt ältere lastSentAt von manuellen
  Sends; auto-cron-sends sind in den Server-Logs sichtbar. Future
  patch: GET /api/v1/forms/:id/recurrence-status (auth) gibt das
  snapshot.internal_meta zurück, UI rendert Auto-Cron-State.
- Webapp-side wave-scheduler (M10c) läuft parallel weiter — wenn
  Owner-Tab offen ist, kann beides feuern. Idempotent durch
  lastSentAt-check (weekly/monthly buckets), aber theoretisch könnte
  double-fire passieren wenn die Calls innerhalb 1ms versetzt sind.
  Real-world ignorierbar; future patch: scheduler liest jetzt
  internal_meta.lastSentAt vom server-side state.

apps/api buildet (1776 modules). mana-mail buildet (523 modules).
svelte-check 0 errors in forms/. Forms-Tests 70/70 unverändert.

DB-Migration 0001_internal_meta.sql muss manuell appliziert werden
(siehe feedback memory: hand-authored SQL migrations sind nicht in
pnpm setup:db).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 17:18:05 +02:00
..
mana-ai fix(articles, mana-ai): rollout-block hardening for sync_changes projections 2026-04-29 00:53:39 +02:00
mana-analytics fix(feedback): keine doppelte Anzeige von Title + Body 2026-04-28 17:37:51 +02:00
mana-api-gateway chore: complete ManaCore → Mana rename (docs, go modules, plists, images) 2026-04-07 12:26:10 +02:00
mana-auth feat(notes): isSpaceContext flag replaces kontext module (Option B) 2026-04-29 00:14:32 +02:00
mana-crawler fix(mana-crawler): default DATABASE_URL to mana_platform in dev 2026-04-15 18:18:19 +02:00
mana-credits fix(mana-credits): correct pnpm workspace filter (@mana/credits-service, not @mana/credits) 2026-04-27 14:27:18 +02:00
mana-events feat(llm-aliases): M5 — migrate consumers to MANA_LLM aliases 2026-04-26 21:26:03 +02:00
mana-geocoding fix(geocoding): bump PROVIDER_TIMEOUT_MS to 20s for cold cross-LAN 2026-04-28 22:19:21 +02:00
mana-image-gen feat(mana-image-gen): replace Mac flux2.c implementation with Windows GPU diffusers 2026-04-08 13:02:42 +02:00
mana-landing-builder fix(tsconfig): unblock shared-types consumers 2026-04-21 18:53:55 +02:00
mana-llm fix(mana-llm): copy aliases.yaml into Docker image 2026-04-28 15:47:48 +02:00
mana-mail feat(forms): M10d headless wave-cron — server-worker + private internal_meta 2026-05-06 17:18:05 +02:00
mana-mcp docs(mana-mcp,mana-ai): CLAUDE.md coverage for M1 agent-loop primitives 2026-04-23 14:25:14 +02:00
mana-media fix(mana-media): HEIC uploads from Chrome — sniff + transcode at the edge 2026-04-25 13:46:13 +02:00
mana-notify fix(mana-auth) + chore: rewrite /api/v1/auth/login JWT mint, remove Matrix stack 2026-04-08 16:32:13 +02:00
mana-persona-runner fix(personas): exact tool_use_id pairing + CI drift audit 2026-04-23 15:34:52 +02:00
mana-research feat(llm-aliases): M5 — migrate consumers to MANA_LLM aliases 2026-04-26 21:26:03 +02:00
mana-search chore(docker): drop obsolete services/mana-search/docker-compose.dev.yml 2026-04-23 15:27:19 +02:00
mana-stt chore(mac-mini): remove all AI service infrastructure (moved to Windows GPU) 2026-04-08 13:06:40 +02:00
mana-subscriptions chore(db): enforce pgSchema isolation with a lint script 2026-04-20 14:45:59 +02:00
mana-sync docs(plans): mark llm-fallback-aliases SHIPPED, add M-by-M commit table 2026-04-26 21:27:57 +02:00
mana-tts feat(profile): voice interview with pre-rendered TTS audio + Orpheus/Zonos backends 2026-04-17 15:22:52 +02:00
mana-user refactor(shared-tailwind): rewrite themes.css to single-layer shadcn convention 2026-04-09 01:13:06 +02:00
mana-video-gen chore(matrix): final scrub of stale matrix references 2026-04-08 16:47:54 +02:00
mana-voice-bot fix(mana-voice-bot): move default port 3050 → 3024 + Windows GPU deployment notes 2026-04-08 13:14:57 +02:00
news-ingester refactor(shared-rss): extract RSS parsing + Readability into one package 2026-04-15 22:30:44 +02:00