feat(admin): web admin panel — user CRUD, role assignment, system health (#125)
Some checks failed
ci/woodpecker/push/ci Pipeline failed

- Add BetterAuth admin plugin with admin/member role configuration
- Add banned/banReason/banExpires fields to users schema
- Gateway AdminModule: user list/create/role/ban/delete endpoints at /api/admin/users
- Gateway AdminHealthController: system health at /api/admin/health (DB, Valkey, agent pool, providers)
- AdminGuard: enforces admin role on all /api/admin/* routes
- Web admin page: two-tab UI (User Management + System Health)
- AdminRoleGuard: client-side redirect for non-admin users
- Add adminClient to web auth-client for BetterAuth admin plugin integration
- Add @mosaic/queue as gateway dep for Valkey health check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 14:15:50 -05:00
parent 76abf11eba
commit 3bc863dd1d
13 changed files with 893 additions and 81 deletions

View File

@@ -0,0 +1,40 @@
'use client';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useSession } from '@/lib/auth-client';
interface AdminRoleGuardProps {
children: React.ReactNode;
}
export function AdminRoleGuard({ children }: AdminRoleGuardProps): React.ReactElement | null {
const { data: session, isPending } = useSession();
const router = useRouter();
const user = session?.user as
| (NonNullable<typeof session>['user'] & { role?: string })
| undefined;
useEffect(() => {
if (!isPending && !session) {
router.replace('/login');
} else if (!isPending && session && user?.role !== 'admin') {
router.replace('/');
}
}, [isPending, session, user?.role, router]);
if (isPending) {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-sm text-text-muted">Loading...</div>
</div>
);
}
if (!session || user?.role !== 'admin') {
return null;
}
return <>{children}</>;
}