# Module Reference

Mailforge ist in 10 togglebare Module aufgeteilt. Jedes hat eine eigene Verantwortung, eigene API-Routes, eigene UI-Komponenten und eigene Datenbank-Tabellen.

Module werden in `modules.config.ts` an/ausgeschaltet. Bei `false`:
- UI-Tab wird nicht gerendert
- API-Routes geben 404 zurück
- Worker-Tasks (falls vorhanden) starten nicht
- Tabellen werden bei Migration übersprungen (Phase D)

Manche Module haben **Dependencies**: wird die Dependency deaktiviert, wird das abhängige Modul automatisch mitdeaktiviert (mit Warnung im Boot-Log).

## Modul-Struktur

Jedes Modul lebt unter `src/modules/<id>/` und hat dieselbe Struktur:

```
src/modules/<id>/
├── module.ts        # Manifest (id, label, deps, requiredAdapters, tables)
├── lib/             # Modul-interne Library-Code (kein UI)
└── ui/              # React-Komponenten (kein Server-Code)
```

Was NICHT in `src/modules/<id>/` lebt:
- **Drizzle-Schema:** alle Tabellen in `src/lib/db/schema.ts` (geteilt — FKs überall).
- **Repos:** `src/lib/db/*-repo.ts` — werden von mehreren Modulen genutzt.
- **Adapter:** `src/adapters/crm/`, `src/adapters/send/` — Plugin-Pluggable.
- **App-Shell:** `src/app/`, `src/components/sidebar`, `src/components/settings`.

## Beim Migrieren weiterer Code-Folder

Wenn ein Modul-Folder noch in `src/lib/` oder `src/components/` liegt:

1. `mkdir -p src/modules/<id>/{lib,ui}`
2. `git mv src/lib/<folder>/*.ts src/modules/<id>/lib/`
3. Imports überall fixen:
   ```bash
   find src/ scripts/ -type f \( -name "*.ts" -o -name "*.tsx" \) \
     -exec perl -pi -e 's|\@/lib/<folder>|\@/modules/<id>/lib|g' {} +
   ```
4. `npm run typecheck` zum Verifizieren.

Bei mehreren Importpfad-Patterns in einem Move siehe das große `perl -pi -e`-Pattern in den
Plugin-Learnings (`/Users/tijan/.claude/skills/...` oder ggf. `.claude/skills/learnings/`).

---

## 1. `briefing` — AI & Strategy Generation

**Was es tut:** Aus einem freiformulierten Brief generiert OpenRouter (Claude Sonnet) eine strukturierte Email-Strategie + MJML-Source.

**API-Routes:**
- `POST /api/generate` — Vollständige Strategy + MJML
- `POST /api/suggest` — Briefing-Felder aus Text extrahieren
- `POST /api/draft-brief` — Brief aus Strichworten formulieren
- `POST /api/suggest-utm` — UTM-Parameter generieren

**Tabellen:** Keine eigenen — Briefing landet in `campaigns.briefingJson`

**Required Adapters:** `ai` (OpenRouter via `getOpenRouterKey()`)

**Optional?** Nein (Foundation-Modul, immer aktiv)

**Stub-Mode:** Ohne OpenRouter-Key liefert die Pipeline Stub-Strategy + Stub-MJML zurück — Tool bleibt benutzbar.

---

## 2. `editor` — TipTap MJML Editor

**Was es tut:** Inline-Editor für die generierte Email. TipTap mit MJML-aware-Bridge (`src/lib/email/inline-editor.ts`).

**API-Routes:**
- `POST /api/render-mjml` — MJML → HTML rendern
- `POST /api/render` — Full Render mit Substitution

**Tabellen:** Keine eigenen — TipTap-State in `campaigns.tiptapJson`

**Required Adapters:** —

**Optional?** Nein (Foundation-Modul)

---

## 3. `recipients` — Audience & Segments

**Was es tut:** Empfänger-Listen verwalten, CSV-Upload, CRM-Filter (Segment-Picker).

