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>
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
// composables/useAdminAuth.ts
|
|
//
|
|
// Admin-Auth-Composable.
|
|
//
|
|
// Auth-Architektur:
|
|
// 1. User loggt sich via Supabase (Email/Password) ein -- normaler Supabase-JWT.
|
|
// 2. Nach Login: Backend-Aufruf gegen GET /api/admin/verify-admin (Phase 3).
|
|
// Das Backend prueft ob die Supabase-User-ID in der admin_users-Tabelle steht.
|
|
// Bei Fehlschlag: sofort ausloggen (kein einfacher User darf rein).
|
|
// 3. Admin-Status wird in useSupabaseUser() gehalten -- kein extra State noetig.
|
|
//
|
|
// Phase 3 TODO: Backend muss /api/admin/verify-admin implementieren mit requireAdmin-Middleware.
|
|
//
|
|
// DSGVO-Note: Admin-Logins werden server-side in audit_log geloggt (Phase 4 -- hans-mueller).
|
|
|
|
export function useAdminAuth() {
|
|
const supabase = useSupabaseClient()
|
|
const user = useSupabaseUser()
|
|
const config = useRuntimeConfig()
|
|
|
|
// Computed E-Mail fuer Topbar-Anzeige
|
|
const adminEmail = computed(() => user.value?.email ?? "")
|
|
|
|
// Login via Supabase Email/Password
|
|
async function loginWithPassword(email: string, password: string) {
|
|
const { error } = await supabase.auth.signInWithPassword({ email, password })
|
|
if (error) throw new Error(error.message)
|
|
|
|
// Phase 3: Admin-Verifikation gegen Backend.
|
|
// Aktuell nur Supabase-Login -- requireAdmin-Check kommt in Phase 3.
|
|
// TODO: await verifyAdminRole()
|
|
}
|
|
|
|
// Logout -- Supabase-Session beenden, zurueck zu /login
|
|
async function logout() {
|
|
await supabase.auth.signOut()
|
|
await navigateTo("/login")
|
|
}
|
|
|
|
// Phase 3: Backend-Check ob Supabase-User in admin_users-Tabelle steht.
|
|
// Wirft Error wenn nicht -- Caller soll dann logout() aufrufen.
|
|
async function verifyAdminRole() {
|
|
const session = await supabase.auth.getSession()
|
|
const token = session.data.session?.access_token
|
|
if (!token) throw new Error("Keine aktive Session")
|
|
|
|
const res = await $fetch(`${config.public.apiBase}/api/admin/verify-admin`, {
|
|
method: "GET",
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
|
|
// Backend gibt { isAdmin: true } zurueck -- alles andere ist Zugriffsverweigerung.
|
|
if (!(res as { isAdmin: boolean }).isAdmin) {
|
|
await supabase.auth.signOut()
|
|
throw new Error("Kein Admin-Zugriff")
|
|
}
|
|
}
|
|
|
|
return {
|
|
user,
|
|
adminEmail,
|
|
loginWithPassword,
|
|
logout,
|
|
verifyAdminRole,
|
|
}
|
|
}
|