fix issue

pull/410/head
vapao 2021-08-20 00:29:33 +08:00
parent 0dd2ef3df8
commit 083452dd90
6 changed files with 0 additions and 478 deletions

View File

@ -1,182 +0,0 @@
/**
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
* Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License.
*/
import React from 'react';
import { observer } from 'mobx-react';
import { CaretRightOutlined, LoadingOutlined, PlayCircleOutlined, SyncOutlined } from '@ant-design/icons';
import { Steps, Collapse, PageHeader, Spin, Tag, Button } from 'antd';
import { http, history, X_TOKEN } from 'libs';
import { AuthDiv } from 'components';
import OutView from './OutView';
import styles from './index.module.css';
import store from './store';
import lds from 'lodash';
@observer
class Ext1Index extends React.Component {
constructor(props) {
super(props);
this.timer = null;
this.id = props.match.params.id;
this.log = props.match.params.log;
this.state = {
fetching: true,
loading: false,
}
}
componentDidMount() {
this.fetch()
}
componentWillUnmount() {
if (this.socket) this.socket.close();
if (this.timer) clearTimeout(this.timer);
store.request = {targets: [], host_actions: [], server_actions: []};
store.outputs = {};
}
fetch = () => {
if (!this.timer) this.setState({fetching: true});
http.get(`/api/deploy/request/${this.id}/`, {params: {log: this.log}})
.then(res => {
store.request = res;
const outputs = {}
while (res.outputs.length) {
const msg = JSON.parse(res.outputs.pop());
if (!outputs.hasOwnProperty(msg.key)) {
const data = msg.key === 'local' ? ['读取数据... '] : [];
outputs[msg.key] = {data}
}
this._parse_message(msg, outputs)
}
store.outputs = outputs;
if (store.request.status === '2') {
this.timer = setTimeout(this.fetch, 2000)
} else {
this.timer = null
}
})
.finally(() => this.setState({fetching: false}))
};
_parse_message = (message, outputs) => {
outputs = outputs || store.outputs;
const {key, data, step, status} = message;
if (data !== undefined) {
outputs[key]['data'].push(data);
}
if (step !== undefined) outputs[key]['step'] = step;
if (status !== undefined) outputs[key]['status'] = status;
};
handleDeploy = () => {
this.setState({loading: true});
http.post(`/api/deploy/request/${this.id}/`)
.then(({token, outputs}) => {
store.request.status = '2';
store.outputs = outputs;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/?x-token=${X_TOKEN}`);
this.socket.onopen = () => {
this.socket.send('ok');
};
this.socket.onmessage = e => {
if (e.data === 'pong') {
this.socket.send('ping')
} else {
this._parse_message(JSON.parse(e.data))
}
}
})
.finally(() => this.setState({loading: false}))
};
getStatus = (key, n) => {
const step = lds.get(store.outputs, `${key}.step`, -1);
const isError = lds.get(store.outputs, `${key}.status`) === 'error';
const icon = <LoadingOutlined />;
if (n > step) {
return {key: n, status: 'wait'}
} else if (n === step) {
return isError ? {key: n, status: 'error'} : {key: n, status: 'process', icon}
} else {
return {key: n, status: 'finish'}
}
};
getStatusAlias = () => {
if (Object.keys(store.outputs).length !== 0) {
for (let item of [{id: 'local'}, ...store.request.targets]) {
if (lds.get(store.outputs, `${item.id}.status`) === 'error') {
return <Tag color="red">发布异常</Tag>
} else if (lds.get(store.outputs, `${item.id}.step`, -1) < 5) {
return <Tag color="orange">发布中</Tag>
}
}
return <Tag color="green">发布成功</Tag>
} else {
return <Tag color="blue">{store.request['status_alias'] || '...'}</Tag>
}
};
render() {
const {app_name, env_name, status} = store.request;
return (
<AuthDiv auth="deploy.request.do">
<Spin spinning={this.state.fetching}>
<PageHeader
title="应用发布"
subTitle={`${app_name} - ${env_name}`}
style={{padding: 0}}
tags={this.getStatusAlias()}
extra={this.log ? (
<Button icon={<SyncOutlined />} type="primary" onClick={this.fetch}>刷新</Button>
) : (
<Button icon={<PlayCircleOutlined />} loading={this.state.loading} type="primary"
disabled={!['1', '-3'].includes(status)}
onClick={this.handleDeploy}>发布</Button>
)}
onBack={() => history.goBack()}/>
<Collapse defaultActiveKey={1} className={styles.collapse}>
<Collapse.Panel showArrow={false} key={1} header={
<Steps>
<Steps.Step {...this.getStatus('local', 0)} title="建立连接"/>
<Steps.Step {...this.getStatus('local', 1)} title="发布准备"/>
<Steps.Step {...this.getStatus('local', 2)} title="检出前任务"/>
<Steps.Step {...this.getStatus('local', 3)} title="执行检出"/>
<Steps.Step {...this.getStatus('local', 4)} title="检出后任务"/>
<Steps.Step {...this.getStatus('local', 5)} title="执行打包"/>
</Steps>}>
<OutView id="local"/>
</Collapse.Panel>
</Collapse>
<Collapse
defaultActiveKey={'0'}
className={styles.collapse}
expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0} />}>
{store.request.targets.map((item, index) => (
<Collapse.Panel key={index} header={
<div style={{display: 'flex', justifyContent: 'space-between'}}>
<b>{item.title}</b>
<Steps size="small" style={{maxWidth: 600}}>
<Steps.Step {...this.getStatus(item.id, 1)} title="数据准备"/>
<Steps.Step {...this.getStatus(item.id, 2)} title="发布前任务"/>
<Steps.Step {...this.getStatus(item.id, 3)} title="执行发布"/>
<Steps.Step {...this.getStatus(item.id, 4)} title="发布后任务"/>
</Steps>
</div>}>
<OutView id={item.id}/>
</Collapse.Panel>
))}
</Collapse>
</Spin>
</AuthDiv>
)
}
}
export default Ext1Index

View File

@ -1,188 +0,0 @@
/**
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
* Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License.
*/
import React from 'react';
import { observer } from 'mobx-react';
import { CaretRightOutlined, LoadingOutlined, PlayCircleOutlined, SyncOutlined } from '@ant-design/icons';
import { Steps, Collapse, PageHeader, Spin, Tag, Button } from 'antd';
import { http, history, X_TOKEN } from 'libs';
import { AuthDiv } from 'components';
import OutView from './OutView';
import styles from './index.module.css';
import store from './store';
import lds from 'lodash';
@observer
class Ext1Index extends React.Component {
constructor(props) {
super(props);
this.timer = null;
this.id = props.match.params.id;
this.log = props.match.params.log;
this.state = {
fetching: true,
loading: false,
}
}
componentDidMount() {
this.fetch()
}
componentWillUnmount() {
if (this.socket) this.socket.close();
if (this.timer) clearTimeout(this.timer);
store.request = {targets: [], server_actions: [], host_actions: []};
store.outputs = {};
}
fetch = () => {
if (!this.timer) this.setState({fetching: true});
http.get(`/api/deploy/request/${this.id}/`, {params: {log: this.log}})
.then(res => {
store.request = res;
const outputs = {};
while (res.outputs.length) {
const msg = JSON.parse(res.outputs.pop());
if (!outputs.hasOwnProperty(msg.key)) {
const data = msg.key === 'local' ? ['读取数据... '] : [];
outputs[msg.key] = {data}
}
this._parse_message(msg, outputs)
}
store.outputs = outputs;
if (store.request.status === '2') {
this.timer = setTimeout(this.fetch, 2000)
} else {
this.timer = null
}
})
.finally(() => this.setState({fetching: false}))
};
_parse_message = (message, outputs) => {
outputs = outputs || store.outputs;
const {key, data, step, status} = message;
if (data !== undefined) {
outputs[key]['data'].push(data);
}
if (step !== undefined) outputs[key]['step'] = step;
if (status !== undefined) outputs[key]['status'] = status;
};
handleDeploy = () => {
this.setState({loading: true});
http.post(`/api/deploy/request/${this.id}/`)
.then(({token, outputs}) => {
store.request.status = '2';
store.outputs = outputs;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/?x-token=${X_TOKEN}`);
this.socket.onopen = () => {
this.socket.send('ok');
};
this.socket.onmessage = e => {
if (e.data === 'pong') {
this.socket.send('ping')
} else {
this._parse_message(JSON.parse(e.data))
}
}
})
.finally(() => this.setState({loading: false}))
};
getStatus = (key, n) => {
const step = lds.get(store.outputs, `${key}.step`, -1);
const isError = lds.get(store.outputs, `${key}.status`) === 'error';
const icon = <LoadingOutlined />;
if (n > step) {
return {key: n, status: 'wait'}
} else if (n === step) {
return isError ? {key: n, status: 'error'} : {key: n, status: 'process', icon}
} else {
return {key: n, status: 'finish'}
}
};
getStatusAlias = () => {
if (Object.keys(store.outputs).length !== 0) {
const {targets, host_actions} = store.request;
for (let item of [{id: 'local'}, ...(host_actions.length > 0 ? targets : [])]) {
if (lds.get(store.outputs, `${item.id}.status`) === 'error') {
return <Tag color="red">发布异常</Tag>
} else if (lds.get(store.outputs, `${item.id}.step`, -1) < 100) {
return <Tag color="orange">发布中</Tag>
}
}
return <Tag color="green">发布成功</Tag>
} else {
return <Tag color="blue">{store.request['status_alias'] || '...'}</Tag>
}
};
render() {
const {app_name, env_name, status, server_actions, host_actions} = store.request;
return (
<AuthDiv auth="deploy.request.do">
<Spin spinning={this.state.fetching}>
<PageHeader
title="应用发布"
subTitle={`${app_name} - ${env_name}`}
style={{padding: 0}}
tags={this.getStatusAlias()}
extra={this.log ? (
<Button icon={<SyncOutlined />} type="primary" onClick={this.fetch}>刷新</Button>
) : (
<Button icon={<PlayCircleOutlined />} loading={this.state.loading} type="primary"
disabled={!['1', '-3'].includes(status)}
onClick={this.handleDeploy}>发布</Button>
)}
onBack={() => history.goBack()}/>
<Collapse defaultActiveKey={1} className={styles.collapse}>
<Collapse.Panel showArrow={false} key={1} header={
<Steps style={{maxWidth: 400 + server_actions.length * 200}}>
<Steps.Step {...this.getStatus('local', 0)} title="建立连接"/>
<Steps.Step {...this.getStatus('local', 1)} title="发布准备"/>
{server_actions.map((item, index) => (
<Steps.Step {...this.getStatus('local', 2 + index)} key={index} title={item.title}/>
))}
</Steps>}>
<OutView id="local"/>
</Collapse.Panel>
</Collapse>
{host_actions.length > 0 && (
<Collapse
defaultActiveKey={'0'}
className={styles.collapse}
expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0} />}>
{store.request.targets.map((item, index) => (
<Collapse.Panel key={index} header={
<div style={{display: 'flex', justifyContent: 'space-between'}}>
<b>{item.title}</b>
<Steps size="small" style={{maxWidth: 150 + host_actions.length * 150}}>
<Steps.Step {...this.getStatus(item.id, 1)} title="数据准备"/>
{host_actions.map((action, index) => (
<Steps.Step {...this.getStatus(item.id, 2 + index)} key={index} title={action.title}/>
))}
</Steps>
</div>}>
<OutView id={item.id}/>
</Collapse.Panel>
))}
</Collapse>
)}
{host_actions.length === 0 && this.state.fetching === false && (
<div className={styles.ext2Tips}>无目标主机动作</div>
)}
</Spin>
</AuthDiv>
)
}
}
export default Ext1Index

