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>
40 lines
1.1 KiB
TypeScript
40 lines
1.1 KiB
TypeScript
// apps/admin/server/api/moderation/[id]/ban-user.post.ts
|
|
//
|
|
// Proxy: leitet Ban-User-Request ans Backend. Profile.banned wird gesetzt
|
|
// (gleicher Patch-Pattern wie /api/admin/users/[id]).
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const config = useRuntimeConfig();
|
|
const apiBase = config.public.apiBase;
|
|
const adminSecret = config.adminSecret;
|
|
|
|
if (!adminSecret) {
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: "ADMIN_SECRET nicht konfiguriert",
|
|
});
|
|
}
|
|
|
|
const id = getRouterParam(event, "id");
|
|
if (!id) throw createError({ statusCode: 400, statusMessage: "ID fehlt" });
|
|
|
|
const body = await readBody(event).catch(() => ({}));
|
|
|
|
try {
|
|
return await $fetch(
|
|
`${apiBase}/api/admin/moderation/${encodeURIComponent(id)}/ban-user`,
|
|
{
|
|
method: "POST",
|
|
headers: { "x-admin-secret": adminSecret },
|
|
body: body ?? {},
|
|
},
|
|
);
|
|
} catch (err: any) {
|
|
throw createError({
|
|
statusCode: err?.statusCode ?? 502,
|
|
statusMessage:
|
|
err?.statusMessage ?? err?.message ?? "Backend-Request fehlgeschlagen",
|
|
});
|
|
}
|
|
});
|