import { listAdminUsers } from "../../../db/adminUsers"; /** * GET /api/admin/users — Admin-User-Liste (cursor-paginated, search, plan-filter) * * Query-Params: * ?cursor= — pagination cursor (id from previous nextCursor) * ?limit=50 — max 100 * ?q= — fuzzy-match auf nickname + username (case-insensitive) * ?plan=free|pro|legend — filter * ?includeDeleted=1 — auch soft-deleted users zurückgeben * * Auth: x-admin-secret Header (gleicher Pattern wie /api/admin/stats). * * DSGVO-Note: Email lebt in supabase.auth.users — bewusst NICHT joinen, * Admin-UI zeigt nur Nickname/Username (siehe memory/feedback_anonymity_nickname). * Wenn Email für DSGVO-Auskunft nötig ist → separater Endpoint mit * zusätzlicher Bestätigung (Phase F, hans-mueller). */ export default defineEventHandler(async (event) => { const config = useRuntimeConfig(); const secret = getHeader(event, "x-admin-secret"); if (!config.adminSecret || secret !== config.adminSecret) { throw createError({ statusCode: 401, message: "Unauthorized" }); } const query = getQuery(event); const limitNum = query.limit ? Number(query.limit) : undefined; const planRaw = typeof query.plan === "string" ? query.plan : undefined; const plan = planRaw === "free" || planRaw === "pro" || planRaw === "legend" ? planRaw : undefined; return listAdminUsers({ cursor: typeof query.cursor === "string" ? query.cursor : undefined, limit: Number.isFinite(limitNum) ? limitNum : undefined, q: typeof query.q === "string" ? query.q : undefined, plan, includeDeleted: query.includeDeleted === "1" || query.includeDeleted === "true", }); });