221 Commits

Author SHA1 Message Date
chahinebrini
b9c48dfd63 test: update stale comments in test fixes 2026-06-18 10:19:48 +02:00
chahinebrini
21ac150685 test: add reframe pattern to JB-002 mock response 2026-06-18 10:12:41 +02:00
chahinebrini
eb3fb129e9 test: update mail classifier score expectations 2026-06-18 10:12:37 +02:00
chahinebrini
057c6533af test: align voice quota tests with pro plan limits 2026-06-18 10:12:33 +02:00
chahinebrini
8a24ee890d test: add domainSubmission mock to profile-counts tests 2026-06-18 10:12:29 +02:00
chahinebrini
b0a7091ac7 feat(backend): /api/protection/event akzeptiert Extension-Secret
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
Deploy Staging / Build backend (Nitro) (push) Has been cancelled
Deploy Staging / Deploy zu Hetzner (push) Has been cancelled
- Extension-Auth-Path via x-extension-secret Header.
- Ermittelt userId anhand deviceId aus UserDevice.
- EXTENSION_SECRET in runtimeConfig + Infisical staging.
2026-06-18 09:38:05 +02:00
chahinebrini
a7ac5545ae fix(magic): entferne reportDeviceProtectionState aus Lock-Profil-Flow
Some checks failed
Deploy Staging / Build backend (Nitro) (push) Has been cancelled
ci/woodpecker/push/woodpecker Pipeline was successful
Deploy Staging / Deploy zu Hetzner (push) Has been cancelled
Build ReBreak Magic Windows / NSIS Installer (x64) (push) Has been cancelled
- Status kommt jetzt über /api/protection/event vom iOS Plugin.
- /api/devices/protection-state gibt jetzt { success: true, data: null }
  zurück, damit der Rust-Client nicht failed.
2026-06-18 09:22:41 +02:00
chahinebrini
97f8d593a5 feat(protection): /api/protection/event aktualisiert device_protection_states
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
Deploy Staging / Build backend (Nitro) (push) Has been cancelled
Deploy Staging / Deploy zu Hetzner (push) Has been cancelled
- Backend: /api/protection/event setzt bei Vorhandensein von deviceId
  (Body oder x-device-id Header) auch device_protection_states.
  source=mdm -> protectionType=nefilter, sonst vpn.
- Native App: sendet deviceId im Body von /api/protection/event.
- Magic App: Lock-Profil-Status wird nach lokaler Installation ans Backend
  gemeldet und Backend-Status neu geladen.
2026-06-18 09:10:33 +02:00
chahinebrini
e6fad4f51e fix(magic): always show supervised + lock profile true/false; company from exists 2026-06-18 07:10:12 +02:00
chahinebrini
dd84f8ec38 fix(backend): mdm.get must use status.enrolled, not hardcoded true 2026-06-18 06:47:05 +02:00
chahinebrini
e14a36f95a fix(backend): keep mdmId when device exists but is disenrolled; add exists flag 2026-06-18 06:28:03 +02:00
chahinebrini
bb8e0d3f62 fix(backend): mdm.get always returns full status shape even when not enrolled 2026-06-18 06:10:33 +02:00
chahinebrini
5117c7b37c fix(backend): getMdmStatusByUdid must check enrollments.enabled 2026-06-18 06:03:01 +02:00
chahinebrini
c8f5bfc82e fix(backend): make user_devices migration idempotent for fresh DBs
Add CREATE TABLE IF NOT EXISTS for rebreak.user_devices to the
hardware_id migration so fresh databases can migrate despite the
alphabetical ordering mismatch with 20260430_add_user_devices.
Also apply Prettier formatting to mdm.ts.
2026-06-18 04:04:05 +02:00
chahinebrini
7bf26a3138 test(backend): add unit tests for MDM health-check DB helpers 2026-06-18 03:53:26 +02:00
chahinebrini
15b4441deb feat(backend): add MDM health check cron 2026-06-18 03:42:18 +02:00
chahinebrini
74784fc4da feat(backend): add bulk MDM health status helpers
Add MdmEnrollmentStatus, UserDeviceMdmHealthRecord types and helpers:
- getLinkedUserDevices() to load iOS devices with NanoMDM UDIDs
- getMdmEnrollmentStatusesByUdids() for bulk NanoMDM lookups
- updateUserDeviceMdmHealth() to mirror status on UserDevice

