diff --git a/src/api-engine/api/routes/node/views.py b/src/api-engine/api/routes/node/views.py index 5ca3d1272..106eb00f4 100644 --- a/src/api-engine/api/routes/node/views.py +++ b/src/api-engine/api/routes/node/views.py @@ -772,6 +772,7 @@ def block_file(self, request, pk=None): for chunk in uploaded_block_file.chunks(): f.write(chunk) join_peers(envs, block_path) + os.remove(block_path) return Response(status=status.HTTP_202_ACCEPTED) except Exception as e: return Response( diff --git a/src/dashboard/src/locales/en-US/form.js b/src/dashboard/src/locales/en-US/form.js index ccde10695..6a4fdbb92 100755 --- a/src/dashboard/src/locales/en-US/form.js +++ b/src/dashboard/src/locales/en-US/form.js @@ -4,6 +4,7 @@ export default { 'form.button.new': 'New', 'form.table.header.operation': 'Operation', + 'form.menu.item.download': 'Download', 'form.menu.item.delete': 'Delete', 'form.input.placeholder': 'Please input', 'form.menu.item.update': 'Update', diff --git a/src/dashboard/src/locales/en-US/operatorNode.js b/src/dashboard/src/locales/en-US/operatorNode.js index bd082bf88..69618b369 100644 --- a/src/dashboard/src/locales/en-US/operatorNode.js +++ b/src/dashboard/src/locales/en-US/operatorNode.js @@ -22,6 +22,7 @@ export default { 'app.operator.node.delete.confirm': 'Deleting node {name} may cause abnormality in the blockchain network. Confirm delete?', 'app.operator.node.delete.success': 'Delete Node Successful.', + 'app.operator.node.download.success': 'Download Node Config File Successful.', 'app.operator.node.operation.start.success': 'Start Node Successful.', 'app.operator.node.operation.stop.success': 'Stop Node Successful.', 'app.operator.node.operation.restart.success': 'Restart Node Successful.', diff --git a/src/dashboard/src/locales/zh-CN/form.js b/src/dashboard/src/locales/zh-CN/form.js index e61548196..fd2c0ff2a 100755 --- a/src/dashboard/src/locales/zh-CN/form.js +++ b/src/dashboard/src/locales/zh-CN/form.js @@ -4,6 +4,7 @@ export default { 'form.button.new': '新建', 'form.table.header.operation': '操作', + 'form.menu.item.download': '下载', 'form.menu.item.delete': '删除', 'form.input.placeholder': '请输入', 'form.menu.item.update': '更新', diff --git a/src/dashboard/src/locales/zh-CN/operatorNode.js b/src/dashboard/src/locales/zh-CN/operatorNode.js index a9b0e8745..de721a360 100644 --- a/src/dashboard/src/locales/zh-CN/operatorNode.js +++ b/src/dashboard/src/locales/zh-CN/operatorNode.js @@ -21,6 +21,7 @@ export default { 'app.operator.node.delete.title': '删除节点', 'app.operator.node.delete.confirm': '删除节点 {name} 可能导致区块链网络异常,是否确认删除?', 'app.operator.node.delete.success': '删除节点成功。', + 'app.operator.node.download.success': '下载节点配置文件成功。', 'app.operator.node.operation.start.success': '启动节点成功。', 'app.operator.node.operation.stop.success': '停止节点成功。', 'app.operator.node.operation.restart.success': '重启节点成功。', diff --git a/src/dashboard/src/models/node.js b/src/dashboard/src/models/node.js index a83a9ca3f..e7d90ac25 100644 --- a/src/dashboard/src/models/node.js +++ b/src/dashboard/src/models/node.js @@ -5,6 +5,7 @@ import { deleteNode, operateNode, createNode, + downloadNodeConfig, } from '@/services/node'; export default { @@ -90,6 +91,12 @@ export default { }); } }, + *downloadNodeConfig({ payload, callback }, { call }) { + const response = yield call(downloadNodeConfig, payload); + if (callback) { + callback(response); + } + }, }, reducers: { save(state, { payload }) { diff --git a/src/dashboard/src/pages/Operator/Node/index.js b/src/dashboard/src/pages/Operator/Node/index.js index 66d0e4ff5..6ab0d0be4 100644 --- a/src/dashboard/src/pages/Operator/Node/index.js +++ b/src/dashboard/src/pages/Operator/Node/index.js @@ -27,6 +27,14 @@ import styles from '../styles.less'; const FormItem = Form.Item; const { Option } = Select; +// function str2bytes (str) { +// var bytes = new Uint8Array(str.length); +// for (var i=0; i { const { registerUserFormVisible, @@ -502,6 +510,36 @@ class Index extends PureComponent { }); }; + handleDownloadConfig = row => { + const { dispatch } = this.props; + const params = { + id: row.id, + }; + dispatch({ + type: 'node/downloadNodeConfig', + payload: params, + callback: this.downloadCallBack, + }); + }; + + downloadCallBack = response => { + const { intl } = this.props; + message.success( + intl.formatMessage({ + id: 'app.operator.node.download.success', + defaultMessage: 'Download Node Config File Successful.', + }) + ); + const dispositionHeader = response.response.headers.get('Content-Disposition'); + const blob = response.data; + const link = document.createElement('a'); + link.href = URL.createObjectURL(new Blob([blob], { type: 'application/zip' })); + link.download = dispositionHeader.split('filename=')[1]; + document.body.appendChild(link); + link.click(); + URL.revokeObjectURL(link.href); + }; + render() { const { selectedRows, registerUserFormVisible, targetNodeId, createModalVisible } = this.state; @@ -567,6 +605,13 @@ class Index extends PureComponent { )} + {(record.type === 'peer' || record.type === 'orderer') && ( + + this.handleDownloadConfig(record)}> + {intl.formatMessage({ id: 'form.menu.item.download', defaultMessage: 'Download' })} + + + )} this.handleDeleteNode(record)}> {intl.formatMessage({ id: 'form.menu.item.delete', defaultMessage: 'Delete' })} diff --git a/src/dashboard/src/services/node.js b/src/dashboard/src/services/node.js index 258a7f498..f0562295f 100644 --- a/src/dashboard/src/services/node.js +++ b/src/dashboard/src/services/node.js @@ -39,3 +39,11 @@ export async function operateNode(params) { data: { action: params.message }, }); } + +export async function downloadNodeConfig(params) { + return request(`/api/v1/nodes/${params.id}/config`, { + method: 'GET', + responseType: 'blob', + getResponse: true, + }); +}