Commit ea7e636d authored by tom's avatar tom

first two resources and hook

parent 67b6ddab
import appConfig from 'configs/app/config';
import { RESOURCES } from './resources';
export default function buildUrl(resource: keyof typeof RESOURCES, params?: Record<string, string>) {
const base = appConfig.host === 'localhost' ? appConfig.baseUrl : appConfig.api.endpoint;
const path = appConfig.host === 'localhost' ?
'/proxy' + appConfig.api.basePath + RESOURCES[resource].path :
appConfig.api.basePath + RESOURCES[resource].path;
const url = new URL(path, base);
params && Object.entries(params).forEach(([ key, value ]) => {
url.searchParams.append(key, value);
});
return url.toString();
}
import type { UserInfo } from 'types/api/account';
import type { CsrfData } from 'types/client/account';
export const RESOURCES = {
user_info: {
path: '/api/account/v1/user/info',
queryKey: 'user_info',
},
csrf: {
path: '/api/account/v1/get_csrf',
queryKey: 'csrf',
},
};
export const resourceKey = (x: keyof typeof RESOURCES) => x;
export type ResourcePayload<Q extends keyof typeof RESOURCES> =
Q extends 'user_info' ? UserInfo :
Q extends 'csrf' ? CsrfData : never;
export interface ResourceError {
error?: {
status?: number;
statusText?: string;
};
}
import type { UseQueryOptions } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import useFetch from 'lib/hooks/useFetch';
import buildUrl from './buildUrl';
import type { RESOURCES, ResourcePayload, ResourceError } from './resources';
export default function useApi<R extends keyof typeof RESOURCES>(
resource: R,
queryOptions?: Omit<UseQueryOptions<unknown, ResourceError, ResourcePayload<R>>, 'queryKey' | 'queryFn'>,
) {
const fetch = useFetch();
return useQuery<unknown, ResourceError, ResourcePayload<R>>([ resource ], async() => {
const url = buildUrl(resource);
return fetch(url, { credentials: 'include' });
}, queryOptions);
}
......@@ -4,6 +4,8 @@ import React from 'react';
import type { CsrfData } from 'types/client/account';
import { resourceKey, RESOURCES } from 'lib/api/resources';
export interface ErrorType<T> {
error?: T;
status: Response['status'];
......@@ -18,7 +20,7 @@ interface Params {
export default function useFetch() {
const queryClient = useQueryClient();
const { token } = queryClient.getQueryData<CsrfData>([ 'csrf' ]) || {};
const { token } = queryClient.getQueryData<CsrfData>([ resourceKey('csrf') ]) || {};
return React.useCallback(<Success, Error>(path: string, params?: Params): Promise<Success | ErrorType<Error>> => {
const reqParams = {
......@@ -29,6 +31,7 @@ export default function useFetch() {
};
return fetch(path, reqParams).then(response => {
if (!response.ok) {
const error = {
status: response.status,
......@@ -48,6 +51,10 @@ export default function useFetch() {
);
} else {
if (path.includes(RESOURCES.csrf.path)) {
return Promise.resolve({ token: response.headers.get('x-bs-account-csrf') } as Success);
}
return response.json() as Promise<Success>;
}
});
......
import { useQuery } from '@tanstack/react-query';
import type { UserInfo } from 'types/api/account';
import { QueryKeys } from 'types/client/queries';
import appConfig from 'configs/app/config';
// import * as cookies from 'lib/cookies';
import useFetch from 'lib/hooks/useFetch';
interface Error {
error?: {
status?: number;
statusText?: string;
};
}
import useApi from 'lib/api/useApi';
import * as cookies from 'lib/cookies';
export default function useFetchProfileInfo() {
const fetch = useFetch();
return useQuery<unknown, Error, UserInfo>([ QueryKeys.profile ], async() => {
const url = new URL(`/proxy/poa/core/api/account/v1/user/info`, appConfig.baseUrl);
return fetch(url.toString(), { credentials: 'include' });
}, {
return useApi('user_info', {
refetchOnMount: false,
// enabled: Boolean(cookies.get(cookies.NAMES.API_TOKEN)),
enabled: Boolean(cookies.get(cookies.NAMES.API_TOKEN)),
});
}
......@@ -23,7 +23,7 @@ function MyApp({ Component, pageProps }: AppProps) {
refetchOnWindowFocus: false,
retry: (failureCount, _error) => {
const error = _error as ErrorType<{ status: number }>;
const status = error?.error?.status;
const status = error?.status || error?.error?.status;
if (status && status >= 400 && status < 500) {
// don't do retry for client error responses
return false;
......
import type { NextApiRequest, NextApiResponse } from 'next';
import fetchFactory from 'lib/api/fetch';
import getUrlWithNetwork from 'lib/api/getUrlWithNetwork';
import { httpLogger } from 'lib/api/logger';
export default async function csrfHandler(_req: NextApiRequest, res: NextApiResponse) {
httpLogger(_req, res);
const url = getUrlWithNetwork(_req, `/api/account/v1/get_csrf`);
const fetch = fetchFactory(_req);
const response = await fetch(url);
if (response.status === 200) {
const token = response.headers.get('x-bs-account-csrf');
res.status(200).json({ token });
return;
}
const responseError = { statusText: response.statusText, status: response.status };
httpLogger.logger.error({ err: responseError, url: _req.url });
res.status(500).json(responseError);
}
import handler from 'lib/api/handler';
const profileHandler = handler(() => '/account/v1/user/info', [ 'GET' ]);
export default profileHandler;
......@@ -15,6 +15,12 @@ const handler = async(_req: NextApiRequest, res: NextApiResponse) => {
_pickBy(_pick(_req, [ 'body', 'method' ]), Boolean),
);
// don't think that we have to proxy all headers, so pick only necessary ones
[ 'x-bs-account-csrf' ].forEach((headerName) => {
const headerValue = response.headers.get(headerName);
headerValue && res.setHeader(headerName, headerValue);
});
res.status(response.status).send(response.body);
};
......
import { VStack, Textarea, Button, Alert, AlertTitle, AlertDescription, Link, Code, Flex, Box } from '@chakra-ui/react';
import { VStack, Textarea, Button, Alert, AlertTitle, AlertDescription, Code, Flex, Box } from '@chakra-ui/react';
import * as Sentry from '@sentry/react';
import type { ChangeEvent } from 'react';
import React from 'react';
......@@ -47,8 +47,6 @@ const Login = () => {
});
}, [ toast, token ]);
const prodUrl = 'https://blockscout.com/poa/core';
const handleNumIncrement = React.useCallback(() => {
for (let index = 0; index < 5; index++) {
setNum(5);
......@@ -58,19 +56,15 @@ const Login = () => {
return (
<Page>
<VStack gap={ 4 } alignItems="flex-start" maxW="1000px">
<PageTitle text="Vercel page"/>
<Flex columnGap={ 2 } alignItems="center">
<Box w="50px" textAlign="center">{ num }</Box>
<Button onClick={ handleNumIncrement } size="sm">add</Button>
</Flex>
<PageTitle text="Login page 😂"/>
{ isFormVisible && (
<>
<Alert status="error" flexDirection="column" alignItems="flex-start">
<AlertTitle fontSize="md">
!!! Temporary solution for authentication !!!
!!! Temporary solution for authentication on localhost !!!
</AlertTitle>
<AlertDescription mt={ 3 }>
To Sign in go to <Link href={ prodUrl } target="_blank">{ prodUrl }</Link> first, sign in there, copy obtained API token from cookie
To Sign in go to production instance first, sign in there, copy obtained API token from cookie
<Code ml={ 1 }>{ cookies.NAMES.API_TOKEN }</Code> and paste it in the form below. After submitting the form you should be successfully
authenticated in current environment
</AlertDescription>
......@@ -80,6 +74,10 @@ const Login = () => {
</>
) }
<Button colorScheme="red" onClick={ checkSentry }>Check Sentry</Button>
<Flex columnGap={ 2 } alignItems="center">
<Box w="50px" textAlign="center">{ num }</Box>
<Button onClick={ handleNumIncrement } size="sm">add</Button>
</Flex>
</VStack>
</Page>
);
......
import { Flex } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import { QueryKeys } from 'types/client/queries';
import useApi from 'lib/api/useApi';
import * as cookies from 'lib/cookies';
import useFetch from 'lib/hooks/useFetch';
import AppError from 'ui/shared/AppError/AppError';
import ErrorBoundary from 'ui/shared/ErrorBoundary';
import PageContent from 'ui/shared/Page/PageContent';
......@@ -25,9 +22,7 @@ const Page = ({
hideMobileHeaderOnScrollDown,
isHomePage,
}: Props) => {
const fetch = useFetch();
useQuery<unknown, unknown, unknown>([ QueryKeys.csrf ], async() => await fetch('/node-api/account/csrf'), {
useApi('csrf', {
enabled: Boolean(cookies.get(cookies.NAMES.API_TOKEN)),
});
......
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