api-tokens
This commit is contained in:
@@ -0,0 +1,196 @@
|
|||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
const useApiTokens = ({ api, notifyApiError, setStatusMessage, token }) => {
|
||||||
|
const [tokens, setTokens] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [creating, setCreating] = useState(false);
|
||||||
|
const [deletingId, setDeletingId] = useState(null);
|
||||||
|
const [regeneratingId, setRegeneratingId] = useState(null);
|
||||||
|
const [updatingId, setUpdatingId] = useState(null);
|
||||||
|
const [createdSecret, setCreatedSecret] = useState(null);
|
||||||
|
|
||||||
|
const refresh = useCallback(async () => {
|
||||||
|
if (!token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const { data } = await api.get('/profile/api-tokens');
|
||||||
|
setTokens(Array.isArray(data) ? data : []);
|
||||||
|
} catch (error) {
|
||||||
|
notifyApiError?.(error, 'Failed to load API tokens.');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [api, notifyApiError, token]);
|
||||||
|
|
||||||
|
const create = useCallback(
|
||||||
|
async ({ label, expires_at, capabilities } = {}) => {
|
||||||
|
if (creating) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setCreating(true);
|
||||||
|
try {
|
||||||
|
const payload = {};
|
||||||
|
if (label) {
|
||||||
|
payload.label = label;
|
||||||
|
}
|
||||||
|
if (expires_at) {
|
||||||
|
payload.expires_at = expires_at;
|
||||||
|
}
|
||||||
|
if (Array.isArray(capabilities) && capabilities.length > 0) {
|
||||||
|
payload.capabilities = capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await api.post('/profile/api-tokens', payload);
|
||||||
|
if (data?.token_info) {
|
||||||
|
setTokens((previous) => {
|
||||||
|
const filtered = previous.filter((entry) => entry.id !== data.token_info.id);
|
||||||
|
return [data.token_info, ...filtered];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data?.token) {
|
||||||
|
setCreatedSecret(data.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatusMessage?.('API token created.', 'success');
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
notifyApiError?.(error, 'Failed to create API token.');
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setCreating(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[api, creating, notifyApiError, refresh, setStatusMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const revoke = useCallback(
|
||||||
|
async (tokenId) => {
|
||||||
|
if (!tokenId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setDeletingId(tokenId);
|
||||||
|
try {
|
||||||
|
await api.delete(`/profile/api-tokens/${tokenId}`);
|
||||||
|
await refresh();
|
||||||
|
setStatusMessage?.('API token revoked.', 'success');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
notifyApiError?.(error, 'Failed to revoke API token.');
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setDeletingId(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[api, notifyApiError, refresh, setStatusMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const regenerate = useCallback(
|
||||||
|
async (tokenId) => {
|
||||||
|
if (!tokenId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setRegeneratingId(tokenId);
|
||||||
|
try {
|
||||||
|
const { data } = await api.post(`/profile/api-tokens/${tokenId}/regenerate`);
|
||||||
|
if (data?.token_info) {
|
||||||
|
setTokens((previous) => {
|
||||||
|
let found = false;
|
||||||
|
const next = previous.map((entry) => {
|
||||||
|
if (entry.id === data.token_info.id) {
|
||||||
|
found = true;
|
||||||
|
return data.token_info;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
});
|
||||||
|
if (!found) {
|
||||||
|
return [data.token_info, ...previous];
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data?.token) {
|
||||||
|
setCreatedSecret(data.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatusMessage?.('API token regenerated.', 'success');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
notifyApiError?.(error, 'Failed to regenerate API token.');
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setRegeneratingId(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[api, notifyApiError, refresh, setStatusMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateCapabilities = useCallback(
|
||||||
|
async (tokenId, capabilities) => {
|
||||||
|
if (!tokenId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setUpdatingId(tokenId);
|
||||||
|
try {
|
||||||
|
const { data } = await api.patch(`/profile/api-tokens/${tokenId}`, {
|
||||||
|
capabilities,
|
||||||
|
});
|
||||||
|
if (data) {
|
||||||
|
setTokens((previous) => {
|
||||||
|
let found = false;
|
||||||
|
const next = previous.map((entry) => {
|
||||||
|
if (entry.id === data.id) {
|
||||||
|
found = true;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
});
|
||||||
|
if (!found) {
|
||||||
|
return [data, ...previous];
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
|
setStatusMessage?.('API token updated.', 'success');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
notifyApiError?.(error, 'Failed to update API token.');
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setUpdatingId(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[api, notifyApiError, refresh, setStatusMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
const dismissSecret = useCallback(() => {
|
||||||
|
setCreatedSecret(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tokens,
|
||||||
|
loading,
|
||||||
|
creating,
|
||||||
|
deletingId,
|
||||||
|
regeneratingId,
|
||||||
|
updatingId,
|
||||||
|
createdSecret,
|
||||||
|
refresh,
|
||||||
|
create,
|
||||||
|
revoke,
|
||||||
|
regenerate,
|
||||||
|
updateCapabilities,
|
||||||
|
dismissSecret,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useApiTokens;
|
||||||
Reference in New Issue
Block a user