Also fix PoolConfig option casing: queryTimeout -> query_timeout.
2026-06-18 03:36:13 +02:00
chahinebrini
b107262d60 feat(mdm): add NanoMDM health columns migration for UserDevice
Adds mdm_id, mdm_enrolled, mdm_supervised, mdm_last_seen_at and an
index on mdm_id. Uses IF NOT EXISTS to stay idempotent because mdm_id
was added manually before the migration was created.
2026-06-18 03:29:30 +02:00
chahinebrini
9065543b5a feat(backend): extend UserDevice with MDM health columns
Add mirrored NanoMDM enrollment, supervision and last-seen fields
(mdm_enrolled, mdm_supervised, mdm_last_seen_at) to UserDevice model.
Migration will follow in a separate task.
2026-06-18 03:15:09 +02:00
chahinebrini
943efe4b45 fix(backend): remove hardwareId references, use deviceId only 2026-06-18 00:06:39 +02:00
chahinebrini
6245fc4573 fix(magic): real MDM supervised state, mdmId matching, MDM status for unknown USB devices 2026-06-17 23:47:33 +02:00
chahinebrini
b87ec08431 feat(mdm): remove mdm_lock type, derive lockProfileInstalled from nefilter state 2026-06-17 22:32:40 +02:00
chahinebrini
5b0a4d03d2 feat(magic): identify current device via hardwareId, migrate existing devices 2026-06-17 17:18:40 +02:00
chahinebrini
10ca1c4bc5 tmp(backend): debug bypass code 000000 -> charioanouar session 2026-06-17 03:13:36 +02:00
chahinebrini
6b44fb2927 chore(prisma): add migration_lock.toml for deploy 2026-06-17 02:38:44 +02:00
chahinebrini
97977011ae feat(backend): include status, cooldownUntil, lastSeenAt and sleep flag 2026-06-16 20:28:21 +02:00
chahinebrini
4ee6849f3f feat(backend): add cooldown and cancel-cooldown endpoints 2026-06-16 20:24:57 +02:00
chahinebrini
b9bd577e47 feat(backend): add magicCooldownUntil to UserDevice 2026-06-16 20:13:23 +02:00
chahinebrini
1a270739bc feat(coach): Lyra-LLM auf direkten Anthropic Haiku 4.5 + SOS-Fallback-Kette; ElevenLabs reaktiviert
- Chat (message.post.ts): neuer nativer anthropic-Branch in tryModel
  (api.anthropic.com/v1/messages, x-api-key, system top-level), führt die
  Fallback-Kette claude-haiku-4-5 → gemini-flash-lite → gemini-flash → gpt-4o-mini.
- SOS (sos-stream.get.ts): Dispatch-Refactor mit buildUpstream() + Kandidaten-
  Fallback-Kette (anthropic → gemini → openai). Behebt strukturell den Bug
  "SOS liefert nur Krisen-Text" (vorher single fetch ohne Fallback). Nativer
  Anthropic-Stream: Delta-Parser liest content_block_delta.delta.text.
- nitro.config.ts: anthropicApiKey deklariert (ANTHROPIC_API_KEY).
- plan-features.ts: Legend-Voice zurück auf ElevenLabs eleven_turbo_v2_5
  (Cartesia-Übergang nach Zahlungsproblem 2026-06-08 aufgehoben, neuer Key).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 22:41:59 +02:00
chahinebrini
444688b6e9 test(eval): Concurrency-Limit + 429-Retry für Lyra-Eval-Suite
LYRA_EVAL_CONCURRENCY (Batch statt Promise.all-30-parallel) und
LYRA_EVAL_TIMEOUT_MS als Env-Schalter, plus 429-Retry mit Backoff —
nötig für Live-Runs gegen Provider mit niedrigem TPM-Limit (Groq
on-demand 12k). Default-Verhalten unverändert; Prompts/Regeln/
System-Prompt nicht angefasst.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 06:36:32 +02:00
chahinebrini
2c33ba55a4 fix(backend): username (Login-Identifikator) aus öffentlichen Payloads entfernt
community/posts.get.ts + social/profile/[userId].get.ts lieferten neben
nickname auch username an fremde Clients — username ist der Login-
Identifikator ({username}@rebreak.internal) und verletzt die Nickname-
Anonymitäts-Invariante (REQ-COMM-005 / R-DATA-07) + exponiert das halbe
Login-Credential-Paar. Frontend rendert das Feld nirgends (verifiziert);
totes Typ-Feld in stores/community.ts entfernt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 06:36:16 +02:00
chahinebrini
df3c4fafa3 fix(mail): force full-sweep on idle-daemon recovery (cold-start + downtime>5min)
Eine global-blocklistete Casino-Mail (mpmgame.com) rutschte in die Inbox,
obwohl die Absender-Domain Layer-2-Hard-Block ist. Ursache: kein Erkennungs-
Fehler, sondern ein Scan-Gap. Der idle-daemon crash-loopte heute (Port-3016-
Race, ~49 Restarts) und machte nach jedem Reconnect/Cold-Start nur einen
INKREMENTELLEN Scan (lastUid). Mails, die waehrend einer Downtime ankamen,
fielen damit dauerhaft durch das last-seen-UID-Raster.

