'use client' import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import { authFetch, clearToken, getApiBase, getToken } from '../lib/auth' import AdminShell from '../ui/AdminShell' type AdminUser = { username: string role: string authProvider?: string | null lastLoginAt?: string | null isBlocked?: boolean } const formatLastLogin = (value?: string | null) => { if (!value) return 'Never' const date = new Date(value) if (Number.isNaN(date.valueOf())) return value return date.toLocaleString() } export default function UsersPage() { const router = useRouter() const [users, setUsers] = useState([]) const [error, setError] = useState(null) const [loading, setLoading] = useState(true) const [jellyfinSyncStatus, setJellyfinSyncStatus] = useState(null) const [jellyfinSyncBusy, setJellyfinSyncBusy] = useState(false) const loadUsers = async () => { try { const baseUrl = getApiBase() const response = await authFetch(`${baseUrl}/admin/users`) if (!response.ok) { if (response.status === 401) { clearToken() router.push('/login') return } if (response.status === 403) { router.push('/') return } throw new Error('Could not load users.') } const data = await response.json() if (Array.isArray(data?.users)) { setUsers( data.users.map((user: any) => ({ username: user.username ?? 'Unknown', role: user.role ?? 'user', authProvider: user.auth_provider ?? 'local', lastLoginAt: user.last_login_at ?? null, isBlocked: Boolean(user.is_blocked), })) ) } else { setUsers([]) } setError(null) } catch (err) { console.error(err) setError('Could not load user list.') } finally { setLoading(false) } } const toggleUserBlock = async (username: string, blocked: boolean) => { try { const baseUrl = getApiBase() const response = await authFetch( `${baseUrl}/admin/users/${encodeURIComponent(username)}/${blocked ? 'block' : 'unblock'}`, { method: 'POST' } ) if (!response.ok) { throw new Error('Update failed') } await loadUsers() } catch (err) { console.error(err) setError('Could not update user access.') } } const updateUserRole = async (username: string, role: string) => { try { const baseUrl = getApiBase() const response = await authFetch( `${baseUrl}/admin/users/${encodeURIComponent(username)}/role`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ role }), } ) if (!response.ok) { throw new Error('Update failed') } await loadUsers() } catch (err) { console.error(err) setError('Could not update user role.') } } const syncJellyfinUsers = async () => { setJellyfinSyncStatus(null) setJellyfinSyncBusy(true) try { const baseUrl = getApiBase() const response = await authFetch(`${baseUrl}/admin/jellyfin/users/sync`, { method: 'POST', }) if (!response.ok) { const text = await response.text() throw new Error(text || 'Sync failed') } const data = await response.json() setJellyfinSyncStatus(`Synced ${data?.imported ?? 0} Jellyfin users.`) await loadUsers() } catch (err) { console.error(err) setJellyfinSyncStatus('Could not sync Jellyfin users.') } finally { setJellyfinSyncBusy(false) } } useEffect(() => { if (!getToken()) { router.push('/login') return } void loadUsers() }, [router]) if (loading) { return
Loading users...
} return ( } >
{error &&
{error}
} {jellyfinSyncStatus &&
{jellyfinSyncStatus}
} {users.length === 0 ? (
No users found yet.
) : (
{users.map((user) => (
{user.username}
Role: {user.role} Login type: {user.authProvider || 'local'} Last login: {formatLastLogin(user.lastLoginAt)}
))}
)}
) }