Commit 5f60fb91 authored by tom's avatar tom

api keys resource

parent 7a8a5c0b
...@@ -21,6 +21,9 @@ export const RESOURCES = { ...@@ -21,6 +21,9 @@ export const RESOURCES = {
private_tags_tx: { private_tags_tx: {
path: '/api/account/v1/user/tags/transaction/:id?', path: '/api/account/v1/user/tags/transaction/:id?',
}, },
api_keys: {
path: '/api/account/v1/user/api_keys/:id?',
},
// DEPRECATED // DEPRECATED
old_api: { old_api: {
......
import type { UseQueryOptions } from '@tanstack/react-query'; import type { UseQueryOptions } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import type { UserInfo, CustomAbis, PublicTags, AddressTags, TransactionTags } from 'types/api/account'; import type { UserInfo, CustomAbis, PublicTags, AddressTags, TransactionTags, ApiKeys } from 'types/api/account';
import type { CsrfData } from 'types/client/account'; import type { CsrfData } from 'types/client/account';
import type { RESOURCES, ResourceError } from './resources'; import type { RESOURCES, ResourceError } from './resources';
...@@ -30,4 +30,5 @@ export type ResourcePayload<Q extends keyof typeof RESOURCES> = ...@@ -30,4 +30,5 @@ export type ResourcePayload<Q extends keyof typeof RESOURCES> =
Q extends 'public_tags' ? PublicTags : Q extends 'public_tags' ? PublicTags :
Q extends 'private_tags_address' ? AddressTags : Q extends 'private_tags_address' ? AddressTags :
Q extends 'private_tags_tx' ? TransactionTags : Q extends 'private_tags_tx' ? TransactionTags :
Q extends 'api_keys' ? ApiKeys :
never; never;
...@@ -15,7 +15,7 @@ const handler = async(_req: NextApiRequest, res: NextApiResponse) => { ...@@ -15,7 +15,7 @@ const handler = async(_req: NextApiRequest, res: NextApiResponse) => {
_pickBy(_pick(_req, [ 'body', 'method' ]), Boolean), _pickBy(_pick(_req, [ 'body', 'method' ]), Boolean),
); );
// don't think that we have to proxy all headers, so pick only necessary ones // some data back sends to us as header 🤦‍♂️
[ 'x-bs-account-csrf' ].forEach((headerName) => { [ 'x-bs-account-csrf' ].forEach((headerName) => {
const headerValue = response.headers.get(headerName); const headerValue = response.headers.get(headerName);
headerValue && res.setHeader(headerName, headerValue); headerValue && res.setHeader(headerName, headerValue);
......
...@@ -12,11 +12,11 @@ import type { SubmitHandler, ControllerRenderProps } from 'react-hook-form'; ...@@ -12,11 +12,11 @@ import type { SubmitHandler, ControllerRenderProps } from 'react-hook-form';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import type { ApiKey, ApiKeys, ApiKeyErrors } from 'types/api/account'; import type { ApiKey, ApiKeys, ApiKeyErrors } from 'types/api/account';
import { QueryKeys } from 'types/client/accountQueries';
import type { ResourceErrorAccount } from 'lib/api/resources';
import { resourceKey } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/getErrorMessage'; import getErrorMessage from 'lib/getErrorMessage';
import type { ErrorType } from 'lib/hooks/useFetch';
import useFetch from 'lib/hooks/useFetch';
import InputPlaceholder from 'ui/shared/InputPlaceholder'; import InputPlaceholder from 'ui/shared/InputPlaceholder';
type Props = { type Props = {
...@@ -40,7 +40,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -40,7 +40,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
name: data?.name || '', name: data?.name || '',
}, },
}); });
const fetch = useFetch(); const apiFetch = useApiFetch();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const formBackgroundColor = useColorModeValue('white', 'gray.900'); const formBackgroundColor = useColorModeValue('white', 'gray.900');
...@@ -48,17 +48,20 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -48,17 +48,20 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
const body = { name: data.name }; const body = { name: data.name };
if (!data.token) { if (!data.token) {
return fetch('/node-api/account/api-keys', { method: 'POST', body }); return apiFetch('api_keys', { fetchParams: { method: 'POST', body } });
} }
return fetch(`/node-api/account/api-keys/${ data.token }`, { method: 'PUT', body }); return apiFetch('api_keys', {
pathParams: { id: data.token },
fetchParams: { method: 'PUT', body },
});
}; };
const mutation = useMutation(updateApiKey, { const mutation = useMutation(updateApiKey, {
onSuccess: async(data) => { onSuccess: async(data) => {
const response = data as unknown as ApiKey; const response = data as unknown as ApiKey;
queryClient.setQueryData([ QueryKeys.apiKeys ], (prevData: ApiKeys | undefined) => { queryClient.setQueryData([ resourceKey('api_keys') ], (prevData: ApiKeys | undefined) => {
const isExisting = prevData && prevData.some((item) => item.api_key === response.api_key); const isExisting = prevData && prevData.some((item) => item.api_key === response.api_key);
if (isExisting) { if (isExisting) {
...@@ -76,11 +79,12 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => { ...@@ -76,11 +79,12 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
onClose(); onClose();
}, },
onError: (e: ErrorType<ApiKeyErrors>) => { onError: (error: ResourceErrorAccount<ApiKeyErrors>) => {
if (e?.error?.name) { const errorMap = error.payload?.errors;
setError('name', { type: 'custom', message: getErrorMessage(e.error, 'name') }); if (errorMap?.name) {
} else if (e?.error?.identity_id) { setError('name', { type: 'custom', message: getErrorMessage(errorMap, 'name') });
setError('name', { type: 'custom', message: getErrorMessage(e.error, 'identity_id') }); } else if (errorMap?.identity_id) {
setError('name', { type: 'custom', message: getErrorMessage(errorMap, 'identity_id') });
} else { } else {
setAlertVisible(true); setAlertVisible(true);
} }
......
...@@ -3,9 +3,9 @@ import { useQueryClient } from '@tanstack/react-query'; ...@@ -3,9 +3,9 @@ import { useQueryClient } from '@tanstack/react-query';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { ApiKey, ApiKeys } from 'types/api/account'; import type { ApiKey, ApiKeys } from 'types/api/account';
import { QueryKeys } from 'types/client/accountQueries';
import useFetch from 'lib/hooks/useFetch'; import { resourceKey } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch';
import DeleteModal from 'ui/shared/DeleteModal'; import DeleteModal from 'ui/shared/DeleteModal';
type Props = { type Props = {
...@@ -16,14 +16,17 @@ type Props = { ...@@ -16,14 +16,17 @@ type Props = {
const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => { const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const fetch = useFetch(); const apiFetch = useApiFetch();
const mutationFn = useCallback(() => { const mutationFn = useCallback(() => {
return fetch(`/node-api/account/api-keys/${ data.api_key }`, { method: 'DELETE' }); return apiFetch('api_keys', {
}, [ data.api_key, fetch ]); pathParams: { id: data.api_key },
fetchParams: { method: 'DELETE' },
});
}, [ data.api_key, apiFetch ]);
const onSuccess = useCallback(async() => { const onSuccess = useCallback(async() => {
queryClient.setQueryData([ QueryKeys.apiKeys ], (prevData: ApiKeys | undefined) => { queryClient.setQueryData([ resourceKey('api_keys') ], (prevData: ApiKeys | undefined) => {
return prevData?.filter((item) => item.api_key !== data.api_key); return prevData?.filter((item) => item.api_key !== data.api_key);
}); });
}, [ data, queryClient ]); }, [ data, queryClient ]);
......
import { Box, Button, Stack, Link, Text, Skeleton, useDisclosure } from '@chakra-ui/react'; import { Box, Button, Stack, Link, Text, Skeleton, useDisclosure } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import type { ApiKey, ApiKeys } from 'types/api/account'; import type { ApiKey } from 'types/api/account';
import { QueryKeys } from 'types/client/accountQueries';
import useFetch from 'lib/hooks/useFetch'; import useApiQuery from 'lib/api/useApiQuery';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken'; import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
...@@ -26,13 +24,12 @@ const ApiKeysPage: React.FC = () => { ...@@ -26,13 +24,12 @@ const ApiKeysPage: React.FC = () => {
const apiKeyModalProps = useDisclosure(); const apiKeyModalProps = useDisclosure();
const deleteModalProps = useDisclosure(); const deleteModalProps = useDisclosure();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const fetch = useFetch();
useRedirectForInvalidAuthToken(); useRedirectForInvalidAuthToken();
const [ apiKeyModalData, setApiKeyModalData ] = useState<ApiKey>(); const [ apiKeyModalData, setApiKeyModalData ] = useState<ApiKey>();
const [ deleteModalData, setDeleteModalData ] = useState<ApiKey>(); const [ deleteModalData, setDeleteModalData ] = useState<ApiKey>();
const { data, isLoading, isError } = useQuery<unknown, unknown, ApiKeys>([ QueryKeys.apiKeys ], async() => await fetch('/node-api/account/api-keys')); const { data, isLoading, isError } = useApiQuery('api_keys');
const onEditClick = useCallback((data: ApiKey) => { const onEditClick = useCallback((data: ApiKey) => {
setApiKeyModalData(data); setApiKeyModalData(data);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment