//=========================================== // THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template //=========================================== import { fromNow } from "@hexclave/shared/dist/utils/dates"; import { captureError } from "@hexclave/shared/dist/utils/errors"; import { runAsynchronously } from "@hexclave/shared/dist/utils/promises"; import { ActionCell, Badge, Button, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from "@hexclave/ui"; import { useEffect, useState } from "react"; import { useUser } from "../../../lib/hooks"; import { ActiveSession } from "../../../lib/hexclave-app/users"; import { useTranslation } from "../../../lib/translations"; import { PageLayout } from "../page-layout"; export function ActiveSessionsPage(props?: { mockSessions?: Array<{ id: string, isCurrentSession: boolean, isImpersonation?: boolean, createdAt: string, lastUsedAt?: string, geoInfo?: { ip?: string, cityName?: string, }, }>, mockMode?: boolean, }) { const { t } = useTranslation(); const userFromHook = useUser({ or: (props?.mockSessions || props?.mockMode) ? 'return-null' : 'throw' }); const [isLoading, setIsLoading] = useState(!props?.mockSessions); const [isRevokingAll, setIsRevokingAll] = useState(false); const [sessions, setSessions] = useState([]); const [showConfirmRevokeAll, setShowConfirmRevokeAll] = useState(false); // Use mock data if provided const mockSessionsData = props?.mockSessions ? props.mockSessions.map(session => ({ id: session.id, isCurrentSession: session.isCurrentSession, isImpersonation: session.isImpersonation || false, createdAt: session.createdAt, lastUsedAt: session.lastUsedAt, geoInfo: session.geoInfo, })) : [ { id: 'current-session', isCurrentSession: true, createdAt: new Date().toISOString(), lastUsedAt: new Date().toISOString(), geoInfo: { ip: '192.168.1.1', cityName: 'San Francisco' } }, { id: 'mobile-session', isCurrentSession: false, createdAt: new Date(Date.now() - 86400000).toISOString(), // 1 day ago lastUsedAt: new Date(Date.now() - 7200000).toISOString(), // 2 hours ago geoInfo: { ip: '10.0.0.1', cityName: 'New York' } } ]; // Fetch sessions when component mounts (only if not using mock data) useEffect(() => { if (props?.mockSessions) { setSessions(mockSessionsData as any); setIsLoading(false); return; } // If in mock mode but no mock sessions provided, use default mock data if (props?.mockMode && !userFromHook) { setSessions(mockSessionsData as any); setIsLoading(false); return; } if (!userFromHook) return; runAsynchronously(async () => { setIsLoading(true); const sessionsData = await userFromHook.getActiveSessions(); const enhancedSessions = sessionsData; setSessions(enhancedSessions); setIsLoading(false); }); }, [userFromHook, props?.mockSessions]); const handleRevokeSession = async (sessionId: string) => { if (props?.mockSessions) { // Mock revoke - just remove from list setSessions(prev => prev.filter(session => session.id !== sessionId)); return; } if (!userFromHook) return; try { await userFromHook.revokeSession(sessionId); setSessions(prev => prev.filter(session => session.id !== sessionId)); } catch (error) { captureError("session-revoke", { sessionId ,error }); throw error; } }; const handleRevokeAllSessions = async () => { setIsRevokingAll(true); try { if (props?.mockSessions) { // Mock revoke all - just keep current session setSessions(prevSessions => prevSessions.filter(session => session.isCurrentSession)); } else if (userFromHook) { const deletionPromises = sessions .filter(session => !session.isCurrentSession) .map(session => userFromHook.revokeSession(session.id)); await Promise.all(deletionPromises); setSessions(prevSessions => prevSessions.filter(session => session.isCurrentSession)); } } catch (error) { captureError("all-sessions-revoke", { error, sessionIds: sessions.map(session => session.id) }); throw error; } finally { setIsRevokingAll(false); setShowConfirmRevokeAll(false); } }; return (
{t("Active Sessions")} {sessions.filter(s => !s.isCurrentSession).length > 0 && !isLoading && ( showConfirmRevokeAll ? (
) : ( ) )}
{t("These are devices where you're currently logged in. You can revoke access to end a session.")} {isLoading ? ( ) : (
{t("Session")} {t("IP Address")} {t("Location")} {t("Last used")} {sessions.length === 0 ? ( {t("No active sessions found")} ) : ( sessions.map((session) => (
{/* We currently do not save any usefull information about the user, in the future, the name should probably say what kind of session it is (e.g. cli, browser, maybe what auth method was used) */} {session.isCurrentSession ? t("Current Session") : t("Other Session")} {session.isImpersonation && {t("Impersonation")}} {t("Signed in {time}", { time: new Date(session.createdAt).toLocaleDateString() })}
{session.geoInfo?.ip || t('-')} {session.geoInfo?.cityName || t('Unknown')}
{session.lastUsedAt ? fromNow(new Date(session.lastUsedAt)) : t("Never")} {session.lastUsedAt ? new Date(session.lastUsedAt).toLocaleDateString() : ""}
handleRevokeSession(session.id), danger: true, disabled: session.isCurrentSession, disabledTooltip: session.isCurrentSession ? t("You cannot revoke your current session") : undefined, }, ]} />
)) )}
)}
); }