Commit 973b59e1 authored by tom's avatar tom

refactoring and clean up

parent 420bfd15
...@@ -2,10 +2,11 @@ import { compile } from 'path-to-regexp'; ...@@ -2,10 +2,11 @@ import { compile } from 'path-to-regexp';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import type { ApiResource } from './resources'; import { RESOURCES } from './resources';
import type { ApiResource, ResourceName } from './resources';
export default function buildUrl( export default function buildUrl(
resource: ApiResource, _resource: ApiResource | ResourceName,
pathParams?: Record<string, string | undefined>, pathParams?: Record<string, string | undefined>,
queryParams?: Record<string, string | number | undefined>, queryParams?: Record<string, string | number | undefined>,
) { ) {
...@@ -22,6 +23,7 @@ export default function buildUrl( ...@@ -22,6 +23,7 @@ export default function buildUrl(
// will need to change the condition if there are more micro services that need authentication and DB state changes // will need to change the condition if there are more micro services that need authentication and DB state changes
const needProxy = appConfig.host !== appConfig.api.host; const needProxy = appConfig.host !== appConfig.api.host;
const resource: ApiResource = typeof _resource === 'string' ? RESOURCES[_resource] : _resource;
const baseUrl = needProxy ? appConfig.baseUrl : (resource.endpoint || appConfig.api.endpoint); const baseUrl = needProxy ? appConfig.baseUrl : (resource.endpoint || appConfig.api.endpoint);
const basePath = resource.basePath !== undefined ? resource.basePath : appConfig.api.basePath; const basePath = resource.basePath !== undefined ? resource.basePath : appConfig.api.basePath;
const path = needProxy ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path; const path = needProxy ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path;
......
import type { NextApiRequest } from 'next';
export default function getSearchParams(req: NextApiRequest) {
const searchParams: Record<string, string> = {};
Object.entries(req.query).forEach(([ key, value ]) => {
searchParams[key] = Array.isArray(value) ? value.join(',') : (value || '');
});
return new URLSearchParams(searchParams).toString();
}
import type { NextApiRequest } from 'next';
import appConfig from 'configs/app/config';
export default function getUrlWithNetwork(_req: NextApiRequest, path: string) {
return [
appConfig.api.basePath,
path,
]
.filter((segment) => segment !== '' && segment !== '/')
.join('');
}
import type { NextApiRequest, NextApiResponse } from 'next';
import fetchFactory from 'lib/api/fetch';
import getUrlWithNetwork from 'lib/api/getUrlWithNetwork';
import { httpLogger } from 'lib/api/logger';
type Methods = 'GET' | 'POST' | 'PUT' | 'DELETE';
export default function createHandler(getUrl: (_req: NextApiRequest) => string, allowedMethods: Array<Methods>, apiEndpoint?: string) {
const handler = async(_req: NextApiRequest, res: NextApiResponse) => {
httpLogger(_req, res);
if (!_req.method || !allowedMethods.includes(_req.method as Methods)) {
res.setHeader('Allow', allowedMethods);
res.status(405).end(`Method ${ _req.method } Not Allowed`);
return;
}
const isBodyDisallowed = _req.method === 'GET' || _req.method === 'HEAD';
const url = apiEndpoint ? `/api${ getUrl(_req) }` : getUrlWithNetwork(_req, `/api${ getUrl(_req) }`);
const fetch = fetchFactory(_req, apiEndpoint);
const response = await fetch(url, {
method: _req.method,
body: isBodyDisallowed ? undefined : _req.body,
});
if (response.status === 200) {
const data = await response.json();
res.status(200).json(data);
return;
}
let responseError;
const defaultError = { statusText: response.statusText, status: response.status };
try {
const error = await response.json() as { errors: unknown };
responseError = error?.errors || defaultError;
} catch (error) {
responseError = defaultError;
}
httpLogger.logger.error({ err: responseError, url: _req.url });
res.status(500).json(responseError);
};
return handler;
}
...@@ -6,12 +6,12 @@ import appConfig from 'configs/app/config'; ...@@ -6,12 +6,12 @@ import appConfig from 'configs/app/config';
import { httpLogger } from 'lib/api/logger'; import { httpLogger } from 'lib/api/logger';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
// first arg can be only a string
// FIXME migrate to RequestInfo later if needed
export default function fetchFactory( export default function fetchFactory(
_req: NextApiRequest, _req: NextApiRequest,
apiEndpoint: string = appConfig.api.endpoint, apiEndpoint: string = appConfig.api.endpoint,
) { ) {
// first arg can be only a string
// FIXME migrate to RequestInfo later if needed
return function fetch(path: string, init?: RequestInit): Promise<Response> { return function fetch(path: string, init?: RequestInit): Promise<Response> {
const csrfToken = _req.headers['x-csrf-token']?.toString(); const csrfToken = _req.headers['x-csrf-token']?.toString();
const headers = { const headers = {
......
...@@ -35,6 +35,9 @@ export interface ApiResource { ...@@ -35,6 +35,9 @@ export interface ApiResource {
export const RESOURCES = { export const RESOURCES = {
// ACCOUNT // ACCOUNT
csrf: {
path: '/api/account/v1/get_csrf',
},
user_info: { user_info: {
path: '/api/account/v1/user/info', path: '/api/account/v1/user/info',
}, },
...@@ -198,7 +201,6 @@ export type ResourcePaginationKey<R extends ResourceName> = typeof RESOURCES[R] ...@@ -198,7 +201,6 @@ export type ResourcePaginationKey<R extends ResourceName> = typeof RESOURCES[R]
export const resourceKey = (x: keyof typeof RESOURCES) => x; export const resourceKey = (x: keyof typeof RESOURCES) => x;
export interface ResourceError<T = unknown> { export interface ResourceError<T = unknown> {
error?: T;
payload?: T; payload?: T;
status: Response['status']; status: Response['status'];
statusText: Response['statusText']; statusText: Response['statusText'];
......
...@@ -5,6 +5,7 @@ import React from 'react'; ...@@ -5,6 +5,7 @@ import React from 'react';
import type { CsrfData } from 'types/client/account'; import type { CsrfData } from 'types/client/account';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import { getResourceKey } from 'lib/api/useApiQuery';
export interface Params { export interface Params {
method?: RequestInit['method']; method?: RequestInit['method'];
...@@ -15,7 +16,7 @@ export interface Params { ...@@ -15,7 +16,7 @@ export interface Params {
export default function useFetch() { export default function useFetch() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { token } = queryClient.getQueryData<CsrfData>([ 'csrf' ]) || {}; const { token } = queryClient.getQueryData<CsrfData>(getResourceKey('csrf')) || {};
return React.useCallback(<Success, Error>(path: string, params?: Params): Promise<Success | ResourceError<Error>> => { return React.useCallback(<Success, Error>(path: string, params?: Params): Promise<Success | ResourceError<Error>> => {
const reqParams = { const reqParams = {
...@@ -42,8 +43,6 @@ export default function useFetch() { ...@@ -42,8 +43,6 @@ export default function useFetch() {
return response.json().then( return response.json().then(
(jsonError) => Promise.reject({ (jsonError) => Promise.reject({
// DEPRECATED
error: jsonError as Error,
payload: jsonError as Error, payload: jsonError as Error,
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
......
...@@ -23,7 +23,7 @@ function MyApp({ Component, pageProps }: AppProps) { ...@@ -23,7 +23,7 @@ function MyApp({ Component, pageProps }: AppProps) {
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
retry: (failureCount, _error) => { retry: (failureCount, _error) => {
const error = _error as ResourceError<{ status: number }>; const error = _error as ResourceError<{ status: number }>;
const status = error?.status || error?.error?.status; const status = error?.status || error?.payload?.status;
if (status && status >= 400 && status < 500) { if (status && status >= 400 && status < 500) {
// don't do retry for client error responses // don't do retry for client error responses
return false; return false;
......
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import fetchFactory from 'lib/api/fetch'; import buildUrl from 'lib/api/buildUrl';
import getUrlWithNetwork from 'lib/api/getUrlWithNetwork';
import { httpLogger } from 'lib/api/logger'; import { httpLogger } from 'lib/api/logger';
import fetchFactory from 'lib/api/nodeFetch';
export default async function csrfHandler(_req: NextApiRequest, res: NextApiResponse) { export default async function csrfHandler(_req: NextApiRequest, res: NextApiResponse) {
httpLogger(_req, res); httpLogger(_req, res);
const url = getUrlWithNetwork(_req, `/api/account/v1/get_csrf`); const url = buildUrl('csrf');
const fetch = fetchFactory(_req); const response = await fetchFactory(_req)(url);
const response = await fetch(url);
if (response.status === 200) { if (response.status === 200) {
const token = response.headers.get('x-bs-account-csrf'); const token = response.headers.get('x-bs-account-csrf');
......
...@@ -2,7 +2,7 @@ import _pick from 'lodash/pick'; ...@@ -2,7 +2,7 @@ import _pick from 'lodash/pick';
import _pickBy from 'lodash/pickBy'; import _pickBy from 'lodash/pickBy';
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import fetchFactory from 'lib/api/fetch'; import fetchFactory from 'lib/api/nodeFetch';
const handler = async(_req: NextApiRequest, res: NextApiResponse) => { const handler = async(_req: NextApiRequest, res: NextApiResponse) => {
if (!_req.url) { if (!_req.url) {
......
...@@ -57,7 +57,7 @@ const BlockDetails = () => { ...@@ -57,7 +57,7 @@ const BlockDetails = () => {
} }
if (isError) { if (isError) {
const is404 = error?.error?.status === 404; const is404 = error?.payload?.status === 404;
return is404 ? <span>This block has not been processed yet.</span> : <DataFetchAlert/>; return is404 ? <span>This block has not been processed yet.</span> : <DataFetchAlert/>;
} }
......
...@@ -2,8 +2,11 @@ import { Flex } from '@chakra-ui/react'; ...@@ -2,8 +2,11 @@ import { Flex } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config';
import buildUrl from 'lib/api/buildUrl';
import { getResourceKey } from 'lib/api/useApiQuery';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
// import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import AppError from 'ui/shared/AppError/AppError'; import AppError from 'ui/shared/AppError/AppError';
import ErrorBoundary from 'ui/shared/ErrorBoundary'; import ErrorBoundary from 'ui/shared/ErrorBoundary';
import PageContent from 'ui/shared/Page/PageContent'; import PageContent from 'ui/shared/Page/PageContent';
...@@ -23,22 +26,25 @@ const Page = ({ ...@@ -23,22 +26,25 @@ const Page = ({
hideMobileHeaderOnScrollDown, hideMobileHeaderOnScrollDown,
isHomePage, isHomePage,
}: Props) => { }: Props) => {
// const customFetch = useFetch(); const nodeApiFetch = useFetch();
useQuery([ 'csrf' ], async() => { useQuery(getResourceKey('csrf'), async() => {
// const nodeApiResponse = await customFetch('/node-api/csrf'); if (appConfig.host === appConfig.api.host) {
const apiResponse = await fetch('https://blockscout-main.test.aws-k8s.blockscout.com/api/account/v1/get_csrf', { credentials: 'include' }); const url = buildUrl('csrf');
const apiResponse = await fetch(url, { credentials: 'include' });
const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf');
// eslint-disable-next-line no-console
console.log('>>> RESPONSE HEADERS <<<');
// eslint-disable-next-line no-console
console.table([ {
'content-length': apiResponse.headers.get('content-length'),
'x-bs-account-csrf': csrfFromHeader,
} ]);
const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf'); return csrfFromHeader ? { token: csrfFromHeader } : undefined;
// eslint-disable-next-line no-console }
console.log('>>> RESPONSE HEADERS <<<');
// eslint-disable-next-line no-console
console.table([ {
'content-length': apiResponse.headers.get('content-length'),
'x-bs-account-csrf': csrfFromHeader,
} ]);
return csrfFromHeader ? { token: csrfFromHeader } : undefined; return nodeApiFetch('/node-api/csrf');
}, { }, {
enabled: Boolean(cookies.get(cookies.NAMES.API_TOKEN)), 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