Files
emaui/libs/auth/src/lib/hooks/useSessions.ts

66 lines
2.2 KiB
TypeScript

import { useApiLazyQuery, useApiMutation, useApiQuery } from '@ema-platform/api';
export interface MySession {
id: string;
createdAt: string;
email: string;
/** IP address the session was created from — IAM sends no user agent. */
device: string;
expiryTime: string;
refreshCount: number;
status: string;
}
/** `/sessions/my-sessions` answers with a tuple, not the usual `{items, count}`. */
type SessionsResponse = [MySession[], number];
const SESSIONS_URL = '/sessions/my-sessions';
const ORDER_BY = 'CreatedAt:DESC';
function unwrapList(data: unknown): SessionsResponse {
if (!Array.isArray(data)) return [[], 0];
const [items, total] = data as Partial<SessionsResponse>;
return [items ?? [], total ?? 0];
}
/**
* The signed-in user's login sessions, and the two ways to end them.
*
* Uses the generic query/mutation endpoints rather than its own slice, so
* freshness comes from `refetch()` rather than cache tags — the same shape as
* `useTwoFactor`.
*/
export function useSessions({ skip, take }: { skip: number; take: number }) {
const { data, isFetching, refetch } = useApiQuery<SessionsResponse>({
url: SESSIONS_URL,
params: { skip, take, orderBy: ORDER_BY },
});
const [fetchAll] = useApiLazyQuery<SessionsResponse>();
const [send, { isLoading: isRevoking }] = useApiMutation();
const [sessions, total] = unwrapList(data);
/** Every session id the user has, not just the ones on the current page. */
const allSessionIds = async (): Promise<string[]> => {
// `total` is one page stale at worst; ask for a page big enough to cover it
// growing between render and click.
const result = await fetchAll({
url: SESSIONS_URL,
params: { skip: 0, take: Math.max(total, sessions.length) + 20, orderBy: ORDER_BY },
}).unwrap();
return unwrapList(result)[0].map((s) => s.id);
};
const revoke = async (ids: string[]) => {
if (ids.length === 0) return;
await send(
ids.length === 1
? { url: `/sessions/revoke/${ids[0]}`, method: 'DELETE' }
: { url: '/sessions/bulk-revoke', method: 'POST', body: { sessionIds: ids } },
).unwrap();
await refetch();
};
return { sessions, total, isFetching, refetch, revoke, isRevoking, allSessionIds };
}