diff --git a/src/components/Form/index.jsx b/src/components/Form/index.jsx index 0ddb96dc..e90950b2 100644 --- a/src/components/Form/index.jsx +++ b/src/components/Form/index.jsx @@ -256,7 +256,7 @@ export default class BaseForm extends React.Component { (err) => { this.updateSumbitting(false); this.responseError = err; - this.showNotice && Notify.errorWithDetail(this.errorText, err); + this.showNotice && Notify.errorWithDetail(err, this.errorText); // eslint-disable-next-line no-console console.log(err); if (callback && isFunction(callback)) { diff --git a/src/components/Notify/index.jsx b/src/components/Notify/index.jsx index ed433a61..acea9981 100644 --- a/src/components/Notify/index.jsx +++ b/src/components/Notify/index.jsx @@ -25,6 +25,8 @@ import CodeEditor from 'components/CodeEditor'; import ModalButton from 'components/ModalButton'; import globalRootStore from 'stores/root'; import { unescapeHtml } from 'utils/index'; +import { statusMap } from '@/resources/code'; +import { isEmpty, isString } from 'lodash'; import styles from './index.less'; const open = (args) => { @@ -122,29 +124,44 @@ const process = (title, description) => { }); }; -const errorWithDetail = (title, err) => { - const description = err ? ( - +const errorWithDetail = (err, title) => { + const { status, message } = err || {}; + let nTitle = title; + let description; + if (status && parseInt(status, 10) >= 500) { + if (!isEmpty(message) && !statusMap[status]) { + if (isString(message)) { + nTitle += `${t('message')}${t('.')}`; + } else if (message.reason) { + nTitle += `${t('message.reason')}${t('.')}`; } - /> - ) : ( - '' - ); - error(title, description); + nTitle += `${t('Status Code')}: ${status}`; + } else { + nTitle += statusMap[status]; + } + } else { + // prettier-ignore + description = err + ? + } + /> + : '' + } + error(nTitle, description); }; const Notify = { diff --git a/src/components/StepForm/index.jsx b/src/components/StepForm/index.jsx index 78e49acf..e7e49321 100644 --- a/src/components/StepForm/index.jsx +++ b/src/components/StepForm/index.jsx @@ -220,7 +220,7 @@ export default class BaseStepForm extends React.Component { (err) => { // eslint-disable-next-line no-console console.log('reject', err); - Notify.errorWithDetail(this.errorText, err); + Notify.errorWithDetail(err, this.errorText); } ); }; diff --git a/src/components/Tables/Base/ActionButton/index.jsx b/src/components/Tables/Base/ActionButton/index.jsx index 64445c74..ccc16fe7 100644 --- a/src/components/Tables/Base/ActionButton/index.jsx +++ b/src/components/Tables/Base/ActionButton/index.jsx @@ -243,7 +243,7 @@ class ActionButton extends Component { const message = submitErrorMsgBatch ? submitErrorMsgBatch(data) : getDefaultMsg(this.props.action, data).submitErrorMsgBatch; - Notify.errorWithDetail(message, error); + Notify.errorWithDetail(error, message); this.onCallback(false, true); }; @@ -263,7 +263,7 @@ class ActionButton extends Component { const message = submitErrorMsg ? submitErrorMsg(data, error) : getDefaultMsg(this.props.action, data).submitErrorMsg; - Notify.errorWithDetail(message, error); + Notify.errorWithDetail(error, message); this.onCallback(false, true); }; diff --git a/src/containers/List/index.jsx b/src/containers/List/index.jsx index b4487067..97bf749f 100644 --- a/src/containers/List/index.jsx +++ b/src/containers/List/index.jsx @@ -491,10 +491,11 @@ export default class BaseList extends React.Component { message: t("You don't have access to get {name}.", { name: this.name.toLowerCase(), }), + status: 401, }; Notify.errorWithDetail( - t('Unable to get {name}.', { name: this.name.toLowerCase() }), - error + error, + t('Unable to get {name}.', { name: this.name.toLowerCase() }) ); this.list.isLoading = false; this.list.silent = false; @@ -515,14 +516,15 @@ export default class BaseList extends React.Component { const title = `${t('Get {name} error.', { name: this.name.toLowerCase(), })} ${sysErr}`; - Notify.errorWithDetail(title); + Notify.errorWithDetail(null, title); } else { const error = { message: data || message || e || '', + status: e.status, }; Notify.errorWithDetail( - t('Get {name} error.', { name: this.name.toLowerCase() }), - error + error, + t('Get {name} error.', { name: this.name.toLowerCase() }) ); } this.list.isLoading = false; diff --git a/src/containers/TabDetail/index.jsx b/src/containers/TabDetail/index.jsx index 922ba60e..378059e9 100644 --- a/src/containers/TabDetail/index.jsx +++ b/src/containers/TabDetail/index.jsx @@ -223,10 +223,11 @@ export default class DetailBase extends React.Component { message: t("You don't have access to get {name}.", { name: this.name.toLowerCase(), }), + status: 401, }; Notify.errorWithDetail( - t('Unable to get {name} detail.', { name: this.name.toLowerCase() }), - error + error, + t('Unable to get {name} detail.', { name: this.name.toLowerCase() }) ); return; } @@ -260,10 +261,11 @@ export default class DetailBase extends React.Component { } else { const error = { message: e, + status: e.code || e.status, }; Notify.errorWithDetail( - t('Get {name} detail error.', { name: this.name.toLowerCase() }), - error + error, + t('Get {name} detail error.', { name: this.name.toLowerCase() }) ); } }; diff --git a/src/locales/en.json b/src/locales/en.json index 90f3552d..dd5022ce 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -2,6 +2,7 @@ " You can go to the console to ": " You can go to the console to ", "\"Shared\" volume can be mounted on multiple instances": "\"Shared\" volume can be mounted on multiple instances", "-1 means no connection limit": "-1 means no connection limit", + ".": ".", "1. The backup can only capture the data that has been written to the volume at the beginning of the backup task, excluding the data in the cache at that time.": "1. The backup can only capture the data that has been written to the volume at the beginning of the backup task, excluding the data in the cache at that time.", "1. The name of the custom resource class property should start with CUSTOM_, can only contain uppercase letters A ~ Z, numbers 0 ~ 9 or underscores, and the length should not exceed 255 characters (for example: CUSTOM_BAREMETAL_SMALL).": "1. The name of the custom resource class property should start with CUSTOM_, can only contain uppercase letters A ~ Z, numbers 0 ~ 9 or underscores, and the length should not exceed 255 characters (for example: CUSTOM_BAREMETAL_SMALL).", "1. The name of the trait should start with CUSTOM_, can only contain uppercase letters A ~ Z, numbers 0 ~ 9 or underscores, and the length should not exceed 255 characters (for example: CUSTOM_TRAIT1).": "1. The name of the trait should start with CUSTOM_, can only contain uppercase letters A ~ Z, numbers 0 ~ 9 or underscores, and the length should not exceed 255 characters (for example: CUSTOM_TRAIT1).", @@ -154,6 +155,7 @@ "Backup Name": "Backup Name", "Backups": "Backups", "Backups & Snapshots": "Backups & Snapshots", + "Bad Gateway (code: 502) ": "Bad Gateway (code: 502) ", "BandWidth Limit Egress": "BandWidth Limit Egress", "BandWidth Limit Ingress": "BandWidth Limit Ingress", "Bandwidth limit": "Bandwidth limit", @@ -657,6 +659,7 @@ "GPU pass-through will load GPU devices directly to the instance for use. VGPU is a GPU virtualization solution. GPU resources will be segmented and distributed to multiple instances for shared use.": "GPU pass-through will load GPU devices directly to the instance for use. VGPU is a GPU virtualization solution. GPU resources will be segmented and distributed to multiple instances for shared use.", "GRE": "GRE", "Gateway IP": "Gateway IP", + "Gateway Time-out (code: 504) ": "Gateway Time-out (code: 504) ", "General Computing Type": "General Computing Type", "General Purpose": "General Purpose", "Get OpenRC file": "Get OpenRC file", @@ -666,6 +669,7 @@ "Gigabytes(GB)": "Gigabytes(GB)", "Given IP": "Given IP", "Global Setting": "Global Setting", + "HTTP Version not supported (code: 505) ": "HTTP Version not supported (code: 505) ", "Hard Reboot": "Hard Reboot", "Hard Rebooting": "Hard Rebooting", "Health Monitor Delay": "Health Monitor Delay", @@ -812,6 +816,7 @@ "Internal Ip Address": "Internal Ip Address", "Internal Network Bandwidth(Gbps)": "Internal Network Bandwidth(Gbps)", "Internal Port": "Internal Port", + "Internal Server Error (code: 500) ": "Internal Server Error (code: 500) ", "Invalid": "Invalid", "Invalid CIDR.": "Invalid CIDR.", "Invalid IP Address": "Invalid IP Address", @@ -1011,6 +1016,7 @@ "Node Name": "Node Name", "Node Num": "Node Num", "Normal": "Normal", + "Not Implemented (code: 501) ": "Not Implemented (code: 501) ", "Not Open": "Not Open", "Not deleted with the instance": "Not deleted with the instance", "Not select": "Not select", @@ -1362,6 +1368,7 @@ "Service": "Service", "Service State": "Service State", "Service Status": "Service Status", + "Service Unavailable (code: 503) ": "Service Unavailable (code: 503) ", "Services": "Services", "Set Boot Device": "Set Boot Device", "Set IP": "Set IP", @@ -1431,6 +1438,7 @@ "State": "State", "Static Routes": "Static Routes", "Status": "Status", + "Status Code": "Status Code", "Stop": "Stop", "Stop Instance": "Stop Instance", "Stop auto refreshing data": "Stop auto refreshing data", @@ -1855,6 +1863,8 @@ "lock instance": "lock instance", "manage qos spec": "manage qos spec", "manage resource types": "manage resource types", + "message": "message", + "message.reason": "message.reason", "metadata": "metadata", "metadatas": "metadatas", "migrate": "migrate", diff --git a/src/locales/zh.json b/src/locales/zh.json index 678c60b6..56c1c2f2 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -2,6 +2,7 @@ " You can go to the console to ": "您可以前往控制台 ", "\"Shared\" volume can be mounted on multiple instances": "“共享”云硬盘类型的云硬盘,可挂载到多台云主机上", "-1 means no connection limit": "-1表示无连接限制", + ".": "。", "1. The backup can only capture the data that has been written to the volume at the beginning of the backup task, excluding the data in the cache at that time.": "1. 备份只能捕获在备份任务开始时已经写入磁盘的数据,不包括当时位于缓存的数据。", "1. The name of the custom resource class property should start with CUSTOM_, can only contain uppercase letters A ~ Z, numbers 0 ~ 9 or underscores, and the length should not exceed 255 characters (for example: CUSTOM_BAREMETAL_SMALL).": "1. 自定义资源属性的命名应该以 CUSTOM_ 开头、只能包含大写字母A ~ Z、数字0 ~ 9或下划线、长度不超过255个字符(比如:CUSTOM_BAREMETAL_SMALL)。", "1. The name of the trait should start with CUSTOM_, can only contain uppercase letters A ~ Z, numbers 0 ~ 9 or underscores, and the length should not exceed 255 characters (for example: CUSTOM_TRAIT1).": "1. 特性的命名应该以 CUSTOM_ 开头、只能包含大写字母A ~ Z、数字0 ~ 9或下划线、长度不超过255个字符(比如:CUSTOM_TRAIT1)。", @@ -154,6 +155,7 @@ "Backup Name": "备份名称", "Backups": "备份", "Backups & Snapshots": "备份和快照", + "Bad Gateway (code: 502) ": "无效网关(错误码:502)", "BandWidth Limit Egress": "出方向带宽限制", "BandWidth Limit Ingress": "入方向带宽限制", "Bandwidth limit": "带宽限制", @@ -657,6 +659,7 @@ "GPU pass-through will load GPU devices directly to the instance for use. VGPU is a GPU virtualization solution. GPU resources will be segmented and distributed to multiple instances for shared use.": "GPU直通将GPU设备直接加载给云主机进行使用。vGPU是GPU虚拟化方案,GPU资源将被切分后分配给多个云主机共享使用。", "GRE": "", "Gateway IP": "网关IP", + "Gateway Time-out (code: 504) ": "网关超时(错误码:504 )", "General Computing Type": "通用计算型", "General Purpose": "通用型", "Get OpenRC file": "获取Openstack RC 文件", @@ -666,6 +669,7 @@ "Gigabytes(GB)": "云硬盘容量(GB)", "Given IP": "指定IP", "Global Setting": "平台配置", + "HTTP Version not supported (code: 505) ": "", "Hard Reboot": "硬重启", "Hard Rebooting": "硬重启中", "Health Monitor Delay": "检查间隔(秒)", @@ -812,6 +816,7 @@ "Internal Ip Address": "目标IP", "Internal Network Bandwidth(Gbps)": "内网带宽(Gbps)", "Internal Port": "目标端口", + "Internal Server Error (code: 500) ": "服务器错误(错误码:500)", "Invalid": "失效", "Invalid CIDR.": "无效的CIDR", "Invalid IP Address": "无效的IP地址", @@ -1011,6 +1016,7 @@ "Node Name": "节点名称", "Node Num": "节点总数", "Normal": "正常", + "Not Implemented (code: 501) ": "服务器不支持请求(错误码:501)", "Not Open": "未开放", "Not deleted with the instance": "不随云主机删除", "Not select": "不选择", @@ -1362,6 +1368,7 @@ "Service": "服务", "Service State": "服务状态", "Service Status": "管理状态", + "Service Unavailable (code: 503) ": "服务不可用(错误码:503 )", "Services": "服务", "Set Boot Device": "设置引导设备", "Set IP": "设置IP", @@ -1431,6 +1438,7 @@ "State": "状态", "Static Routes": "静态路由", "Status": "状态", + "Status Code": "", "Stop": "关闭", "Stop Instance": "关闭云主机", "Stop auto refreshing data": "关闭自动刷新数据", @@ -1855,6 +1863,8 @@ "lock instance": "锁定云主机", "manage qos spec": "管理QoS规格", "manage resource types": "管理资源类型", + "message": "", + "message.reason": "", "metadata": "元数据", "metadatas": "元数据", "migrate": "迁移", diff --git a/src/pages/compute/containers/Instance/actions/StepCreate/index.jsx b/src/pages/compute/containers/Instance/actions/StepCreate/index.jsx index dd9cb9a1..815b6367 100644 --- a/src/pages/compute/containers/Instance/actions/StepCreate/index.jsx +++ b/src/pages/compute/containers/Instance/actions/StepCreate/index.jsx @@ -370,7 +370,7 @@ class StepCreate extends StepAction { ) { Notify.error(t('Quota exceeded')); } else { - Notify.errorWithDetail(this.errorText, err); + Notify.errorWithDetail(err, this.errorText); } } ); diff --git a/src/pages/network/containers/Network/actions/CreateNetwork.jsx b/src/pages/network/containers/Network/actions/CreateNetwork.jsx index 0144b999..2d9dfe0c 100644 --- a/src/pages/network/containers/Network/actions/CreateNetwork.jsx +++ b/src/pages/network/containers/Network/actions/CreateNetwork.jsx @@ -144,14 +144,14 @@ export default class CreateNetwork extends ModalAction { (err) => { const { type, error } = JSON.parse(err); if (type === 'create_network') { - Notify.errorWithDetail(this.errorText, error); + Notify.errorWithDetail(error, this.errorText); } else if (type === 'create_subnet') { Notify.errorWithDetail( + error, t('Unable to {action}, instance: {name}.', { action: t('Create Subnet'), name: values.subnet_name, - }), - error + }) ); } // eslint-disable-next-line no-console diff --git a/src/resources/code.js b/src/resources/code.js new file mode 100644 index 00000000..4c048350 --- /dev/null +++ b/src/resources/code.js @@ -0,0 +1,8 @@ +export const statusMap = { + 500: t('Internal Server Error (code: 500) '), + 501: t('Not Implemented (code: 501) '), + 502: t('Bad Gateway (code: 502) '), + 503: t('Service Unavailable (code: 503) '), + 504: t('Gateway Time-out (code: 504) '), + 505: t('HTTP Version not supported (code: 505) '), +};