**API-Routes:**
- `GET /api/recipients` — Liste Recipients pro Workspace/Segment
- `POST /api/recipients/upload` — CSV-Import
- `GET /api/crm/audience` — Live-Filter gegen CRM-Adapter
- `GET /api/crm/facets` — Available filter-facets vom CRM
- `GET /api/crm/schema` — CRM-Feld-Schema (für Filter-UI)

**Tabellen:** `recipients`

**Required Adapters:** `crm` (CrmAdapter — siehe ADAPTERS.md)

**Optional?** Ja — wenn dein CRM die Empfänger-Auswahl bereits selbst macht und nur die fertige Liste an Mailforge übergibt, kann das Modul aus.

**Hinweis:** Cumas-Adapter unterstützt `facets`/`schema`-Endpoints; Generic-REST-Adapter nicht. UI fällt für Generic-REST auf einen einfacheren Filter zurück.

---

## 4. `sending` — Versand-Pipeline

**Was es tut:** Worker-Loop, der `email_queue` abarbeitet, Personalisierung anwendet, durch den aktiven SendProvider verschickt, Retry-Logik, A/B-Champion-Promotion.

**API-Routes:**
- `POST /api/campaign/send` — Campaign in die Queue legen
- `POST /api/send-test` — Test-Mail (kein Tracking, keine Stats)
- `GET /api/m365-diagnose` — Sender-Mailbox-Health (nur bei M365-Adapter)

**Tabellen:** `email_queue`, `tracking_events` (wird vom Worker geschrieben), `behavior_events`

**Required Adapters:** `send` (SendProvider — SMTP, M365 oder eigener)

**Optional?** Ja — wenn dieses Modul aus ist, läuft das Tool als **read-only Strategie-Workshop**: Templates können generiert werden, aber nicht verschickt. Sinnvoll wenn das CRM den Versand selbst macht und Mailforge nur die HTML/MJML-Quelle liefert.

**Worker-Verhalten:**
- Worker startet nur wenn `sending: true`
- Mindestens 9.500/Tag/Mailbox-Throttling (siehe M365-Default; SMTP-Standard ist 1.000/Tag)
- Conversion-Polling läuft im selben Worker-Tick (wenn `crm.pollConversions` definiert)

---

## 5. `scheduler` — Send-Time + A/B Tests

**Was es tut:** Zeitpunkt-basierter Versand, A/B-Test-Variant-Distribution (Pattern C: jede Variant trägt eigenes MJML), Champion-Promotion alle 5 Minuten.

**API-Routes:**
- `POST /api/scheduler/suggest` — KI-Empfehlung für Sendezeit
- `POST /api/scheduler` — Schedule eine Campaign
- `POST /api/trigger` — A/B-Variant-Distribution starten

**Tabellen:** `campaign_variants`, `campaign_versions`

**Required Adapters:** —

**Depends on:** `sending` (Scheduler ohne Sending hat keine Wirkung — auto-disable)

**Optional?** Ja

---

## 6. `stats` — Tracking & Attribution

**Was es tut:** Open/Click/Conversion-Tracking, Multi-Touch-Attribution-Modelle (last-click, first-click, linear, time-decay), Stats-Dashboard.

**API-Routes:**
- `GET /api/campaigns/[id]/stats`
- `GET /api/track/view/[messageId]` — Open-Pixel
- `GET /api/track/click/[messageId]` — Click-Wrapper
- `POST /api/track/conversion` — Pixel-basierte Conversion
- `POST /api/track/webhook/[workspaceSlug]` — Shop/CRM-Webhook (HMAC)

**Tabellen:** `tracking_events`, `behavior_events` (für Multi-Touch-Attribution)

**Required Adapters:** — (Conversion-Polling läuft via `adapters.crm.pollConversions` wenn `recipients` aktiv ist)

**Optional?** Ja — Tracking-Endpoints geben 404, generierte HTML enthält keine Tracking-Pixel/Wrapper wenn das Modul aus ist.

---

## 7. `heroImage` — AI Hero-Image Generation

**Was es tut:** fal.ai `flux/schnell` generiert ein Hero-Bild basierend auf einem Image-Brief aus der Strategy.

