diff --git a/ui/src/components/HttpErrorContent/index.tsx b/ui/src/components/HttpErrorContent/index.tsx
index e1c40d25..abbb976c 100644
--- a/ui/src/components/HttpErrorContent/index.tsx
+++ b/ui/src/components/HttpErrorContent/index.tsx
@@ -1,30 +1,48 @@
-import { memo } from 'react';
+import { memo, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { usePageTags } from '@/hooks';
-const Index = ({ httpCode = '' }) => {
+const Index = ({ httpCode = '', errMsg = '' }) => {
const { t } = useTranslation('translation', { keyPrefix: 'page_error' });
+ useEffect(() => {
+ // auto height of container
+ const pageWrap = document.querySelector('.page-wrap');
+ if (pageWrap) {
+ // @ts-ignore
+ pageWrap.style.display = 'contents';
+ }
+
+ return () => {
+ if (pageWrap) {
+ // @ts-ignore
+ pageWrap.style.display = 'block';
+ }
+ };
+ }, []);
usePageTags({
title: t(`http_${httpCode}`, { keyPrefix: 'page_title' }),
});
+
return (
- <>
+
{httpStatusCode ? (
-
-
-
+
) : (
)}
diff --git a/ui/src/router/RouteErrorBoundary.tsx b/ui/src/router/RouteErrorBoundary.tsx
index d70683e3..2fe5b1fa 100644
--- a/ui/src/router/RouteErrorBoundary.tsx
+++ b/ui/src/router/RouteErrorBoundary.tsx
@@ -1,8 +1,7 @@
-import Error50X from '@/pages/50X';
-// import Page404 from '@/pages/404';
+import { HttpErrorContent } from '@/components';
-const Index = () => {
- return
;
+const Index = ({ errCode = '50X', errMsg = '' }) => {
+ return
;
};
export default Index;
diff --git a/ui/src/router/RouteGuard.tsx b/ui/src/router/RouteGuard.tsx
index 60d1249c..153bca51 100644
--- a/ui/src/router/RouteGuard.tsx
+++ b/ui/src/router/RouteGuard.tsx
@@ -1,41 +1,53 @@
import { FC, ReactNode, useEffect } from 'react';
-import { useLocation, useNavigate } from 'react-router-dom';
+import { useLocation, useNavigate, useLoaderData } from 'react-router-dom';
import { floppyNavigation } from '@/utils';
import { TGuardFunc } from '@/utils/guard';
-const Index: FC<{
+import RouteErrorBoundary from './RouteErrorBoundary';
+
+const RouteGuard: FC<{
children: ReactNode;
- onEnter?: TGuardFunc;
+ onEnter: TGuardFunc;
path?: string;
-}> = ({
- children,
- onEnter,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- path,
-}) => {
+ page?: string;
+}> = ({ children, onEnter, path, page }) => {
const navigate = useNavigate();
const location = useLocation();
- const callGuards = () => {
- if (onEnter) {
- const gr = onEnter();
- const redirectUrl = gr.redirect;
- if (redirectUrl) {
- floppyNavigation.navigate(redirectUrl, () => {
- navigate(redirectUrl, { replace: true });
- });
- }
+ const loaderData = useLoaderData();
+ const gr = onEnter({
+ loaderData,
+ path,
+ page,
+ });
+
+ let guardError;
+ const errCode = gr.error?.code;
+ if (errCode === '403' || errCode === '404' || errCode === '50X') {
+ guardError = {
+ code: errCode,
+ msg: gr.error?.msg,
+ };
+ }
+ const handleGuardRedirect = () => {
+ const redirectUrl = gr.redirect;
+ if (redirectUrl) {
+ floppyNavigation.navigate(redirectUrl, () => {
+ navigate(redirectUrl, { replace: true });
+ });
}
};
useEffect(() => {
- callGuards();
+ handleGuardRedirect();
}, [location]);
return (
<>
- {/* Route Guard */}
- {children}
+ {gr.ok ? children : null}
+ {!gr.ok && guardError ? (
+
+ ) : null}
>
);
};
-export default Index;
+export default RouteGuard;
diff --git a/ui/src/router/index.tsx b/ui/src/router/index.tsx
index 2e30ce75..50b6d2ef 100644
--- a/ui/src/router/index.tsx
+++ b/ui/src/router/index.tsx
@@ -13,7 +13,7 @@ const routeWrapper = (routeNodes: RouteNode[], root: RouteNode[]) => {
routeNodes.forEach((rn) => {
if (rn.page === 'pages/Layout') {
rn.element = rn.guard ? (
-
+
) : (
@@ -30,7 +30,7 @@ const routeWrapper = (routeNodes: RouteNode[], root: RouteNode[]) => {
rn.element = (
{rn.guard ? (
-
+
) : (
diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts
index 3b5b2e3b..5a350d75 100644
--- a/ui/src/router/routes.ts
+++ b/ui/src/router/routes.ts
@@ -2,6 +2,8 @@ import type { IndexRouteObject, NonIndexRouteObject } from 'react-router-dom';
import { guard } from '@/utils';
import type { TGuardFunc } from '@/utils/guard';
+import { editCheck } from '@/services';
+import { isEditable } from '@/utils/guard';
type IndexRouteNode = Omit;
type NonIndexRouteNode = Omit;
@@ -70,6 +72,13 @@ const routes: RouteNode[] = [
{
path: 'posts/:qid/:aid/edit',
page: 'pages/Questions/EditAnswer',
+ loader: async ({ params }) => {
+ const ret = await editCheck(params.aid as string, true);
+ return ret;
+ },
+ guard: (args) => {
+ return isEditable(args);
+ },
},
{
path: '/search',
diff --git a/ui/src/services/client/revision.ts b/ui/src/services/client/revision.ts
index 06b5ce49..41201cb3 100644
--- a/ui/src/services/client/revision.ts
+++ b/ui/src/services/client/revision.ts
@@ -1,9 +1,11 @@
import request from '@/utils/request';
import * as Type from '@/common/interface';
-export const editCheck = (id: string) => {
+export const editCheck = (id: string, passingError: boolean = false) => {
const apiUrl = `/answer/api/v1/revisions/edit/check?id=${id}`;
- return request.get(apiUrl);
+ return request.get(apiUrl, {
+ passingError,
+ });
};
export const revisionAudit = (id: string, operation: 'approve' | 'reject') => {
diff --git a/ui/src/stores/errorCode.ts b/ui/src/stores/errorCode.ts
index 6da8f8d0..aa2bcea1 100644
--- a/ui/src/stores/errorCode.ts
+++ b/ui/src/stores/errorCode.ts
@@ -2,24 +2,26 @@ import create from 'zustand';
type codeType = '403' | '404' | '50X' | '';
-interface NotFoundType {
+interface ErrorCodeType {
code: codeType;
- update: (code: codeType) => void;
+ msg: string;
+ update: (code: codeType, msg?: string) => void;
reset: () => void;
}
-const notFound = create((set) => ({
+const Index = create((set) => ({
code: '',
- update: (code: codeType) => {
+ msg: '',
+ update: (code: codeType, msg: string = '') => {
set(() => {
- return { code };
+ return { code, msg };
});
},
reset: () => {
set(() => {
- return { code: '' };
+ return { code: '', msg: '' };
});
},
}));
-export default notFound;
+export default Index;
diff --git a/ui/src/stores/index.ts b/ui/src/stores/index.ts
index a6c3b143..247f1d64 100644
--- a/ui/src/stores/index.ts
+++ b/ui/src/stores/index.ts
@@ -10,7 +10,7 @@ import pageTagStore from './pageTags';
import customizeStore from './customize';
import themeSettingStore from './themeSetting';
import loginToContinueStore from './loginToContinue';
-import errorCode from './errorCode';
+import errorCodeStore from './errorCode';
export {
toastStore,
@@ -24,5 +24,5 @@ export {
themeSettingStore,
seoSettingStore,
loginToContinueStore,
- errorCode,
+ errorCodeStore,
};
diff --git a/ui/src/stores/seoSetting.ts b/ui/src/stores/seoSetting.ts
index 6c15e9cf..c714a8f5 100644
--- a/ui/src/stores/seoSetting.ts
+++ b/ui/src/stores/seoSetting.ts
@@ -7,7 +7,7 @@ interface IProps {
update: (params: AdminSettingsSeo) => void;
}
-const siteInfo = create((set) => ({
+const Index = create((set) => ({
seo: {
robots: '',
permalink: 1,
@@ -25,4 +25,4 @@ const siteInfo = create((set) => ({
}),
}));
-export default siteInfo;
+export default Index;
diff --git a/ui/src/utils/guard.ts b/ui/src/utils/guard.ts
index 7c852986..0b231b1d 100644
--- a/ui/src/utils/guard.ts
+++ b/ui/src/utils/guard.ts
@@ -30,8 +30,16 @@ type TLoginState = {
export type TGuardResult = {
ok: boolean;
redirect?: string;
+ error?: {
+ code?: number | string;
+ msg?: string;
+ };
};
-export type TGuardFunc = () => TGuardResult;
+export type TGuardFunc = (args: {
+ loaderData?: any;
+ path?: string;
+ page?: string;
+}) => TGuardResult;
export const deriveLoginState = (): TLoginState => {
const ls: TLoginState = {
@@ -189,6 +197,19 @@ export const isAdminOrModerator = () => {
return gr;
};
+export const isEditable = (args) => {
+ const loaderData = args?.loaderData || {};
+ const gr: TGuardResult = { ok: true };
+ if (loaderData.code === 400) {
+ gr.ok = false;
+ gr.error = {
+ code: '403',
+ msg: loaderData.msg,
+ };
+ }
+ return gr;
+};
+
export const allowNewRegistration = () => {
const gr: TGuardResult = { ok: true };
const loginSetting = loginSettingStore.getState().login;
diff --git a/ui/src/utils/request.ts b/ui/src/utils/request.ts
index acb96556..8ba18831 100644
--- a/ui/src/utils/request.ts
+++ b/ui/src/utils/request.ts
@@ -2,7 +2,7 @@ import axios, { AxiosResponse } from 'axios';
import type { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios';
import { Modal } from '@/components';
-import { loggedUserInfoStore, toastStore, errorCode } from '@/stores';
+import { loggedUserInfoStore, toastStore, errorCodeStore } from '@/stores';
import { LOGGED_TOKEN_STORAGE_KEY, IGNORE_PATH_LIST } from '@/common/constants';
import { RouteAlias } from '@/router/alias';
import { getCurrentLang } from '@/utils/localize';
@@ -16,8 +16,11 @@ const baseConfig = {
withCredentials: true,
};
-interface APIconfig extends AxiosRequestConfig {
- allow404: boolean;
+interface ApiConfig extends AxiosRequestConfig {
+ // Configure whether to allow takeover of 404 errors
+ allow404?: boolean;
+ // Configure whether to pass errors directly
+ passingError?: boolean;
}
class Request {
@@ -52,14 +55,17 @@ class Request {
return data;
},
(error) => {
- const { status, data: respData } = error.response || {};
- const { data = {}, msg = '', reason = '' } = respData || {};
-
- // console.log('response error:', error);
-
+ const {
+ status,
+ data: errModel,
+ config: errConfig,
+ } = error.response || {};
+ const { data = {}, msg = '' } = errModel || {};
if (status === 400) {
- // show error message
- if (data instanceof Object && data.err_type) {
+ if (data.err_type && errConfig?.passingError) {
+ return errModel;
+ }
+ if (data.err_type) {
if (data.err_type === 'toast') {
// toast error message
toastStore.getState().show({
@@ -90,7 +96,6 @@ class Request {
return Promise.reject({
code: status,
msg,
- reason,
isError: true,
list: data,
});
@@ -107,7 +112,7 @@ class Request {
// 401: Re-login required
if (status === 401) {
// clear userinfo
- errorCode.getState().reset();
+ errorCodeStore.getState().reset();
loggedUserInfoStore.getState().clear();
floppyNavigation.navigateToLogin();
return Promise.reject(false);
@@ -140,7 +145,7 @@ class Request {
return Promise.reject(false);
}
if (error.config?.url.includes('/admin/api')) {
- errorCode.getState().update('403');
+ errorCodeStore.getState().update('403');
return Promise.reject(false);
}
@@ -157,14 +162,14 @@ class Request {
if (isIgnoredPath(IGNORE_PATH_LIST)) {
return Promise.reject(false);
}
- errorCode.getState().update('404');
+ errorCodeStore.getState().update('404');
return Promise.reject(false);
}
if (status >= 500) {
if (isIgnoredPath(IGNORE_PATH_LIST)) {
return Promise.reject(false);
}
- errorCode.getState().update('50X');
+ errorCodeStore.getState().update('50X');
console.error(
`Request failed with status code ${status}, ${msg || ''}`,
);
@@ -178,7 +183,7 @@ class Request {
return this.instance.request(config);
}
- public get(url: string, config?: APIconfig): Promise {
+ public get(url: string, config?: ApiConfig): Promise {
return this.instance.get(url, config);
}