From da1f4e365ac6a3b46defc15fc94127ae0cb1c338 Mon Sep 17 00:00:00 2001 From: Felipe Cardoso Date: Thu, 6 Nov 2025 20:01:46 +0100 Subject: [PATCH] Add admin session management functionality via new API integration - Implemented `adminListSessions` function to fetch paginated session data for admin monitoring. - Updated `useAdmin` hook to include session statistics and new API call. - Enhanced `DashboardStats` to display total session count. - Added types for `/api/v1/admin/sessions` endpoint responses, errors, and request parameters. --- frontend/src/lib/api/generated/sdk.gen.ts | 24 +++- frontend/src/lib/api/generated/types.gen.ts | 126 ++++++++++++++++++++ frontend/src/lib/api/hooks/useAdmin.tsx | 75 ++++++------ 3 files changed, 188 insertions(+), 37 deletions(-) diff --git a/frontend/src/lib/api/generated/sdk.gen.ts b/frontend/src/lib/api/generated/sdk.gen.ts index ce0e472..81508fa 100644 --- a/frontend/src/lib/api/generated/sdk.gen.ts +++ b/frontend/src/lib/api/generated/sdk.gen.ts @@ -3,7 +3,7 @@ import { type Client, type Options as Options2, type TDataShape, urlSearchParamsBodySerializer } from './client'; import { client } from './client.gen'; -import type { AdminActivateUserData, AdminActivateUserErrors, AdminActivateUserResponses, AdminAddOrganizationMemberData, AdminAddOrganizationMemberErrors, AdminAddOrganizationMemberResponses, AdminBulkUserActionData, AdminBulkUserActionErrors, AdminBulkUserActionResponses, AdminCreateOrganizationData, AdminCreateOrganizationErrors, AdminCreateOrganizationResponses, AdminCreateUserData, AdminCreateUserErrors, AdminCreateUserResponses, AdminDeactivateUserData, AdminDeactivateUserErrors, AdminDeactivateUserResponses, AdminDeleteOrganizationData, AdminDeleteOrganizationErrors, AdminDeleteOrganizationResponses, AdminDeleteUserData, AdminDeleteUserErrors, AdminDeleteUserResponses, AdminGetOrganizationData, AdminGetOrganizationErrors, AdminGetOrganizationResponses, AdminGetUserData, AdminGetUserErrors, AdminGetUserResponses, AdminListOrganizationMembersData, AdminListOrganizationMembersErrors, AdminListOrganizationMembersResponses, AdminListOrganizationsData, AdminListOrganizationsErrors, AdminListOrganizationsResponses, AdminListUsersData, AdminListUsersErrors, AdminListUsersResponses, AdminRemoveOrganizationMemberData, AdminRemoveOrganizationMemberErrors, AdminRemoveOrganizationMemberResponses, AdminUpdateOrganizationData, AdminUpdateOrganizationErrors, AdminUpdateOrganizationResponses, AdminUpdateUserData, AdminUpdateUserErrors, AdminUpdateUserResponses, ChangeCurrentUserPasswordData, ChangeCurrentUserPasswordErrors, ChangeCurrentUserPasswordResponses, CleanupExpiredSessionsData, CleanupExpiredSessionsResponses, ConfirmPasswordResetData, ConfirmPasswordResetErrors, ConfirmPasswordResetResponses, DeleteUserData, DeleteUserErrors, DeleteUserResponses, GetCurrentUserProfileData, GetCurrentUserProfileResponses, GetMyOrganizationsData, GetMyOrganizationsErrors, GetMyOrganizationsResponses, GetOrganizationData, GetOrganizationErrors, GetOrganizationMembersData, GetOrganizationMembersErrors, GetOrganizationMembersResponses, GetOrganizationResponses, GetUserByIdData, GetUserByIdErrors, GetUserByIdResponses, HealthCheckData, HealthCheckResponses, ListMySessionsData, ListMySessionsResponses, ListUsersData, ListUsersErrors, ListUsersResponses, LoginData, LoginErrors, LoginOauthData, LoginOauthErrors, LoginOauthResponses, LoginResponses, LogoutAllData, LogoutAllResponses, LogoutData, LogoutErrors, LogoutResponses, RefreshTokenData, RefreshTokenErrors, RefreshTokenResponses, RegisterData, RegisterErrors, RegisterResponses, RequestPasswordResetData, RequestPasswordResetErrors, RequestPasswordResetResponses, RevokeSessionData, RevokeSessionErrors, RevokeSessionResponses, RootGetData, RootGetResponses, UpdateCurrentUserData, UpdateCurrentUserErrors, UpdateCurrentUserResponses, UpdateOrganizationData, UpdateOrganizationErrors, UpdateOrganizationResponses, UpdateUserData, UpdateUserErrors, UpdateUserResponses } from './types.gen'; +import type { AdminActivateUserData, AdminActivateUserErrors, AdminActivateUserResponses, AdminAddOrganizationMemberData, AdminAddOrganizationMemberErrors, AdminAddOrganizationMemberResponses, AdminBulkUserActionData, AdminBulkUserActionErrors, AdminBulkUserActionResponses, AdminCreateOrganizationData, AdminCreateOrganizationErrors, AdminCreateOrganizationResponses, AdminCreateUserData, AdminCreateUserErrors, AdminCreateUserResponses, AdminDeactivateUserData, AdminDeactivateUserErrors, AdminDeactivateUserResponses, AdminDeleteOrganizationData, AdminDeleteOrganizationErrors, AdminDeleteOrganizationResponses, AdminDeleteUserData, AdminDeleteUserErrors, AdminDeleteUserResponses, AdminGetOrganizationData, AdminGetOrganizationErrors, AdminGetOrganizationResponses, AdminGetUserData, AdminGetUserErrors, AdminGetUserResponses, AdminListOrganizationMembersData, AdminListOrganizationMembersErrors, AdminListOrganizationMembersResponses, AdminListOrganizationsData, AdminListOrganizationsErrors, AdminListOrganizationsResponses, AdminListSessionsData, AdminListSessionsErrors, AdminListSessionsResponses, AdminListUsersData, AdminListUsersErrors, AdminListUsersResponses, AdminRemoveOrganizationMemberData, AdminRemoveOrganizationMemberErrors, AdminRemoveOrganizationMemberResponses, AdminUpdateOrganizationData, AdminUpdateOrganizationErrors, AdminUpdateOrganizationResponses, AdminUpdateUserData, AdminUpdateUserErrors, AdminUpdateUserResponses, ChangeCurrentUserPasswordData, ChangeCurrentUserPasswordErrors, ChangeCurrentUserPasswordResponses, CleanupExpiredSessionsData, CleanupExpiredSessionsResponses, ConfirmPasswordResetData, ConfirmPasswordResetErrors, ConfirmPasswordResetResponses, DeleteUserData, DeleteUserErrors, DeleteUserResponses, GetCurrentUserProfileData, GetCurrentUserProfileResponses, GetMyOrganizationsData, GetMyOrganizationsErrors, GetMyOrganizationsResponses, GetOrganizationData, GetOrganizationErrors, GetOrganizationMembersData, GetOrganizationMembersErrors, GetOrganizationMembersResponses, GetOrganizationResponses, GetUserByIdData, GetUserByIdErrors, GetUserByIdResponses, HealthCheckData, HealthCheckResponses, ListMySessionsData, ListMySessionsResponses, ListUsersData, ListUsersErrors, ListUsersResponses, LoginData, LoginErrors, LoginOauthData, LoginOauthErrors, LoginOauthResponses, LoginResponses, LogoutAllData, LogoutAllResponses, LogoutData, LogoutErrors, LogoutResponses, RefreshTokenData, RefreshTokenErrors, RefreshTokenResponses, RegisterData, RegisterErrors, RegisterResponses, RequestPasswordResetData, RequestPasswordResetErrors, RequestPasswordResetResponses, RevokeSessionData, RevokeSessionErrors, RevokeSessionResponses, RootGetData, RootGetResponses, UpdateCurrentUserData, UpdateCurrentUserErrors, UpdateCurrentUserResponses, UpdateOrganizationData, UpdateOrganizationErrors, UpdateOrganizationResponses, UpdateUserData, UpdateUserErrors, UpdateUserResponses } from './types.gen'; export type Options = Options2 & { /** @@ -812,6 +812,28 @@ export const adminRemoveOrganizationMember = (options?: Options) => { + return (options?.client ?? client).get({ + responseType: 'json', + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/admin/sessions', + ...options + }); +}; + /** * Get My Organizations * diff --git a/frontend/src/lib/api/generated/types.gen.ts b/frontend/src/lib/api/generated/types.gen.ts index 3a2bc46..19d77a3 100644 --- a/frontend/src/lib/api/generated/types.gen.ts +++ b/frontend/src/lib/api/generated/types.gen.ts @@ -23,6 +23,76 @@ export type AddMemberRequest = { role?: OrganizationRole; }; +/** + * AdminSessionResponse + * + * Schema for session responses in admin panel. + * + * Includes user information for admin to see who owns each session. + */ +export type AdminSessionResponse = { + /** + * Device Name + * + * Friendly device name + */ + device_name?: string | null; + /** + * Device Id + * + * Persistent device identifier + */ + device_id?: string | null; + /** + * Id + */ + id: string; + /** + * User Id + */ + user_id: string; + /** + * User Email + * + * Email of the user who owns this session + */ + user_email: string; + /** + * User Full Name + * + * Full name of the user + */ + user_full_name?: string | null; + /** + * Ip Address + */ + ip_address?: string | null; + /** + * Location City + */ + location_city?: string | null; + /** + * Location Country + */ + location_country?: string | null; + /** + * Last Used At + */ + last_used_at: string; + /** + * Created At + */ + created_at: string; + /** + * Expires At + */ + expires_at: string; + /** + * Is Active + */ + is_active: boolean; +}; + /** * Body_login_oauth */ @@ -312,6 +382,22 @@ export type OrganizationUpdate = { } | null; }; +/** + * PaginatedResponse[AdminSessionResponse] + */ +export type PaginatedResponseAdminSessionResponse = { + /** + * Data + * + * List of items + */ + data: Array; + /** + * Pagination metadata + */ + pagination: PaginationMeta; +}; + /** * PaginatedResponse[OrganizationMemberResponse] */ @@ -1711,6 +1797,46 @@ export type AdminRemoveOrganizationMemberResponses = { export type AdminRemoveOrganizationMemberResponse = AdminRemoveOrganizationMemberResponses[keyof AdminRemoveOrganizationMemberResponses]; +export type AdminListSessionsData = { + body?: never; + path?: never; + query?: { + /** + * Is Active + * + * Filter by active status + */ + is_active?: boolean | null; + /** + * Page + */ + page?: number; + /** + * Limit + */ + limit?: number; + }; + url: '/api/v1/admin/sessions'; +}; + +export type AdminListSessionsErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type AdminListSessionsError = AdminListSessionsErrors[keyof AdminListSessionsErrors]; + +export type AdminListSessionsResponses = { + /** + * Successful Response + */ + 200: PaginatedResponseAdminSessionResponse; +}; + +export type AdminListSessionsResponse = AdminListSessionsResponses[keyof AdminListSessionsResponses]; + export type GetMyOrganizationsData = { body?: never; path?: never; diff --git a/frontend/src/lib/api/hooks/useAdmin.tsx b/frontend/src/lib/api/hooks/useAdmin.tsx index ece5cc3..d3b2c7d 100644 --- a/frontend/src/lib/api/hooks/useAdmin.tsx +++ b/frontend/src/lib/api/hooks/useAdmin.tsx @@ -11,31 +11,31 @@ 'use client'; -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import { - adminListUsers, - adminListOrganizations, - adminCreateUser, - adminGetUser, - adminUpdateUser, - adminDeleteUser, - adminActivateUser, - adminDeactivateUser, - adminBulkUserAction, - adminCreateOrganization, - adminUpdateOrganization, - adminDeleteOrganization, - adminGetOrganization, - adminListOrganizationMembers, - adminAddOrganizationMember, - adminRemoveOrganizationMember, - type UserCreate, - type UserUpdate, - type OrganizationCreate, - type OrganizationUpdate, - type AddMemberRequest, + type AddMemberRequest, + adminActivateUser, + adminAddOrganizationMember, + adminBulkUserAction, + adminCreateOrganization, + adminCreateUser, + adminDeactivateUser, + adminDeleteOrganization, + adminDeleteUser, + adminGetOrganization, + adminListOrganizationMembers, + adminListOrganizations, + adminListSessions, + adminListUsers, + adminRemoveOrganizationMember, + adminUpdateOrganization, + adminUpdateUser, + type OrganizationCreate, + type OrganizationUpdate, + type UserCreate, + type UserUpdate, } from '@/lib/api/client'; -import { useAuth } from '@/lib/auth/AuthContext'; +import {useAuth} from '@/lib/auth/AuthContext'; /** * Constants for admin hooks @@ -51,7 +51,7 @@ export interface AdminStats { totalUsers: number; activeUsers: number; totalOrganizations: number; - totalSessions: number; // TODO: Requires admin sessions endpoint + totalSessions: number; } /** @@ -103,19 +103,22 @@ export function useAdminStats() { const orgsData = (orgsResponse as { data: { pagination: { total: number } } }).data; const totalOrganizations = orgsData?.pagination?.total || 0; - // TODO: Add admin sessions endpoint - // Currently no admin-level endpoint exists to fetch all sessions - // across all users. The /api/v1/sessions/me endpoint only returns - // sessions for the current user. - // - // Once backend implements /api/v1/admin/sessions, uncomment below: - // const sessionsResponse = await adminListSessions({ - // query: { page: 1, limit: 10000 }, - // throwOnError: false, - // }); - // const totalSessions = sessionsResponse.data?.pagination?.total || 0; + // Fetch sessions list + const sessionsResponse = await adminListSessions({ + query: { + page: 1, + limit: STATS_FETCH_LIMIT, + }, + throwOnError: false, + }); - const totalSessions = 0; // Placeholder until admin sessions endpoint exists + if ('error' in sessionsResponse) { + throw new Error('Failed to fetch sessions'); + } + + // Type assertion: if no error, response has data + const sessionsData = (sessionsResponse as { data: { pagination: { total: number } } }).data; + const totalSessions = sessionsData?.pagination?.total || 0; return { totalUsers,