Fix in backend/imap-idle/index.mjs:
- Cold-Start (Prozess-/pm2-Restart): immer forceFullSweep
- Reconnect nach Error mit Downtime > 5min (RECOVERY_SWEEP_THRESHOLD_MS):
  forceFullSweep beim naechsten erfolgreichen Connect
- Routinemaessiger IDLE-Renew (10min Close-Reopen): bleibt inkrementell
  -> kein teurer Dauer-Sweep im Normalbetrieb
- triggerScan(conn, {forceFullSweep}) + Coalesce-State "full"/"incremental"
  (ein full-Trigger wird nicht von einem spaeteren inkrementellen degradiert)

scan-internal.post.ts wertet body.forceFullSweep bereits korrekt aus.
Im Zweifel Full: eine global-blocklistete Domain darf nie durchrutschen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 21:58:00 +02:00
chahinebrini
7ce5e58f8d fix(backend): fix imapflow bundling — inline-Funktion statt fragiler Regex
Die inline-Regex /^(?!imapflow)/ griff nur gegen bare Specifier, nicht
gegen aufgelöste absolute Pfade (z.B. /node_modules/.pnpm/imapflow@x/…).
Nitro prüft inlineMatchers VOR externalMatchers — bei absoluten Pfaden
startete der String mit "/" → Lookahead ^(?!imapflow) schlug durch →
imapflow wurde gebundelt → imap-flow.mjs-Chunk → util.inherits-Crash
→ scan-internal 500 (Incident 2026-06-05, Regression in 1493752).

Fix: inline als Funktion mit expliziter blocklist (imapflow, expo-server-sdk,
@supabase/supabase-js). Deckt bare Specifier UND aufgelöste node_modules-Pfade.
Lokal verifiziert: imap-flow.mjs-Chunk weg, alle 3 Packages in server/package.json.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 15:35:52 +02:00
chahinebrini
1493752634 fix(backend): IMAP ECONNRESET crash-loop + dm-push ESM interop
- mail/scan{,-internal}.post.ts + imap-idle: attach imap.on('error')
  + targeted uncaughtException/unhandledRejection guards so a
  connection-level IMAP error (ECONNRESET / TLS disconnect) can no
  longer propagate to a process-level uncaughtException and kill the
  Nitro API (root cause of the staging 502 crash-loop)
