"use client" import { useEffect, useState } from "react" import { useRouter } from "next/navigation" import { api, SystemUser, SystemGroup, LoginEntry } from "@/lib/api" import { Header } from "@/components/Header" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Dialog } from "@/components/ui/dialog" import { AlertCircle, Plus, Trash2, Lock, Unlock, Key, Terminal, Search as SearchIcon } from "lucide-react" export default function IdentitiesPage() { const router = useRouter() const [activeTab, setActiveTab] = useState<"users" | "groups" | "history">("users") const [usersSubTab, setUsersSubTab] = useState<"linux" | "samba">("linux") const [users, setUsers] = useState([]) const [sambaUsers, setSambaUsers] = useState([]) const [groups, setGroups] = useState([]) const [logins, setLogins] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [searchQuery, setSearchQuery] = useState("") const [selectedGroupForAdd, setSelectedGroupForAdd] = useState(null) const [selectedUserForGroup, setSelectedUserForGroup] = useState(null) // Dialog states const [createUserDialog, setCreateUserDialog] = useState(false) const [createGroupDialog, setCreateGroupDialog] = useState(false) const [passwordDialog, setPasswordDialog] = useState(false) const [shellDialog, setShellDialog] = useState(false) const [sambaPasswordDialog, setSambaPasswordDialog] = useState(false) const [deleteUserDialog, setDeleteUserDialog] = useState(false) const [deleteGroupDialog, setDeleteGroupDialog] = useState(false) const [addUserToGroupDialog, setAddUserToGroupDialog] = useState(false) // Form states const [selectedUser, setSelectedUser] = useState(null) const [selectedGroup, setSelectedGroup] = useState(null) const [newUsername, setNewUsername] = useState("") const [newHomeDir, setNewHomeDir] = useState("") const [newShell, setNewShell] = useState("/bin/bash") const [newGecos, setNewGecos] = useState("") const [newGroupName, setNewGroupName] = useState("") const [newPassword, setNewPassword] = useState("") const [newShellValue, setNewShellValue] = useState("") const [sambaPassword, setSambaPassword] = useState("") const [removeHomeDir, setRemoveHomeDir] = useState(true) const [loginLimit, setLoginLimit] = useState(50) useEffect(() => { const token = localStorage.getItem("access_token") if (!token) { router.push("/login") return } loadData() }, [router]) const loadData = async () => { try { setLoading(true) setError(null) const [usersData, sambaUsersData, groupsData, loginsData] = await Promise.all([ api.getUsers(), api.getSambaUsers(), api.getGroups(), api.getLoginHistory(loginLimit), ]) setUsers(usersData) setSambaUsers(sambaUsersData) setGroups(groupsData) setLogins(loginsData) } catch (err) { setError(err instanceof Error ? err.message : "Failed to load data") } finally { setLoading(false) } } const handleCreateUser = async () => { if (!newUsername.trim()) return try { await api.createUser(newUsername, newHomeDir || undefined, newShell, newGecos || undefined) setNewUsername("") setNewHomeDir("") setNewShell("/bin/bash") setNewGecos("") setCreateUserDialog(false) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to create user") } } const handleDeleteUser = async () => { if (!selectedUser) return try { await api.deleteUser(selectedUser, removeHomeDir) setSelectedUser(null) setDeleteUserDialog(false) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to delete user") } } const handleChangePassword = async () => { if (!selectedUser || !newPassword.trim()) return try { await api.changePassword(selectedUser, newPassword) setSelectedUser(null) setNewPassword("") setPasswordDialog(false) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to change password") } } const handleChangeShell = async () => { if (!selectedUser || !newShellValue.trim()) return try { await api.changeShell(selectedUser, newShellValue) setSelectedUser(null) setNewShellValue("") setShellDialog(false) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to change shell") } } const handleSetSambaPassword = async () => { if (!selectedUser || !sambaPassword.trim()) return try { await api.setSambaPassword(selectedUser, sambaPassword) setSelectedUser(null) setSambaPassword("") setSambaPasswordDialog(false) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to set Samba password") } } const handleLockUser = async (username: string) => { try { await api.lockUser(username) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to lock user") } } const handleUnlockUser = async (username: string) => { try { await api.unlockUser(username) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to unlock user") } } const handleCreateGroup = async () => { if (!newGroupName.trim()) return try { await api.createGroup(newGroupName) setNewGroupName("") setCreateGroupDialog(false) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to create group") } } const handleAddUserToGroup = async () => { if (!selectedUserForGroup || !selectedGroupForAdd) return try { await api.addUserToGroup(selectedUserForGroup, selectedGroupForAdd) setSelectedUserForGroup(null) setSelectedGroupForAdd(null) setAddUserToGroupDialog(false) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to add user to group") } } const handleRemoveUserFromGroup = async (username: string, groupname: string) => { try { await api.removeUserFromGroup(username, groupname) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to remove user from group") } } const handleDeleteGroup = async () => { if (!selectedGroup) return try { await api.deleteGroup(selectedGroup) setSelectedGroup(null) setDeleteGroupDialog(false) loadData() } catch (err) { setError(err instanceof Error ? err.message : "Failed to delete group") } } // Filter users and groups based on search const filteredUsers = users.filter(u => u.username.toLowerCase().includes(searchQuery.toLowerCase()) || u.gecos?.toLowerCase().includes(searchQuery.toLowerCase()) ) const filteredSambaUsers = sambaUsers.filter(u => u.username.toLowerCase().includes(searchQuery.toLowerCase()) ) const filteredGroups = groups.filter(g => g.groupname.toLowerCase().includes(searchQuery.toLowerCase()) ) return (
{/* Page Header */}

Identities

Manage users, groups, and view login history

{/* Error Alert */} {error && (

{error}

)} {/* Tab Navigation */}
{/* Search Bar (for Users and Groups tabs) */} {(activeTab === "users" || activeTab === "groups") && (
setSearchQuery(e.target.value)} className="flex-1 px-3 py-2 border border-border rounded bg-background text-foreground text-sm" /> {searchQuery && ( )}
)} {/* USERS TAB */} {activeTab === "users" && (
{/* Sub-tabs for Linux vs Samba users */}
{/* LINUX USERS */} {usersSubTab === "linux" && (
{loading ? (
Loading users...
) : ( System Users
{filteredUsers.map((user) => ( ))}
Username UID Home Shell Groups Status Actions
{user.username} {user.uid} {user.home} {user.shell}
{user.groups.map((g) => ( {g} ))}
{user.locked ? ( Locked ) : ( Active )} {user.locked ? ( ) : ( )}
)}
)} {/* SAMBA USERS */} {usersSubTab === "samba" && (
{loading ? (
Loading Samba users...
) : sambaUsers.length === 0 ? ( No Samba users found. Install and configure Samba to see users here. ) : ( Samba Users
{filteredSambaUsers.map((user) => ( ))}
Username UID Comment
{user.username} {user.uid} {(user as any).comment || "—"}
)}
)}
)} {/* GROUPS TAB */} {activeTab === "groups" && (
{loading ? (
Loading groups...
) : ( System Groups
{filteredGroups.map((group) => ( ))}
Group Name GID Members Actions
{group.groupname} {group.gid}
{group.members && group.members.length > 0 ? ( group.members.map((m) => (
{m}
)) ) : ( (empty) )}
)}
)} {/* LOGIN HISTORY TAB */} {activeTab === "history" && (
{loading ? (
Loading login history...
) : ( Recent Logins {logins.length === 0 ? (
No login history found
) : (
{logins.map((login, idx) => ( ))}
User Terminal Host/IP Login Time Logout Time Duration
{(login as any).username || (login as any).user} {(login as any).tty || (login as any).terminal || "—"} {(login as any).host || "—"} {(login as any).login_str || (login as any).login_time || "—"} {(login as any).logout_time || "—"} {(login as any).duration || "—"}
)}
)}
)}
{/* Create User Dialog */} setCreateUserDialog(false)} title="Create User">
setNewUsername(e.target.value)} className="w-full px-3 py-2 text-sm border border-border rounded bg-background text-foreground" autoFocus /> setNewHomeDir(e.target.value)} className="w-full px-3 py-2 text-sm border border-border rounded bg-background text-foreground" /> setNewGecos(e.target.value)} className="w-full px-3 py-2 text-sm border border-border rounded bg-background text-foreground" />
{/* Create Group Dialog */} setCreateGroupDialog(false)} title="Create Group">
setNewGroupName(e.target.value)} className="w-full px-3 py-2 text-sm border border-border rounded bg-background text-foreground" autoFocus />
{/* Change Password Dialog */} setPasswordDialog(false)} title={`Change Password for ${selectedUser}`}>
setNewPassword(e.target.value)} className="w-full px-3 py-2 text-sm border border-border rounded bg-background text-foreground" autoFocus />
{/* Change Shell Dialog */} setShellDialog(false)} title={`Change Shell for ${selectedUser}`}>
{/* Set Samba Password Dialog */} setSambaPasswordDialog(false)} title={`Set Samba Password for ${selectedUser}`}>
setSambaPassword(e.target.value)} className="w-full px-3 py-2 text-sm border border-border rounded bg-background text-foreground" autoFocus />
{/* Delete User Dialog */} setDeleteUserDialog(false)} title="Delete User">

Are you sure you want to delete user {selectedUser}?

{/* Delete Group Dialog */} setDeleteGroupDialog(false)} title="Delete Group">

Are you sure you want to delete group {selectedGroup}?

{/* Add User to Group Dialog */} setAddUserToGroupDialog(false)} title="Add User to Group">
) }