View File

@ -1,36 +0,0 @@
/**
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
* Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License.
*/
import React from 'react';
import { toJS } from 'mobx';
import { observer } from 'mobx-react';
import styles from './index.module.css';
import store from './store';
import lds from 'lodash';
@observer
class OutView extends React.Component {
constructor(props) {
super(props);
this.el = null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
setTimeout(() => {
if (this.el) this.el.scrollTop = this.el.scrollHeight
}, 100)
}
render() {
const outputs = lds.get(store.outputs, `${this.props.id}.data`, []);
return (
<pre ref={el => this.el = el} className={styles.ext1Console}>
{toJS(outputs)}
</pre>
)
}
}
export default OutView

View File

@ -1,49 +0,0 @@
.header {
display: flex;
}
.collapse {
margin-top: 16px;
}
.collapse :global(.ant-collapse-content-box) {
padding: 0;
}
.ext1Console {
min-height: 40px;
max-height: 300px;
padding: 10px 15px;
}
.ext2Block {
display: flex;
background-color: #fff;
margin-top: 16px;
border-radius: 4px;
border: 1px solid #d9d9d9;
}
.ext2Console {
flex: 1;
padding: 30px;
}
.ext2Step {
padding: 24px;
width: 220px;
border-right: 1px solid #e8e8e8;
}
.ext2Step :global(.ant-steps-item) {
height: 100px;
}
.ext2Tips {
color: #888;
margin-top: 30px;
}
pre {
margin: 0;
}

