feat: provide more user-friendly application-level error messages (#5220)

* fix: display app maintaining message

* fix: app error
This commit is contained in:
chenos 2024-09-07 07:53:50 +08:00 committed by GitHub
parent efac16c6a9
commit 6d2f17a7c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 48 additions and 25 deletions

View File

@ -92,7 +92,7 @@ export class APIClient extends APIClientSDK {
return response;
},
(error) => {
const errs = error?.response?.data?.errors || [{ message: 'Server error' }];
const errs = this.toErrMessages(error);
// Hard code here temporarily
// TODO(yangqia): improve error code and message
if (errs.find((error: { code?: string }) => error.code === 'ROLE_NOT_FOUND_ERR')) {
@ -103,6 +103,17 @@ export class APIClient extends APIClientSDK {
);
}
toErrMessages(error) {
if (typeof error?.response?.data === 'string') {
return [{ message: error?.response?.data }];
}
return (
error?.response?.data?.errors ||
error?.response?.data?.messages ||
error?.response?.error || [{ message: error.message || 'Server error' }]
);
}
useNotificationMiddleware() {
this.axios.interceptors.response.use(
(response) => {
@ -143,7 +154,7 @@ export class APIClient extends APIClientSDK {
} else if (this.app.error) {
this.app.error = null;
}
let errs = error?.response?.data?.errors || error?.response?.data?.messages || [{ message: 'Server error' }];
let errs = this.toErrMessages(error);
errs = errs.filter((error) => {
const lastTime = errorCache.get(error.message);
if (lastTime && new Date().getTime() - lastTime < 500) {

View File

@ -34,17 +34,17 @@ import { compose, normalizeContainer } from './utils';
import { defineGlobalDeps } from './utils/globalDeps';
import { getRequireJs } from './utils/requirejs';
import { CollectionFieldInterfaceComponentOption } from '../data-source/collection-field-interface/CollectionFieldInterface';
import { CollectionField } from '../data-source/collection-field/CollectionField';
import { DataSourceApplicationProvider } from '../data-source/components/DataSourceApplicationProvider';
import { DataBlockProvider } from '../data-source/data-block/DataBlockProvider';
import { DataSourceManager, type DataSourceManagerOptions } from '../data-source/data-source/DataSourceManager';
import { CollectionFieldInterfaceComponentOption } from '../data-source/collection-field-interface/CollectionFieldInterface';
import type { CollectionFieldInterfaceFactory } from '../data-source';
import { OpenModeProvider } from '../modules/popup/OpenModeProvider';
import { AppSchemaComponentProvider } from './AppSchemaComponentProvider';
import type { Plugin } from './Plugin';
import type { RequireJS } from './utils/requirejs';
import type { CollectionFieldInterfaceFactory } from '../data-source';
declare global {
interface Window {
@ -282,10 +282,21 @@ export class Application {
});
}
loadFailed = true;
const others = error?.response?.data?.error || error?.response?.data?.errors?.[0] || { message: error?.message };
const toError = (error) => {
if (typeof error?.response?.data === 'string') {
return { message: error?.response?.data };
}
if (error?.response?.data?.error) {
return error?.response?.data?.error;
}
if (error?.response?.data?.errors?.[0]) {
return error?.response?.data?.errors?.[0];
}
return { message: error?.message };
};
this.error = {
code: 'LOAD_ERROR',
...others,
...toError(error),
};
console.error(error, this.error);
}

View File

@ -142,7 +142,7 @@ const getProps = (app: Application) => {
};
}
if (app.error.code === 'APP_ERROR' || app.error.code === 'LOAD_ERROR') {
if (['ENOENT', 'APP_ERROR', 'LOAD_ERROR'].includes(app.error.code)) {
return {
status: 'error',
title: 'App error',
@ -205,7 +205,11 @@ const getProps = (app: Application) => {
return { ...props, ...commands[app.error?.command?.name] };
}
return {};
return {
status: 'warning',
title: 'App warning',
subTitle: app.error?.message,
};
};
const AppMaintaining: FC<{ app: Application; error: Error }> = observer(

View File

@ -9,11 +9,10 @@
import { uid } from '@nocobase/utils';
import fs from 'fs';
import fse from 'fs-extra';
import path from 'path';
import Application from '../../application';
import { getExposeUrl } from '../clientStaticUtils';
import PluginManager from '../plugin-manager';
//@ts-ignore
export default {
name: 'pm',
@ -135,21 +134,19 @@ export default {
enabled: true,
},
});
ctx.body = items
.map((item) => {
try {
return {
...item.toJSON(),
url: `${process.env.APP_SERVER_BASE_URL}${getExposeUrl(
item.packageName,
PLUGIN_CLIENT_ENTRY_FILE,
)}?version=${item.version}`,
};
} catch {
return false;
}
})
.filter(Boolean);
const arr = [];
for (const item of items) {
const pkgPath = path.resolve(process.env.NODE_MODULES_PATH, item.packageName);
const r = await fse.exists(pkgPath);
if (r) {
const url = `${process.env.APP_SERVER_BASE_URL}${process.env.PLUGIN_STATICS_PATH}${item.packageName}/${PLUGIN_CLIENT_ENTRY_FILE}?version=${item.version}`;
arr.push({
...item.toJSON(),
url,
});
}
}
ctx.body = arr;
await next();
},
async get(ctx, next) {