chahinebrini
20c74de81e
feat(domain-approval): Legend-priority + 24h-SLA-deadline + user-info cards
...
User-Wunsch: Legend-User priorisieren, 24h Approval-SLA, sichtbar wer/wann/Restzeit.
Backend:
- Schema: DomainSubmission.user @relation Profile (FK + composite-index status,createdAt)
- Migration: 20260509_domain_submission_user_relation (additive, FK via DO $$ block,
idempotent IF NOT EXISTS index)
- db/domains.ts getPendingSubmissions enriched:
- include user { id, nickname, plan }
- returns PendingSubmissionRow with planPriority (legend=2, pro=1, free=0)
- deadlineAt = createdAt + 24h
- msUntilDeadline (negative when overdue)
- sort: Legend > Pro > Free, FIFO innerhalb plan-bucket
- Constant ADMIN_APPROVAL_SLA_MS exported
Tests:
- backend/tests/admin/domains.test.ts — 5 cases (priority-sort, FIFO, deadline,
overdue, user-null fallback). 83 backend tests passing total.
Frontend (apps/admin/pages/domains.vue):
- Card-list (statt UTable — sichtbarer urgency-stripe links)
- Filter-chips „Alle | Nur Legend | Überfällig" mit live counts
- Per row: nickname, plan-badge (Legend = sparkles + warning/gold),
request-age (relative), deadline-countdown („noch 18h" / „ÜBERFÄLLIG (6h)")
- Visual urgency-stripe (1px border-left full-height):
- Overdue: red-600 + warning-icon
- <2h: red-500
- Legend: amber-400 (gold)
- <12h: yellow-500
- Normal: gray-700
⚠️ Migration auto-deploy via pipeline (b38bf17 detection).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:55:28 +02:00
chahinebrini
b36d9ae7c4
feat(admin): index.vue als quick-link-dashboard (Phase 2 done)
...
Phase 2-pending-Liste durch 4 NuxtLink-Cards ersetzt → tap navigiert direkt
zur jeweiligen page. Plus separater Stats-Quick-Link unten.
Pages-content unangetastet, nur dashboard refresh.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:48:35 +02:00
chahinebrini
6c1abc1ec9
feat(admin): responsive layout — bottom-tabs auf mobile, sidebar auf desktop
...
User-Wunsch: kleine screens (iPhone) keine sidebar, sondern bottom-tab-bar
wie native rebreak-app.
Layout-Architektur:
- Desktop (lg+, ≥1024px):
- Topbar: email + logout-button
- Sidebar links (w-56) mit full-label-nav (versteckt <lg)
- Content rechts (p-6)
- Mobile (<lg):
- Topbar: hamburger UDropdownMenu rechts (email + logout)
- Sidebar versteckt
- Content full-width (p-4 pb-24, damit content nicht hinter tab-bar)
- Bottom-tab-bar: fixed bottom-0, border-t, bg-gray-950/95 backdrop-blur
- 5 tabs in grid-cols-5: Home / Domains / Users / Stats / Mod
- Icon (h-5 w-5) + label (text-[10px])
- Active-state: text-white bg-gray-800 (route-match isActive helper)
- Safe-area-bottom respektiert via env(safe-area-inset-bottom)
Pages-content unangetastet, nur layout. Build clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:48:35 +02:00
chahinebrini
3bc5360832
feat(admin): Phase 2 Frontend — Domains/Stats/Users/Moderation pages + responsive layout
...
4 page-implementations + server-route-proxies (admin-secret stays server-only):
DOMAINS (apps/admin/pages/domains.vue):
- UTable mit pending-submissions queue
- Approve / Reject buttons per row
- Reject-confirm-modal mit optional note
- useToast + refresh nach action
- 3 server-routes: GET list + POST approve/reject
STATS (apps/admin/pages/stats.vue):
- Stat-cards: Total Users + delta-week, Total Posts + delta-week,
Domains pending (link to /domains), Domains approved, Feedback pending,
Lyra-Posts (30d)
- UProgress für Domain-Approval-Quote
- Auto-refresh 60s + manual refresh-button
- USkeleton während loading
- 1 server-route: GET /api/stats
USERS (apps/admin/pages/users.vue):
- UTable mit avatar+nickname/username, plan-badge, streak, status, createdAt
- Search-input + plan-filter dropdown
- Action-dropdown per row: Plan-Change / Ban-Toggle / Soft-Delete
- 3 separate UModals mit confirm-pattern
- Cursor-pagination (Mehr laden button)
- 3 server-routes: GET list, PATCH /:id, DELETE /:id
MODERATION (apps/admin/pages/moderation.vue):
- Stack-layout mit card-pro-item (statt table — content-preview braucht space)
- Type-badge (Post/Comment), Author + Plan-badge, content-preview (200 chars),
reportedAt
- Action-buttons: Dismiss (gray), Delete Content (red soft + reason-modal),
Ban User (red solid + warning-modal)
- Empty-state, cursor-pagination
- 4 server-routes: GET /queue, POST /:id/dismiss/delete/ban-user
Server-route pattern (apps/admin/server/api/...):
- Use useRuntimeConfig().adminSecret server-only
- Client never sees x-admin-secret
- Body/query passthrough to backend
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:48:35 +02:00
chahinebrini
f3c68c87e2
fix(admin): port-override AFTER infisical injection (was hijacking backend port)
...
Bug: admin-app PM2-service kaperte port 3016 vom backend-staging.
Backend ging in crash-loop (22 restarts), nginx routete /api auf admin Nuxt-app
→ HTTP 302 redirect zu /login → Frontend „JSON Parse error: Unexpected character: <".
Root cause: backend-staging-Infisical-env hat PORT=3016, NITRO_PORT=3016 als
secrets. Admin-Script exportierte PORT=3017 VOR `infisical run` — aber Infisical
overrode mit den 3016-secrets innerhalb seines bash-c block.
Fix: PORT/NITRO_PORT/NITRO_HOST exports MOVED inside `bash -c` block, AFTER
infisical-env-injection. Hard-override gewinnt jetzt.
Verified manual:
- pm2 stop+delete rebreak-admin-staging → port 3016 frei
- pm2 restart rebreak-staging → online auf 3016
- curl /api/auth/me → HTTP 401 JSON (war 302 HTML)
- Backend wieder healthy
Pending: nächster admin-deploy via GH-Actions wird sich mit fixed script
auf 3017 starten.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 22:52:57 +02:00
chahinebrini
8d95e192a6
chore(admin): restore executable bit on deploy scripts
2026-05-08 22:30:17 +02:00
chahinebrini
f7c9c79365
feat(admin): Admin App initial commit + Deploy-Infrastructure
...
apps/admin/:
- Nuxt 4.1.3 + @nuxt/ui 4 + @nuxtjs/supabase, port 3017 staging
- 7 pages: index (59 LOC dashboard), login (72 LOC), auth/confirm, plus stubs
für domains/users/stats/moderation (14-17 LOC each, content für separate
Phase 2 Session)
- composables/useAdminAuth.ts: Supabase login + verifyAdminRole hook
- middleware/admin-auth.ts: route guard (Phase 3 backend-check ready)
- layouts/default.vue, app.vue, README.md
- nuxt.config.ts: SSR=true, port 3017, dark-mode preference, Supabase
pkce-flow, runtimeConfig.adminSecret für Phase 3 backend-binding
Deploy-Infrastructure:
- .github/workflows/deploy-admin-staging.yml: build admin auf push to main mit
path-filter apps/admin/**, scp tar zu Server, atomic-mv + pm2 restart
- scripts/deploy-admin-from-artifact.sh: Server-side deploy (extract, atomic mv,
pm2 reload). Kein prisma-migrate (admin hat kein eigenes DB-Schema).
- apps/admin/start-admin-staging.sh: pm2 start-script mit Infisical-wrapper,
port 3017, mappt Infisical SUPABASE_URL/KEY auf NUXT_PUBLIC_*
- ecosystem.config.js: rebreak-admin-staging Eintrag (port 3017,
max_memory_restart 400M)
- ops/nginx/admin-staging.rebreak.org.conf: HTTP→HTTPS redirect, SSL paths,
proxy auf 127.0.0.1:3017, noindex header
Pending User-Actions für go-live:
1. DNS-A-Record admin.staging.rebreak.org → 49.13.55.22
2. SSL-cert via certbot (oder bestehender wildcard *.staging.rebreak.org)
3. nginx-config auf Server aktivieren (sudo cp + ln + reload)
4. pm2 initial start: pm2 start ecosystem.config.js --only rebreak-admin-staging
5. Infisical-secret ADMIN_SECRET (server-only, Phase 3 binding)
GH-Actions: keine neuen Secrets (nutzt bestehende HETZNER_SSH_KEY/HOST/USER)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 22:30:17 +02:00