View File

@ -1,17 +0,0 @@
/**
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
* Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License.
*/
import { observable } from "mobx";
class Store {
@observable outputs = {};
@observable request = {
targets: [],
host_actions: [],
server_actions: []
};
}
export default new Store()

View File

@ -25,8 +25,6 @@ import ExecTemplate from './pages/exec/template';
import DeployApp from './pages/deploy/app';
import DeployRepository from './pages/deploy/repository';
import DeployRequest from './pages/deploy/request';
import DoExt1Index from './pages/deploy/do/Ext1Index';
import DoExt2Index from './pages/deploy/do/Ext2Index';
import ScheduleIndex from './pages/schedule';
import ConfigEnvironment from './pages/config/environment';
import ConfigService from './pages/config/service';
@ -63,10 +61,6 @@ export default [
{title: '应用管理', auth: 'deploy.app.view', path: '/deploy/app', component: DeployApp},
{title: '构建仓库', auth: 'deploy.repository.view', path: '/deploy/repository', component: DeployRepository},
{title: '发布申请', auth: 'deploy.request.view', path: '/deploy/request', component: DeployRequest},
{path: '/deploy/do/ext1/:id', component: DoExt1Index},
{path: '/deploy/do/ext2/:id', component: DoExt2Index},
{path: '/deploy/do/ext1/:id/:log', component: DoExt1Index},
{path: '/deploy/do/ext2/:id/:log', component: DoExt2Index},
]
},
{