apiClient
This commit is contained in:
@@ -1,14 +1,11 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
interface ApiTokenRecord {
|
||||
id?: string | number;
|
||||
label?: string;
|
||||
expires_at?: string;
|
||||
created_at?: string;
|
||||
last_used_at?: string;
|
||||
capability_set_id?: string | number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
import {
|
||||
createApiToken,
|
||||
deleteApiToken,
|
||||
listApiTokens,
|
||||
regenerateApiToken,
|
||||
type ApiTokenRecord,
|
||||
} from '../lib/apiClient';
|
||||
|
||||
interface ApiTokensResponse {
|
||||
token_info?: ApiTokenRecord;
|
||||
@@ -22,11 +19,6 @@ interface CreateTokenArgs {
|
||||
}
|
||||
|
||||
interface UseApiTokensArgs {
|
||||
api: {
|
||||
get: <T = unknown>(url: string) => Promise<{ data: T }>;
|
||||
post: <T = unknown>(url: string, payload?: unknown) => Promise<{ data: T }>;
|
||||
delete: (url: string) => Promise<unknown>;
|
||||
};
|
||||
notifyApiError?: (error: unknown, message: string) => void;
|
||||
setStatusMessage?: (message: string, variant?: string) => void;
|
||||
token?: string | null;
|
||||
@@ -46,7 +38,7 @@ interface UseApiTokensResult {
|
||||
dismissSecret: () => void;
|
||||
}
|
||||
|
||||
const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTokensArgs): UseApiTokensResult => {
|
||||
const useApiTokens = ({ notifyApiError, setStatusMessage, token }: UseApiTokensArgs): UseApiTokensResult => {
|
||||
const [tokens, setTokens] = useState<ApiTokenRecord[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [creating, setCreating] = useState(false);
|
||||
@@ -60,14 +52,14 @@ const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTo
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
const { data } = await api.get<ApiTokenRecord[]>('/profile/api-tokens');
|
||||
const data = await listApiTokens();
|
||||
setTokens(Array.isArray(data) ? data : []);
|
||||
} catch (error) {
|
||||
notifyApiError?.(error, 'Failed to load API tokens.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [api, notifyApiError, token]);
|
||||
}, [notifyApiError, token]);
|
||||
|
||||
const create = useCallback(
|
||||
async ({ label, expires_at, capability_set_id }: CreateTokenArgs = {}) => {
|
||||
@@ -76,7 +68,7 @@ const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTo
|
||||
}
|
||||
setCreating(true);
|
||||
try {
|
||||
const payload: Record<string, unknown> = { capability_set_id };
|
||||
const payload: { capability_set_id: string | number; label?: string; expires_at?: string } = { capability_set_id };
|
||||
if (label) {
|
||||
payload.label = label;
|
||||
}
|
||||
@@ -84,7 +76,7 @@ const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTo
|
||||
payload.expires_at = expires_at;
|
||||
}
|
||||
|
||||
const { data } = await api.post<ApiTokensResponse>('/profile/api-tokens', payload);
|
||||
const data = await createApiToken(payload) as ApiTokensResponse;
|
||||
if (data?.token_info) {
|
||||
setTokens((previous) => {
|
||||
const filtered = previous.filter((entry) => entry.id !== data.token_info?.id);
|
||||
@@ -107,7 +99,7 @@ const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTo
|
||||
setCreating(false);
|
||||
}
|
||||
},
|
||||
[api, creating, notifyApiError, refresh, setStatusMessage],
|
||||
[creating, notifyApiError, refresh, setStatusMessage],
|
||||
);
|
||||
|
||||
const revoke = useCallback(
|
||||
@@ -117,7 +109,7 @@ const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTo
|
||||
}
|
||||
setDeletingId(tokenId);
|
||||
try {
|
||||
await api.delete(`/profile/api-tokens/${tokenId}`);
|
||||
await deleteApiToken(tokenId);
|
||||
await refresh();
|
||||
setStatusMessage?.('API token revoked.', 'success');
|
||||
return true;
|
||||
@@ -128,7 +120,7 @@ const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTo
|
||||
setDeletingId(null);
|
||||
}
|
||||
},
|
||||
[api, notifyApiError, refresh, setStatusMessage],
|
||||
[notifyApiError, refresh, setStatusMessage],
|
||||
);
|
||||
|
||||
const regenerate = useCallback(
|
||||
@@ -138,7 +130,7 @@ const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTo
|
||||
}
|
||||
setRegeneratingId(tokenId);
|
||||
try {
|
||||
const { data } = await api.post<ApiTokensResponse>(`/profile/api-tokens/${tokenId}/regenerate`);
|
||||
const data = await regenerateApiToken(tokenId) as ApiTokensResponse;
|
||||
if (data?.token_info) {
|
||||
setTokens((previous) => {
|
||||
let found = false;
|
||||
@@ -171,7 +163,7 @@ const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }: UseApiTo
|
||||
setRegeneratingId(null);
|
||||
}
|
||||
},
|
||||
[api, notifyApiError, refresh, setStatusMessage],
|
||||
[notifyApiError, refresh, setStatusMessage],
|
||||
);
|
||||
|
||||
const dismissSecret = useCallback(() => {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { listCapabilitySets } from '../lib/apiClient';
|
||||
import {
|
||||
createCapabilitySet as createCapabilitySetRequest,
|
||||
deleteCapabilitySet as deleteCapabilitySetRequest,
|
||||
listCapabilitySets,
|
||||
updateCapabilitySet as updateCapabilitySetRequest,
|
||||
} from '../lib/apiClient';
|
||||
|
||||
type Identifier = string | number;
|
||||
|
||||
@@ -11,21 +16,13 @@ interface CapabilitySet {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface ApiClient {
|
||||
get<T = CapabilitySet[]>(path: string): Promise<{ data: T }>;
|
||||
post<T = CapabilitySet>(path: string, payload?: unknown): Promise<{ data: T }>;
|
||||
patch<T = CapabilitySet>(path: string, payload?: unknown): Promise<{ data: T }>;
|
||||
delete: (path: string) => Promise<void>;
|
||||
}
|
||||
|
||||
interface UseCapabilitySetsOptions {
|
||||
api: ApiClient;
|
||||
notifyApiError?: (error: unknown, message: string) => void;
|
||||
setStatusMessage?: (message: string, level?: string) => void;
|
||||
token?: string | null;
|
||||
}
|
||||
|
||||
const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }: UseCapabilitySetsOptions) => {
|
||||
const useCapabilitySets = ({ notifyApiError, setStatusMessage, token }: UseCapabilitySetsOptions) => {
|
||||
const [capabilitySets, setCapabilitySets] = useState<CapabilitySet[]>([]);
|
||||
const [capabilitySetsLoading, setCapabilitySetsLoading] = useState(false);
|
||||
const [creatingCapabilitySet, setCreatingCapabilitySet] = useState(false);
|
||||
@@ -92,7 +89,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }: Use
|
||||
payload.label = trimmedLabel;
|
||||
}
|
||||
|
||||
const { data } = await api.post<CapabilitySet>('/capability-sets', payload);
|
||||
const data = await createCapabilitySetRequest(payload);
|
||||
if (data) {
|
||||
applyCapabilitySets((previous) => {
|
||||
const next = previous.filter((entry) => entry?.id !== data.id);
|
||||
@@ -113,7 +110,6 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }: Use
|
||||
}
|
||||
},
|
||||
[
|
||||
api,
|
||||
applyCapabilitySets,
|
||||
creatingCapabilitySet,
|
||||
notifyApiError,
|
||||
@@ -154,7 +150,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }: Use
|
||||
payload.capabilities = capabilities;
|
||||
}
|
||||
|
||||
const { data } = await api.patch<CapabilitySet>(`/capability-sets/${capabilitySetId}`, payload);
|
||||
const data = await updateCapabilitySetRequest(capabilitySetId, payload);
|
||||
if (data) {
|
||||
applyCapabilitySets((previous) => {
|
||||
let found = false;
|
||||
@@ -184,7 +180,6 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }: Use
|
||||
}
|
||||
},
|
||||
[
|
||||
api,
|
||||
applyCapabilitySets,
|
||||
notifyApiError,
|
||||
refreshCapabilitySets,
|
||||
@@ -200,7 +195,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }: Use
|
||||
}
|
||||
setDeletingCapabilitySetId(capabilitySetId);
|
||||
try {
|
||||
await api.delete(`/capability-sets/${capabilitySetId}`);
|
||||
await deleteCapabilitySetRequest(capabilitySetId);
|
||||
applyCapabilitySets((previous) => previous.filter((entry) => entry?.id !== capabilitySetId));
|
||||
setStatusMessage?.('Capability set deleted.', 'success');
|
||||
return true;
|
||||
@@ -211,7 +206,7 @@ const useCapabilitySets = ({ api, notifyApiError, setStatusMessage, token }: Use
|
||||
setDeletingCapabilitySetId(null);
|
||||
}
|
||||
},
|
||||
[api, applyCapabilitySets, notifyApiError, setStatusMessage],
|
||||
[applyCapabilitySets, notifyApiError, setStatusMessage],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -6,12 +6,12 @@ import {
|
||||
preparePublicKeyCreationOptions,
|
||||
serializeRegistrationCredential,
|
||||
} from '../utils/webauthn';
|
||||
|
||||
type ApiClient = {
|
||||
get: (path: string) => Promise<{ data: unknown }>;
|
||||
post: (path: string, body?: unknown) => Promise<{ data: unknown }>;
|
||||
delete: (path: string) => Promise<{ data: unknown }>;
|
||||
};
|
||||
import {
|
||||
deletePasskey,
|
||||
finishPasskeyRegistration,
|
||||
listPasskeys,
|
||||
startPasskeyRegistration,
|
||||
} from '../lib/apiClient';
|
||||
|
||||
type StatusMessageFn = (message: string, variant?: string) => void;
|
||||
type NotifyApiErrorFn = (error: unknown, message: string) => void;
|
||||
@@ -69,7 +69,6 @@ export type RevokePasskeyResult =
|
||||
| { ok: false; reason: RevokePasskeyFailureReason; message?: string };
|
||||
|
||||
interface UsePasskeysArgs {
|
||||
api: ApiClient;
|
||||
notifyApiError: NotifyApiErrorFn;
|
||||
setStatusMessage: StatusMessageFn;
|
||||
token?: string | null;
|
||||
@@ -89,7 +88,7 @@ interface UsePasskeysResult {
|
||||
) => Promise<RevokePasskeyResult>;
|
||||
}
|
||||
|
||||
const usePasskeys = ({ api, notifyApiError, setStatusMessage, token }: UsePasskeysArgs): UsePasskeysResult => {
|
||||
const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArgs): UsePasskeysResult => {
|
||||
const [passkeys, setPasskeys] = useState<PasskeyRecord[]>([]);
|
||||
const [passkeysSupported, setPasskeysSupported] = useState<boolean | null>(null);
|
||||
const [passkeysLoading, setPasskeysLoading] = useState(false);
|
||||
@@ -102,8 +101,7 @@ const usePasskeys = ({ api, notifyApiError, setStatusMessage, token }: UsePasske
|
||||
}
|
||||
setPasskeysLoading(true);
|
||||
try {
|
||||
const { data } = await api.get('/profile/passkeys');
|
||||
const passkeyData = Array.isArray(data) ? (data as PasskeyRecord[]) : [];
|
||||
const passkeyData = await listPasskeys();
|
||||
setPasskeys(passkeyData);
|
||||
setPasskeysSupported(true);
|
||||
} catch (error) {
|
||||
@@ -117,7 +115,7 @@ const usePasskeys = ({ api, notifyApiError, setStatusMessage, token }: UsePasske
|
||||
} finally {
|
||||
setPasskeysLoading(false);
|
||||
}
|
||||
}, [api, notifyApiError, token]);
|
||||
}, [notifyApiError, token]);
|
||||
|
||||
const registerPasskey = useCallback(
|
||||
async ({ nickname }: { nickname?: string } = {}): Promise<RegisterPasskeyResult> => {
|
||||
@@ -132,7 +130,7 @@ const usePasskeys = ({ api, notifyApiError, setStatusMessage, token }: UsePasske
|
||||
|
||||
setRegisteringPasskey(true);
|
||||
try {
|
||||
const { data } = await api.post('/auth/passkeys/register/start', {});
|
||||
const data = await startPasskeyRegistration();
|
||||
const challengeData = data as PasskeyChallengeResponse | undefined;
|
||||
const challengeId = challengeData?.challengeId || challengeData?.challenge_id;
|
||||
const publicKeyOptions =
|
||||
@@ -164,7 +162,7 @@ const usePasskeys = ({ api, notifyApiError, setStatusMessage, token }: UsePasske
|
||||
payload.nickname = trimmedNickname;
|
||||
}
|
||||
|
||||
await api.post('/auth/passkeys/register/finish', payload);
|
||||
await finishPasskeyRegistration(payload);
|
||||
await refreshPasskeys();
|
||||
setPasskeysSupported(true);
|
||||
setStatusMessage('Passkey registered.', 'success');
|
||||
@@ -188,7 +186,7 @@ const usePasskeys = ({ api, notifyApiError, setStatusMessage, token }: UsePasske
|
||||
setRegisteringPasskey(false);
|
||||
}
|
||||
},
|
||||
[api, notifyApiError, refreshPasskeys, registeringPasskey, setStatusMessage],
|
||||
[notifyApiError, refreshPasskeys, registeringPasskey, setStatusMessage],
|
||||
);
|
||||
|
||||
const revokePasskey = useCallback(
|
||||
@@ -201,8 +199,7 @@ const usePasskeys = ({ api, notifyApiError, setStatusMessage, token }: UsePasske
|
||||
}
|
||||
setRevokingPasskeyId(passkeyId);
|
||||
try {
|
||||
const query = reason ? `?reason=${encodeURIComponent(reason)}` : '';
|
||||
await api.delete(`/profile/passkeys/${passkeyId}${query}`);
|
||||
await deletePasskey(passkeyId, { reason });
|
||||
await refreshPasskeys();
|
||||
setStatusMessage('Passkey revoked.', 'success');
|
||||
return { ok: true };
|
||||
@@ -214,7 +211,7 @@ const usePasskeys = ({ api, notifyApiError, setStatusMessage, token }: UsePasske
|
||||
setRevokingPasskeyId(null);
|
||||
}
|
||||
},
|
||||
[api, notifyApiError, refreshPasskeys, setStatusMessage],
|
||||
[notifyApiError, refreshPasskeys, setStatusMessage],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user