chahinebrini
29c5d9c8e5
feat(admin): Phase 2 Backend — Users + Moderation endpoints + 2 schema migrations
...
Two parallel agent-batches consolidated:
USERS-MGMT (rebreak-backend agent):
- Schema: Profile gets banned, bannedAt, bannedReason, deletedAt + indexes
- Migration: 20260509_profile_admin_management (additive, idempotent)
- DB-layer backend/server/db/adminUsers.ts:
listAdminUsers (cursor-pagination, search, plan-filter)
updateAdminUser (plan-validation, ban-stamping)
softDeleteAdminUser (DSGVO PII-scrub: nickname=null, email=deleted-{shortid}@deleted.local )
- 3 endpoints under /api/admin/users:
GET (list with ?cursor&limit&q&plan&includeDeleted)
PATCH /:id (plan/banned/bannedReason/lyraVoiceId)
DELETE /:id (soft-delete idempotent)
- 12 tests passing
MODERATION (rebreak-backend agent):
- Schema: CommunityPost+CommunityReply get isModerated, isDeleted, deletedAt,
reportedAt + index (is_moderated, reported_at)
- New ModerationAction model → audit-log table
- Migration: 20260509_moderation_queue (additive, idempotent)
- DB-layer backend/server/db/moderation.ts:
listModerationQueue (merge posts+comments, sort by reportedAt, cursor)
dismissModerationItem
deleteModerationItem (content scrub + audit snapshot)
banUserFromModerationItem (reuses banned/bannedAt/bannedReason fields)
- 4 endpoints under /api/admin/moderation:
GET /queue, POST /:id/dismiss, POST /:id/delete, POST /:id/ban-user
- 11 tests passing
Backend total: 78 tests passing | 4 skipped (pre-existing requireAdmin tests)
Auth: x-admin-secret header (consistent with existing /admin/* endpoints).
DSGVO:
- Soft-delete scrubt PII statt hard-delete
- Email NICHT in admin user-list (lebt nur in auth.users)
- Audit-log für moderation-actions (90-day cleanup-cron pending hans-mueller-DSB-review)
⚠️ MIGRATIONS — auto-deploy via pipeline (commit b38bf17 detection):
- 20260509_profile_admin_management
- 20260509_moderation_queue
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:46:44 +02:00
chahinebrini
e12da5385c
feat(admin): Phase 3 — requireAdmin middleware + verify-admin endpoint
...
Backend-side admin-auth. Admin-App (apps/admin/) braucht das damit
useAdminAuth.verifyAdminRole() nach Login server-side prüfen kann ob User
in admin_users-tabelle steht.
New schema:
- model AdminUser → table rebreak.admin_users (user_id UUID PK FK Profile.id,
created_at, added_by). Migration 20260508_admin_users/migration.sql.
- ⚠️ SCHEMA-MIGRATION — NICHT autopushen. User entscheidet wann pipeline
triggert.
New backend code:
- backend/server/db/admin.ts: isAdminUser(userId) → boolean
- backend/server/utils/auth.ts: requireAdmin(event) wraps requireUser +
isAdminUser-check. Throws 403 wenn nicht admin.
- backend/server/api/admin/verify-admin.get.ts: GET endpoint. Returns
{ isAdmin: true, userId, email } bei success, 403 sonst, 401 if not auth'd.
Tests (5 cases in tests/admin/verify-admin.test.ts):
- isAdminUser DB-layer: row exists/null
- requireAdmin: admin → user, non-admin → 403, no token → 401
- Endpoint: admin → success, non-admin → 403
Pending User-Actions nach Push+Deploy:
1. Migration deploy auf staging:
ssh rebreak-server && cd /srv/rebreak && pnpm exec prisma migrate deploy
2. Seed-Admin eintragen:
INSERT INTO "rebreak"."admin_users" ("user_id", "created_at")
VALUES ('128df360-2008-4d6f-8aa1-bdb41ec1362f', NOW())
ON CONFLICT DO NOTHING;
3. Admin-App composables/useAdminAuth.ts kann dann verifyAdminRole()
gegen GET /api/admin/verify-admin aufrufen
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 22:16:47 +02:00
chahinebrini
53d6e69512
feat(api): GET /api/profile/me/demographics endpoint
...
Read-counterpart zum existierenden PATCH/DELETE. Frontend braucht den endpoint
um nach Page-Reload die schon-gespeicherten Werte zu fetchen — sonst sieht User
leere Felder und denkt save funktioniert nicht.
- backend/server/db/profile.ts: getDemographics(userId) — SELECT der 9 fields +
demographics_consent_at + demographics_withdrawn_at
- backend/server/api/profile/me/demographics.get.ts: requireUser + getDemographics
+ ISO-string conversion. 404 wenn Profile-row fehlt.
- backend/tests/profile/demographics.get.test.ts: 5 vitest cases
(null fields, 404, populated, withdrawn, 401)
Response shape kompatibel mit PATCH-input (gleiche field names, camelCase) plus
metadata consentAt/withdrawnAt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 21:31:53 +02:00
chahinebrini
d7efd627f5
feat(profile): Demographics employment-split + Pro-Trial-Reward + tests
...
- New Prisma migration 20260508_demographics_employment_split:
ADD COLUMNS employment_status / shift_work / industry / job_tenure
(legacy `profession` kept untouched)
- PATCH /api/profile/me/demographics:
Zod-enums updated to match Frontend values (employed/self_employed/in_training/
unemployed/retired/homemaking/other; jobTenure: less_1y/1_3y/3_5y/5_10y/more_10y)
- profile.ts db-layer: tryAwardProTrial covers new + legacy fields,
withdrawDemographics nulls all (incl. legacy profession)
- Vitest: 8-line trial happy-path + guard rails (free+pro+legend+used) +
zod-validation tests covering new enum boundaries
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 19:31:28 +02:00
chahinebrini
cddc4d0f26
feat(profile): DiGA-Demographics + Pro-Trial-Reward + 7 Profile-Endpoints
...
Schema:
- 8 neue Profile-Felder fuer DiGA-Demographics (birthYear/gender/maritalStatus/
profession/bundesland/city + 2 consent-stamps demographicsConsentAt/
demographicsWithdrawnAt)
- 4 Pro-Trial-Felder (proTrialStartedAt/ExpiresAt/Source/UsedAt) — Free-User
bekommen 1 Woche Pro als Reward fuer DiGA-Daten-Pflege (siehe
project_demographic_pro_trial_reward.md)
- lyra_voice_id (Legend-only Voice-Picker)
- diga_banner_dismissed_at (server-side persistence ueber Re-Install)
- last_install_at (Streak-Logic survives Re-Install)
- Migration 20260507_profile_demographics_and_trial: alle Felder optional,
keine Backfill-Logik notwendig
Endpoints (alle auth-protected, scope=me):
- GET /api/profile/me/sos-insights
- GET /api/profile/me/cooldown-history
- GET /api/profile/me/approved-domains
- POST /api/profile/me/install-event (track app re-installs)
- POST /api/profile/me/diga-banner-dismiss
- PATCH /api/profile/me/demographics (consent-stamp + re-grant-after-withdrawal in tx)
- DELETE /api/profile/me/demographics (DSGVO right-to-be-forgotten)
Plugin:
- pro-trial-expiry-cron: 6h-Interval, conservative-fallback (revoke nur wenn
kein stripeSubId), 60s initial-delay damit Server-boot nicht blockiert wird
Tests:
- vitest config + erste Test-Files (test-infrastructure setup)
Memory:
- feedback_demographics_user_initiated.md (Lyra darf NIE extrahieren)
- project_demographic_pro_trial_reward.md (Pro-Trial-Reward-Mechanik)
- project_profile_page_design.md (UI-Showpiece, eigene/fremde-Ansicht streng getrennt)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 21:14:06 +02:00
RaynisDev
b58588cf3c
initial commit: rebreak-monorepo (RN app + standalone Nitro backend)
2026-05-06 07:13:43 +02:00