**API-Routes:**
- `POST /api/suggest-hero` — Image-Brief verfeinern (OpenRouter)
- `POST /api/regenerate-hero` — Neues Bild generieren
- `POST /api/upload-image` — Manueller Upload (Fallback wenn Modul aus)
- `POST /api/import-image-from-url` — URL → eigenes Asset

**Tabellen:** `assets`

**Required Adapters:** `image-gen` (fal.ai via `getFalKey()`)

**Optional?** Ja — wenn aus, kann der User trotzdem über `/api/upload-image` eigene Hero-Bilder hochladen. Asset-Tabelle bleibt aktiv für Uploads.

**Stub-Mode:** Ohne fal.ai-Key liefert die Pipeline ein `picsum.photos`-Placeholder.

---

## 8. `spamCheck` — Preflight Spam-Score

**Was es tut:** Lokale heuristische Spam-Bewertung (Trigger-Words, Subject-Length, ALL-CAPS, Bild/Text-Ratio, Gmail-Clipping, Plain-Text-Fallback, List-Unsubscribe-Header). Keine externe API.

**API-Routes:** Eingebettet in `/api/campaign/send` (Pre-Send-Check)

**Tabellen:** Keine — Result wird in `campaigns.spamReportJson` gespeichert

**Required Adapters:** —

**Optional?** Ja — wenn aus, wird das Spam-Report-Feld bei Send leer gelassen; kein Block.

**Verdict-Schwellen:**
- < 14 pts → `ship` (grün)
- 14-29 pts → `fix` (gelb, Warnung in UI)
- ≥ 30 pts → `reject` (rot, Send blockiert standardmäßig)

---

## 9. `sectionTemplates` — Wiederverwendbare MJML-Blöcke

**Was es tut:** Pro Workspace verwaltete MJML-Bausteine (Kontakt-Service, App-Download, Testimonial, FAQ, custom). Werden im Briefing-Tab in die generierte Mail eingespliced.

**API-Routes:**
- `GET/POST /api/section-templates` — CRUD pro Workspace
- `POST /api/suggest-section` — KI-Vorschlag für Section-Content (nur `bodyMode='tiptap'`)

**Tabellen:** `section_templates`

**Required Adapters:** —

**Optional?** Ja — Briefing funktioniert auch ohne, die Section-Slots im MJML bleiben dann leer.

**Body-Modi:**
- `tiptap` — Slot `{{TIPTAP_HTML}}`, KI-Suggest aktiv
- `mjml_raw` — User-MJML, kein Slot, KI deaktiviert
- `html_raw` — Sanitized HTML in `<mj-raw>`, kein Slot, KI deaktiviert

---

## 10. `triggerTemplates` — Transactional API

**Was es tut:** API-Endpoints, die Einzelmails (Bestellbestätigung, Passwort-Reset etc.) verschicken. Eigene API-Key-Authentifizierung pro Trigger (gehashed in DB).

**API-Routes:**
- `GET/POST /api/trigger-templates` — Template-CRUD
- `POST /api/trigger/[slug]` — Trigger-Endpoint (mit API-Key-Header)

**Tabellen:** `trigger_templates`, `trigger_logs`

**Required Adapters:** `send` (SendProvider)

**Depends on:** `sending`

**Optional?** Ja — Default `false` in `modules.config.ts`, opt-in für Setups die Transactional brauchen.

**Hinweis:** Trigger-Templates haben ein eigenes Container-Campaign-Konzept (`campaigns.kind='trigger_container'`) — eine versteckte langlebige Campaign pro Trigger-Template, die Stats aggregiert.

---

## Module-Status zur Laufzeit prüfen

```bash
npm run modules:list
```

Output zeigt aktive/inaktive Module + Dependency-Warnungen.

Im Code:
```typescript
import { isModuleEnabled } from '@/core/modules';

if (isModuleEnabled('sending')) {
  // start worker, queue jobs, etc.
}
```

Niemals direkt aus `modules.config.ts` lesen — die Dependency-Resolution passiert nur über das Registry.