- services/push.ts: lazy dynamic-import expo-server-sdk (singleton,
  like voip-push.ts) to fix "Class extends value [object Module]"
  (ESM/CJS undici interop) that broke DM push notifications;
  + nitro.config externals safety net

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 14:59:37 +02:00
chahinebrini
5fb441817f feat(magic): RE-hardening Quick Wins (ACL, #if DEBUG guards, rate-limit)
Härtung der öffentlich downloadbaren Magic-Apps gegen Reverse Engineering
(Assessment: docs/specs/magic-re-hardening.md):
- Windows: protection.json per ACL auf SYSTEM+Admins (DNS-Token nicht mehr von
  Standard-Usern lesbar) — setup.rs
- Mac: MacProfileInstaller.remove() + Debug-Supervision-Modi/Reset nur noch
  #if DEBUG (kein Removal-/Debug-Pfad im Release-Binary)
- Mac: staging-URL einmal als Konstante statt 4x Literal; interne Infra-Notizen
  aus String-Literalen raus
- Backend: Rate-Limit (10/IP/min) auf /api/magic/pair/redeem

NUR Backend-Teil deployt via Push; Mac/Win brauchen Xcode-/Cargo-Release-Build
(zied) + Smoke-Tests vor Release. MagicAPIClient.swift trägt etwas vorbestehenden
WIP mit (gleiche Magic-Client-Domäne).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 05:19:10 +02:00
chahinebrini
547f86187b fix(magic): createAdGuardClient idempotent — 400 → clients/update
Verwaiste AdGuard-Clients (magic_<deviceId> existiert, aber DB-Row fehlt nach
Crash zwischen clients/add und DB-Upsert) führten beim Re-Register zu 400 → 502.
Jetzt: bei 400 auf clients/update zurückfallen und den bestehenden Client auf
die frisch generierte clientId umbiegen. Behebt Magic-Register-502.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 02:45:28 +02:00
chahinebrini
0bff819b09 fix(tts): Legend übergangsweise auf Cartesia (ElevenLabs payment_issue 401)
ElevenLabs-Account hat ein Zahlungsproblem → Legend-TTS bekäme 502.
Leite Legend bis zur Klärung auf Cartesia sonic-3 (unlimited), damit
Legend-User Sprachausgabe behalten. Revert-Config steht im Kommentar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 03:04:17 +02:00
chahinebrini
db6db547ff fix(magic): DNS-Token auf 48 hex kürzen (AdGuard 63-char clientid-Limit)
randomBytes(32).hex = 64 Zeichen → AdGuard lehnt clientid ab
('hostname label is too long: got 64, max 63') → 'DNS-Provisioning
fehlgeschlagen' bei jeder Magic-Registrierung. Live gegen AdGuard
reproduziert: 64→HTTP400, 62→HTTP200.

- register: randomBytes(24).hex = 48 Zeichen (192 bit, ≤63)
- status: Token-Regex {64} → {1,63} (Länge nicht hart koppeln)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 23:33:56 +02:00
chahinebrini
a95e66560d feat(magic): Hard-Lock + Geräte-UX (Push, Realtime, Detail-Sheet, Offline-Removal)
Devices/Magic:
- Offline-Profil-Enroll deaktiviert (410) — Lock-PW würde im Klartext im
  Download landen; stationärer Schutz läuft jetzt nur über Rebreak Magic
- Mac-DNS-Template: ProhibitDisablement (Filter nicht abschaltbar)
- Push "Neues Gerät verbunden" an mobile Geräte bei neuer Bindung
- Realtime auf user_devices → Settings aktualisiert Magic-Bindings live
- Geräte-Detail-Sheet (Tap auf Gerät): Status, verbunden-seit, Schutz-Donut

Hard-Lock (server-gehaltenes Removal-PW, User sieht es nie):
- magic_removal_password generiert/gespeichert + in Profil injiziert (Lazy-Backfill)
- Reveal NUR bei Account-Löschung (user/delete) + Kündigung (stripe webhook),
  per Resend-Mail + in-Response
- Signing config-gated (inaktiv ohne Cert; Lock greift auch unsigniert)

Migrations: user_devices-Realtime-Publication + magic_removal_password-Spalten

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 22:26:25 +02:00
chahinebrini
80be124592 fix(dns): blocklist als ||domain^ statt apex-only → blockt Subdomains
Apex-only-Format (bare domain) blockte nur z.B. bet365.com, NICHT
www.bet365.com — Casino-Content + Google-Ad-Links liegen aber auf www.
Adblock-Syntax ||domain^ deckt Domain + alle Subdomains ab.
Betrifft alle DNS-Plattformen (Mac/Windows/DoH-Server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 13:26:16 +02:00
chahinebrini
7f529c3be3 feat(privacy): Coach-Payload an LLM-Provider pseudonymisieren (Art.9/DSGVO)
Schliesst hans-muellers K1-Befund (Datenschutz-Audit): der Coach-Prompt
sendete Identifier + Art.9-nahe Daten an US-LLMs (Gemini/OpenAI/Anthropic).

- message.post.ts: Geburtsjahr/exaktes Alter -> Altersgruppe (Dekaden-Bucket);
  Stadt komplett entfernt (Bundesland bleibt). Geschlecht/Familienstand/Beruf/
  Nickname unveraendert (gewollte Personalisierung; Nickname = Pseudonym).
- lyraMemoryExtract.ts: Extraction-Prompt reduziert Dritt-Klarnamen auf Rolle
  ("Frau Maria" -> "seine Frau"), keine Orte/Arbeitgeber im Memory-Content.
- 08-datenschutz-audit: Payload-Audit-Platzhalter durch Vorher/Nachher-Tabelle
  ersetzt, K1 erledigt, ZDR-Update (DPA/SCCs deemed-signed, TIA offen).

Pseudonymisierung zaehlt jetzt als zweite Schutzmassnahme neben ZDR fuer die TIA.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 08:35:13 +02:00
chahinebrini
96e1b8368c feat(lyra): deterministisches Krisen-Sicherheitsnetz (R-LYRA-01)
LLM-unabhaengiges Sicherheitsnetz fuer Lyras SOS-Pfad, schliesst das
Top-Risiko der Risiko-Akte (verpasste Krise, ISO 14971 R-LYRA-01).

Backend:
- crisis-filter.ts: deterministische Krisen-/Suizid-Erkennung (DE primaer,
  EN/FR/AR Grundabdeckung) auf den letzten User-Nachrichten, synchron, kein LLM
- sos-session.post: liefert crisisLevel sofort an die App (vor Stream-Start)
- sos-stream: sendet bei Krise zuerst 'crisis_chips' (BZgA/112/Telefonseelsorge);
  Fallback an 3 Stellen (LLM-Fehler/Abbruch/keine Chips) -> nie leerer Screen
- 43/43 Unit-Tests (crisis.json positiv, harmless.json False-Positive-Guard)

Frontend (urge.tsx):
- permanente rote Krisen-Bar oben, durch LLM-Chips nicht ueberschreibbar
  (eigener State-Slot), Hotline-Chips als tel:-Links
- neue Locale-Strings DE/EN

Risiko-Akte: R-LYRA-01 Restrisiko HOCH -> MITTEL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 07:56:34 +02:00
chahinebrini
ac05e255da feat(diga): Technische-Akte Runde 1+2 — Requirements, Risiko-Akte, Datenschutz-Audit, Lyra-Eval
DiGA-Dossier weiter aufgebaut (docs/specs/diga/):
- 03 Requirements (57 REQ-IDs aus dem Code, Traceability-Anker)
- 04 Risiko-Akte (ISO 14971 Erstliste; R-LYRA-01 = verpasste Krise als Top-Risiko)
- 05b Test-Verifikation (Maestro/Vitest-Inventar, IEC-62304-Luecken)
- 05c Lyra-Eval (Suite-Doku)
- 08 Datenschutz-Audit (hans-mueller; Groq/Art.9, DSFA-Pflicht, Mail-Agent, Anonymitaet)
- 00 Dossier-Plan Status aktualisiert

Lyra-Eval-Suite: backend/tests/eval/ (30 Prompts, 5 Kategorien, Vitest-Runner,
Mock-Modus ohne Key; Live-Run misst Crisis-Recall).

Konvergenter Befund aller 3 Agents: Lyras Krisen-Pfad haengt zu sehr am LLM
(R-LYRA-01 + fehlender SOS-Handler-Fallback) -> deterministisches Sicherheitsnetz noetig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 07:38:11 +02:00
chahinebrini
6a3c1e13da feat(lyra): admin DiGA-reminder post category
New 'erinnerung' topic for manual Lyra community posts that gently remind
users they can add optional, anonymous profile details. Wording stays
jargon-free (no 'DiGA'/'data'/'study'). Manual-only, not in the auto-cron
catalog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 00:11:01 +02:00
chahinebrini
b757486579 fix(mail): forceFullSweep on domain-add + 30s idle tick
Domain/display-name adds now force a full re-scan so newly-added gambling
senders are caught immediately instead of waiting for the incremental UID
window. IMAP-idle NOOP tick lowered 2min -> 30s to close the Junk-folder gap
faster (Outlook drops straight into Junk, which IDLE does not watch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 00:11:01 +02:00
chahinebrini
d31e45e2a8 feat(streak): protection-coverage metric (DiGA core) replacing broken streak
The old streak was non-functional: streaks.current_days was always 0 (never
computed/incremented), and the profile page read me.streak (0) + account
created_at as the "since" date — showing "0 days protected since <signup>"
for everyone. This is the DiGA key metric, so it had to be rebuilt.

New model: optimistic protection-coverage based on actual VPN/MDM protection
state, never resets to 0.
- backend: append-only protection_state_log + migration; POST /api/protection/event
  (ingestion, deduped) + GET /api/protection/coverage (read-time compute, no cron);
  server-side cooldown_disable event on cooldown resolve. Generous >6h-off/day rule.
- frontend: report protection on/off transitions (initial + flips, deduped) from
  useProtectionState; rewrote profile StreakSection → half-donut (protected vs
  unprotected) + progress bar (current streak → personal record) + empty state.
- coverage starts fresh from deploy (no historical backfill — clean data for DiGA).
- spec: docs/specs/protection-coverage-streak.md (shared contract).
- old streaks/streak_events/profiles.streak left intact (coach/scores consumers).

Also adds go-to-market one-pagers under docs/marketing/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 10:54:55 +02:00
chahinebrini
084f821bc5 fix(mail): incremental scan UID search returned seq-nums not UIDs
Der inkrementelle Scan-Pfad rief imap.search({ uid: 'X:*' }) ohne das
zweite { uid: true }-Argument auf → ImapFlow sendet "SEARCH UID X:*"
statt "UID SEARCH UID X:*" → Server antwortet mit Sequence-Numbers.
Die nachfolgende fetchAll(..., { uid: true }) interpretiert diese als
UIDs → fetcht die falschen (alten) Mails → neu eingegangene Gambling-
Mail (höchste echte UID) wird nie klassifiziert/gelöscht (>15min Lag).
Auch Ursache des "Command Error. 10" Log-Spams (Mega-UID-Liste).

Fix: { uid: true } als zweites search()-Argument.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 08:43:59 +02:00
chahinebrini
1f73bd8d8d fix(mail): BigInt-Serialisierung blockierte Phase-2-Persistierung
imapflow.status() liefert uidValidity als BigInt. Der Code reichte den BigInt in
JSON.stringify (patchFolderScanState) → 'TypeError: Do not know how to serialize
a BigInt' → vom stummen connection-level catch verschluckt → weder
patchFolderScanState noch markFullSweepDone liefen je → folder_scan_state blieb
{} + last_full_sweep_at NULL → inkrementeller Scan aktivierte nie (immer Full-Sweep).

Fix:
- serverUidValidity: Number((status).uidValidity ?? 0) — BigInt → number vor JSON.
- Stumme catches (auth/lock/conn) loggen jetzt; Persist-Calls (patchFolderScanState
  x2, markFullSweepDone) in eigene try/catch mit console.error — Diagnostik bleibt
  drin für Post-Deploy-Verify.

Lokal verifiziert: Build EXIT 0, imapflow extern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 12:16:09 +02:00
chahinebrini
cc549c7f17 perf(mail): re-apply incremental UID-scan (Phase 2) — safe w/ externals fix
Wiedereinspielung von mo's inkrementellem Scan (war nach dem imapflow-Bundle-
Incident zurückgerollt). Jetzt safe, weil der nitro externals-Fix (d64f31d)
imapflow robust extern hält — lokal verifiziert: Build clean, imapflow in
node_modules (kein Chunk), kein util.inherits.

- scan-internal: inkrementeller UID-Scan (status uidNext/uidValidity, search
  UID>lastUid, leere Ordner skip), UIDVALIDITY-Wächter, täglicher Quality-Full-
  Sweep (last_full_sweep_at). Klassifikation/Delete/Consent 1:1.
- db/mail: patchFolderScanState (atomic JSONB-merge) + markFullSweepDone;
  toter getAllActiveMailUserIds entfernt.

Schema + Migration (folder_scan_state, last_full_sweep_at) sind bereits live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 11:59:44 +02:00
chahinebrini
d64f31d115 fix(nitro): robuste imapflow-Externalisierung — behebt scan-internal 500
Der inline-Negative-Lookahead (/^(?!...)(?!imapflow)/) griff nur auf den nackten
Specifier, nicht auf aufgelöste node_modules-Pfade. Bei Module-Graph-Shifts
(Phase-2 Prisma-Felder) wurde imapflow doch gebundlet → CJS-inherits-Bruch
(util.inherits: superCtor.prototype undefined) → scan-internal 500 → Mail-Filtern
(USP) down (Incident 2026-06-05).

Fix: expliziter external-Eintrag mit Pfad-Regex /(^|node_modules/)imapflow(/|$)/
erzwingt imapflow robust extern. Lokal verifiziert: imapflow landet in
.output/server/node_modules + output-package.json, NICHT als chunk.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 11:29:39 +02:00