mirror of https://github.com/openspug/spug
fix issue
parent
4e900006be
commit
80c6e3d498
|
@ -47,7 +47,7 @@ class DeployRequest(models.Model, ModelMixin):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_quick_deploy(self):
|
def is_quick_deploy(self):
|
||||||
if self.deploy.extend == '1' and self.extra:
|
if self.type == '1' and self.deploy.extend == '1' and self.extra:
|
||||||
extra = json.loads(self.extra)
|
extra = json.loads(self.extra)
|
||||||
return extra[0] in ('branch', 'tag')
|
return extra[0] in ('branch', 'tag')
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -7,6 +7,7 @@ from .views import *
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('request/', RequestView.as_view()),
|
path('request/', RequestView.as_view()),
|
||||||
|
path('request/info/', get_request_info),
|
||||||
path('request/ext1/', post_request_ext1),
|
path('request/ext1/', post_request_ext1),
|
||||||
path('request/ext1/rollback/', post_request_ext1_rollback),
|
path('request/ext1/rollback/', post_request_ext1_rollback),
|
||||||
path('request/ext2/', post_request_ext2),
|
path('request/ext2/', post_request_ext2),
|
||||||
|
|
|
@ -173,25 +173,28 @@ def _deploy_ext1_host(req, helper, h_id, env):
|
||||||
f'mkdir -p {extend.dst_repo} && [ -e {extend.dst_dir} ] && [ ! -L {extend.dst_dir} ]')
|
f'mkdir -p {extend.dst_repo} && [ -e {extend.dst_dir} ] && [ ! -L {extend.dst_dir} ]')
|
||||||
if code == 0:
|
if code == 0:
|
||||||
helper.send_error(host.id, f'检测到该主机的发布目录 {extend.dst_dir!r} 已存在,为了数据安全请自行备份后删除该目录,Spug 将会创建并接管该目录。')
|
helper.send_error(host.id, f'检测到该主机的发布目录 {extend.dst_dir!r} 已存在,为了数据安全请自行备份后删除该目录,Spug 将会创建并接管该目录。')
|
||||||
# clean
|
if req.type == '1':
|
||||||
clean_command = f'ls -d {extend.deploy_id}_* 2> /dev/null | sort -t _ -rnk2 | tail -n +{extend.versions + 1} | xargs rm -rf'
|
# clean
|
||||||
helper.remote_raw(host.id, ssh, f'cd {extend.dst_repo} && {clean_command}')
|
clean_command = f'ls -d {extend.deploy_id}_* 2> /dev/null | sort -t _ -rnk2 | tail -n +{extend.versions + 1} | xargs rm -rf'
|
||||||
# transfer files
|
helper.remote_raw(host.id, ssh, f'cd {extend.dst_repo} && {clean_command}')
|
||||||
tar_gz_file = f'{req.spug_version}.tar.gz'
|
# transfer files
|
||||||
try:
|
tar_gz_file = f'{req.spug_version}.tar.gz'
|
||||||
ssh.put_file(os.path.join(REPOS_DIR, 'build', tar_gz_file), os.path.join(extend.dst_repo, tar_gz_file))
|
try:
|
||||||
except Exception as e:
|
ssh.put_file(os.path.join(REPOS_DIR, 'build', tar_gz_file), os.path.join(extend.dst_repo, tar_gz_file))
|
||||||
helper.send_error(host.id, f'Exception: {e}')
|
except Exception as e:
|
||||||
|
helper.send_error(host.id, f'Exception: {e}')
|
||||||
|
|
||||||
command = f'cd {extend.dst_repo} && rm -rf {req.spug_version} && tar xf {tar_gz_file} && rm -f {req.deploy_id}_*.tar.gz'
|
command = f'cd {extend.dst_repo} && rm -rf {req.spug_version} && tar xf {tar_gz_file} && rm -f {req.deploy_id}_*.tar.gz'
|
||||||
helper.remote_raw(host.id, ssh, command)
|
helper.remote_raw(host.id, ssh, command)
|
||||||
helper.send_step(h_id, 1, '\033[32m完成√\033[0m\r\n')
|
helper.send_step(h_id, 1, '\033[32m完成√\033[0m\r\n')
|
||||||
|
else:
|
||||||
|
helper.send_step(h_id, 1, '\033[33m跳过√\033[0m\r\n')
|
||||||
|
|
||||||
# pre host
|
# pre host
|
||||||
repo_dir = os.path.join(extend.dst_repo, req.spug_version)
|
repo_dir = os.path.join(extend.dst_repo, req.spug_version)
|
||||||
if extend.hook_pre_host:
|
if extend.hook_pre_host:
|
||||||
helper.send_step(h_id, 2, f'{human_time()} 发布前任务... \r\n')
|
helper.send_step(h_id, 2, f'{human_time()} 发布前任务... \r\n')
|
||||||
command = f'cd {repo_dir} ; {extend.hook_pre_host}'
|
command = f'cd {repo_dir} && {extend.hook_pre_host}'
|
||||||
helper.remote(host.id, ssh, command)
|
helper.remote(host.id, ssh, command)
|
||||||
|
|
||||||
# do deploy
|
# do deploy
|
||||||
|
@ -202,7 +205,7 @@ def _deploy_ext1_host(req, helper, h_id, env):
|
||||||
# post host
|
# post host
|
||||||
if extend.hook_post_host:
|
if extend.hook_post_host:
|
||||||
helper.send_step(h_id, 4, f'{human_time()} 发布后任务... \r\n')
|
helper.send_step(h_id, 4, f'{human_time()} 发布后任务... \r\n')
|
||||||
command = f'cd {extend.dst_dir} ; {extend.hook_post_host}'
|
command = f'cd {extend.dst_dir} && {extend.hook_post_host}'
|
||||||
helper.remote(host.id, ssh, command)
|
helper.remote(host.id, ssh, command)
|
||||||
|
|
||||||
helper.send_step(h_id, 100, f'\r\n{human_time()} ** \033[32m发布成功\033[0m **')
|
helper.send_step(h_id, 100, f'\r\n{human_time()} ** \033[32m发布成功\033[0m **')
|
||||||
|
@ -237,7 +240,7 @@ def _deploy_ext2_host(helper, h_id, actions, env, spug_version):
|
||||||
command += f'&& rm -rf {action["dst"]} && mv /tmp/{spug_version}/{sd_dst} {action["dst"]} '
|
command += f'&& rm -rf {action["dst"]} && mv /tmp/{spug_version}/{sd_dst} {action["dst"]} '
|
||||||
command += f'&& rm -rf /tmp/{spug_version}* && echo "transfer completed"'
|
command += f'&& rm -rf /tmp/{spug_version}* && echo "transfer completed"'
|
||||||
else:
|
else:
|
||||||
command = f'cd /tmp ; {action["data"]}'
|
command = f'cd /tmp && {action["data"]}'
|
||||||
helper.remote(host.id, ssh, command)
|
helper.remote(host.id, ssh, command)
|
||||||
|
|
||||||
helper.send_step(h_id, 100, f'\r\n{human_time()} ** \033[32m发布成功\033[0m **')
|
helper.send_step(h_id, 100, f'\r\n{human_time()} ** \033[32m发布成功\033[0m **')
|
||||||
|
|
|
@ -334,6 +334,18 @@ def post_request_ext2(request):
|
||||||
return json_response(error=error)
|
return json_response(error=error)
|
||||||
|
|
||||||
|
|
||||||
|
def get_request_info(request):
|
||||||
|
form, error = JsonParser(
|
||||||
|
Argument('id', type=int, help='参数错误')
|
||||||
|
).parse(request.GET)
|
||||||
|
if error is None:
|
||||||
|
req = DeployRequest.objects.get(pk=form.id)
|
||||||
|
response = req.to_dict(selects=('status', 'reason'))
|
||||||
|
response['status_alias'] = req.get_status_display()
|
||||||
|
return json_response(response)
|
||||||
|
return json_response(error=error)
|
||||||
|
|
||||||
|
|
||||||
def do_upload(request):
|
def do_upload(request):
|
||||||
repos_dir = settings.REPOS_DIR
|
repos_dir = settings.REPOS_DIR
|
||||||
file = request.FILES['file']
|
file = request.FILES['file']
|
||||||
|
|
|
@ -15,6 +15,8 @@ import store from './store';
|
||||||
function Ext1Console(props) {
|
function Ext1Console(props) {
|
||||||
const outputs = useLocalStore(() => ({}));
|
const outputs = useLocalStore(() => ({}));
|
||||||
const terms = useLocalStore(() => ({}));
|
const terms = useLocalStore(() => ({}));
|
||||||
|
const [mini, setMini] = useState(false);
|
||||||
|
const [visible, setVisible] = useState(true);
|
||||||
const [fetching, setFetching] = useState(true);
|
const [fetching, setFetching] = useState(true);
|
||||||
|
|
||||||
useEffect(props.request.mode === 'read' ? readDeploy : doDeploy, [])
|
useEffect(props.request.mode === 'read' ? readDeploy : doDeploy, [])
|
||||||
|
@ -39,7 +41,7 @@ function Ext1Console(props) {
|
||||||
Object.assign(outputs, res.outputs)
|
Object.assign(outputs, res.outputs)
|
||||||
setTimeout(() => setFetching(false), 100)
|
setTimeout(() => setFetching(false), 100)
|
||||||
socket = _makeSocket()
|
socket = _makeSocket()
|
||||||
store.fetchRecords()
|
store.fetchInfo(props.request.id)
|
||||||
})
|
})
|
||||||
return () => socket && socket.close()
|
return () => socket && socket.close()
|
||||||
}
|
}
|
||||||
|
@ -85,8 +87,8 @@ function Ext1Console(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchMiniMode() {
|
function switchMiniMode() {
|
||||||
const value = store.tabModes[props.request.id];
|
setMini(true)
|
||||||
store.tabModes[props.request.id] = !value
|
setVisible(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSetTerm(term, key) {
|
function handleSetTerm(term, key) {
|
||||||
|
@ -97,86 +99,90 @@ function Ext1Console(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let {local, ...hosts} = outputs;
|
let {local, ...hosts} = outputs;
|
||||||
return store.tabModes[props.request.id] ? (
|
return (
|
||||||
<Card
|
<div>
|
||||||
className={styles.item}
|
{mini && (
|
||||||
bodyStyle={{padding: '8px 12px'}}
|
<Card
|
||||||
onClick={switchMiniMode}>
|
className={styles.item}
|
||||||
<div className={styles.header}>
|
bodyStyle={{padding: '8px 12px'}}
|
||||||
<div className={styles.title}>{props.request.name}</div>
|
onClick={() => setVisible(true)}>
|
||||||
<CloseOutlined onClick={() => store.showConsole(props.request, true)}/>
|
<div className={styles.header}>
|
||||||
</div>
|
<div className={styles.title}>{props.request.name}</div>
|
||||||
{local && (
|
<CloseOutlined onClick={() => store.showConsole(props.request, true)}/>
|
||||||
<Progress
|
</div>
|
||||||
percent={(local.step + 1) * 18}
|
{local && (
|
||||||
status={local.step === 100 ? 'success' : outputs.local.status === 'error' ? 'exception' : 'active'}/>
|
<Progress
|
||||||
)}
|
percent={(local.step + 1) * 18}
|
||||||
{Object.values(hosts).map(item => (
|
status={local.step === 100 ? 'success' : outputs.local.status === 'error' ? 'exception' : 'active'}/>
|
||||||
<Progress
|
)}
|
||||||
key={item.id}
|
{Object.values(hosts).map(item => (
|
||||||
percent={(item.step + 1) * 18}
|
<Progress
|
||||||
status={item.step === 100 ? 'success' : item.status === 'error' ? 'exception' : 'active'}/>
|
key={item.id}
|
||||||
))}
|
percent={(item.step + 1) * 18}
|
||||||
</Card>
|
status={item.step === 100 ? 'success' : item.status === 'error' ? 'exception' : 'active'}/>
|
||||||
) : (
|
|
||||||
<Modal
|
|
||||||
visible
|
|
||||||
width={1000}
|
|
||||||
footer={null}
|
|
||||||
maskClosable={false}
|
|
||||||
className={styles.console}
|
|
||||||
onCancel={() => store.showConsole(props.request, true)}
|
|
||||||
title={[
|
|
||||||
<span key="1">{props.request.name}</span>,
|
|
||||||
<div key="2" className={styles.miniIcon} onClick={switchMiniMode}>
|
|
||||||
<ShrinkOutlined/>
|
|
||||||
</div>
|
|
||||||
]}>
|
|
||||||
<Skeleton loading={fetching} active>
|
|
||||||
{local && (
|
|
||||||
<Collapse defaultActiveKey={['0']} className={styles.collapse} style={{marginBottom: 24}}>
|
|
||||||
<Collapse.Panel header={(
|
|
||||||
<div className={styles.header}>
|
|
||||||
<b className={styles.title}/>
|
|
||||||
<Steps size="small" className={styles.step} current={local.step} status={local.status}>
|
|
||||||
<StepItem title="构建准备" item={local} step={0}/>
|
|
||||||
<StepItem title="检出前任务" item={local} step={1}/>
|
|
||||||
<StepItem title="执行检出" item={local} step={2}/>
|
|
||||||
<StepItem title="检出后任务" item={local} step={3}/>
|
|
||||||
<StepItem title="执行打包" item={local} step={4}/>
|
|
||||||
</Steps>
|
|
||||||
</div>
|
|
||||||
)}>
|
|
||||||
<OutView setTerm={term => handleSetTerm(term, 'local')}/>
|
|
||||||
</Collapse.Panel>
|
|
||||||
</Collapse>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Collapse
|
|
||||||
defaultActiveKey="0"
|
|
||||||
className={styles.collapse}
|
|
||||||
expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}>
|
|
||||||
{Object.entries(hosts).map(([key, item], index) => (
|
|
||||||
<Collapse.Panel
|
|
||||||
key={index}
|
|
||||||
header={
|
|
||||||
<div className={styles.header}>
|
|
||||||
<b className={styles.title}>{item.title}</b>
|
|
||||||
<Steps size="small" className={styles.step} current={item.step} status={item.status}>
|
|
||||||
<StepItem title="等待调度" item={item} step={0}/>
|
|
||||||
<StepItem title="数据准备" item={item} step={1}/>
|
|
||||||
<StepItem title="发布前任务" item={item} step={2}/>
|
|
||||||
<StepItem title="执行发布" item={item} step={3}/>
|
|
||||||
<StepItem title="发布后任务" item={item} step={4}/>
|
|
||||||
</Steps>
|
|
||||||
</div>}>
|
|
||||||
<OutView setTerm={term => handleSetTerm(term, key)}/>
|
|
||||||
</Collapse.Panel>
|
|
||||||
))}
|
))}
|
||||||
</Collapse>
|
</Card>
|
||||||
</Skeleton>
|
)}
|
||||||
</Modal>
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
width={1000}
|
||||||
|
footer={null}
|
||||||
|
maskClosable={false}
|
||||||
|
className={styles.console}
|
||||||
|
onCancel={() => store.showConsole(props.request, true)}
|
||||||
|
title={[
|
||||||
|
<span key="1">{props.request.name}</span>,
|
||||||
|
<div key="2" className={styles.miniIcon} onClick={switchMiniMode}>
|
||||||
|
<ShrinkOutlined/>
|
||||||
|
</div>
|
||||||
|
]}>
|
||||||
|
<Skeleton loading={fetching} active>
|
||||||
|
{local && (
|
||||||
|
<Collapse defaultActiveKey={['0']} className={styles.collapse} style={{marginBottom: 24}}>
|
||||||
|
<Collapse.Panel header={(
|
||||||
|
<div className={styles.header}>
|
||||||
|
<b className={styles.title}/>
|
||||||
|
<Steps size="small" className={styles.step} current={local.step} status={local.status}>
|
||||||
|
<StepItem title="构建准备" item={local} step={0}/>
|
||||||
|
<StepItem title="检出前任务" item={local} step={1}/>
|
||||||
|
<StepItem title="执行检出" item={local} step={2}/>
|
||||||
|
<StepItem title="检出后任务" item={local} step={3}/>
|
||||||
|
<StepItem title="执行打包" item={local} step={4}/>
|
||||||
|
</Steps>
|
||||||
|
</div>
|
||||||
|
)}>
|
||||||
|
<OutView setTerm={term => handleSetTerm(term, 'local')}/>
|
||||||
|
</Collapse.Panel>
|
||||||
|
</Collapse>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Collapse
|
||||||
|
defaultActiveKey="0"
|
||||||
|
className={styles.collapse}
|
||||||
|
expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}>
|
||||||
|
{Object.entries(hosts).map(([key, item], index) => (
|
||||||
|
<Collapse.Panel
|
||||||
|
key={index}
|
||||||
|
header={
|
||||||
|
<div className={styles.header}>
|
||||||
|
<b className={styles.title}>{item.title}</b>
|
||||||
|
<Steps size="small" className={styles.step} current={item.step} status={item.status}>
|
||||||
|
<StepItem title="等待调度" item={item} step={0}/>
|
||||||
|
<StepItem title="数据准备" item={item} step={1}/>
|
||||||
|
<StepItem title="发布前任务" item={item} step={2}/>
|
||||||
|
<StepItem title="执行发布" item={item} step={3}/>
|
||||||
|
<StepItem title="发布后任务" item={item} step={4}/>
|
||||||
|
</Steps>
|
||||||
|
</div>}>
|
||||||
|
<OutView setTerm={term => handleSetTerm(term, key)}/>
|
||||||
|
</Collapse.Panel>
|
||||||
|
))}
|
||||||
|
</Collapse>
|
||||||
|
</Skeleton>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default observer(Ext1Console)
|
export default observer(Ext1Console)
|
|
@ -17,6 +17,8 @@ function Ext2Console(props) {
|
||||||
const outputs = useLocalStore(() => ({local: {id: 'local'}}));
|
const outputs = useLocalStore(() => ({local: {id: 'local'}}));
|
||||||
const [sActions, setSActions] = useState([]);
|
const [sActions, setSActions] = useState([]);
|
||||||
const [hActions, setHActions] = useState([]);
|
const [hActions, setHActions] = useState([]);
|
||||||
|
const [mini, setMini] = useState(false);
|
||||||
|
const [visible, setVisible] = useState(true);
|
||||||
const [fetching, setFetching] = useState(true);
|
const [fetching, setFetching] = useState(true);
|
||||||
|
|
||||||
useEffect(props.request.mode === 'read' ? readDeploy : doDeploy, [])
|
useEffect(props.request.mode === 'read' ? readDeploy : doDeploy, [])
|
||||||
|
@ -45,6 +47,7 @@ function Ext2Console(props) {
|
||||||
Object.assign(outputs, res.outputs)
|
Object.assign(outputs, res.outputs)
|
||||||
setTimeout(() => setFetching(false), 100)
|
setTimeout(() => setFetching(false), 100)
|
||||||
socket = _makeSocket()
|
socket = _makeSocket()
|
||||||
|
store.fetchInfo(props.request.id)
|
||||||
})
|
})
|
||||||
return () => socket && socket.close()
|
return () => socket && socket.close()
|
||||||
}
|
}
|
||||||
|
@ -92,8 +95,8 @@ function Ext2Console(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchMiniMode() {
|
function switchMiniMode() {
|
||||||
const value = store.tabModes[props.request.id];
|
setMini(true)
|
||||||
store.tabModes[props.request.id] = !value
|
setVisible(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSetTerm(term, key) {
|
function handleSetTerm(term, key) {
|
||||||
|
@ -104,81 +107,84 @@ function Ext2Console(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const hostOutputs = Object.values(outputs).filter(x => x.id !== 'local');
|
const hostOutputs = Object.values(outputs).filter(x => x.id !== 'local');
|
||||||
return store.tabModes[props.request.id] ? (
|
return (
|
||||||
<Card
|
<div>
|
||||||
className={styles.item}
|
{mini && (
|
||||||
bodyStyle={{padding: '8px 12px'}}
|
<Card
|
||||||
onClick={switchMiniMode}>
|
className={styles.item}
|
||||||
<div className={styles.header}>
|
bodyStyle={{padding: '8px 12px'}}
|
||||||
<div className={styles.title}>{props.request.name}</div>
|
onClick={() => setVisible(true)}>
|
||||||
<CloseOutlined onClick={() => store.showConsole(props.request, true)}/>
|
<div className={styles.header}>
|
||||||
</div>
|
<div className={styles.title}>{props.request.name}</div>
|
||||||
<Progress percent={(outputs.local.step + 1) * (90 / (1 + sActions.length)).toFixed(0)}
|
<CloseOutlined onClick={() => store.showConsole(props.request, true)}/>
|
||||||
status={outputs.local.step === 100 ? 'success' : outputs.local.status === 'error' ? 'exception' : 'active'}/>
|
</div>
|
||||||
{Object.values(outputs).filter(x => x.id !== 'local').map(item => (
|
<Progress percent={(outputs.local.step + 1) * (90 / (1 + sActions.length)).toFixed(0)}
|
||||||
<Progress
|
status={outputs.local.step === 100 ? 'success' : outputs.local.status === 'error' ? 'exception' : 'active'}/>
|
||||||
key={item.id}
|
{Object.values(outputs).filter(x => x.id !== 'local').map(item => (
|
||||||
percent={item.step * (90 / (hActions.length).toFixed(0))}
|
<Progress
|
||||||
status={item.step === 100 ? 'success' : item.status === 'error' ? 'exception' : 'active'}/>
|
key={item.id}
|
||||||
))}
|
percent={item.step * (90 / (hActions.length).toFixed(0))}
|
||||||
</Card>
|
status={item.step === 100 ? 'success' : item.status === 'error' ? 'exception' : 'active'}/>
|
||||||
) : (
|
))}
|
||||||
<Modal
|
</Card>
|
||||||
visible
|
)}
|
||||||
width={1000}
|
<Modal
|
||||||
footer={null}
|
visible={visible}
|
||||||
maskClosable={false}
|
width={1000}
|
||||||
className={styles.console}
|
footer={null}
|
||||||
onCancel={() => store.showConsole(props.request, true)}
|
maskClosable={false}
|
||||||
title={[
|
className={styles.console}
|
||||||
<span key="1">{props.request.name}</span>,
|
onCancel={() => store.showConsole(props.request, true)}
|
||||||
<div key="2" className={styles.miniIcon} onClick={switchMiniMode}>
|
title={[
|
||||||
<ShrinkOutlined/>
|
<span key="1">{props.request.name}</span>,
|
||||||
</div>
|
<div key="2" className={styles.miniIcon} onClick={switchMiniMode}>
|
||||||
]}>
|
<ShrinkOutlined/>
|
||||||
<Skeleton loading={fetching} active>
|
</div>
|
||||||
<Collapse defaultActiveKey={['0']} className={styles.collapse}>
|
]}>
|
||||||
<Collapse.Panel header={(
|
<Skeleton loading={fetching} active>
|
||||||
<div className={styles.header}>
|
<Collapse defaultActiveKey={['0']} className={styles.collapse}>
|
||||||
<b className={styles.title}/>
|
<Collapse.Panel header={(
|
||||||
<Steps size="small" className={styles.step} current={outputs.local.step} status={outputs.local.status}>
|
<div className={styles.header}>
|
||||||
<StepItem title="建立连接" item={outputs.local} step={0}/>
|
<b className={styles.title}/>
|
||||||
{sActions.map((item, index) => (
|
<Steps size="small" className={styles.step} current={outputs.local.step} status={outputs.local.status}>
|
||||||
<StepItem key={index} title={item.title} item={outputs.local} step={index + 1}/>
|
<StepItem title="建立连接" item={outputs.local} step={0}/>
|
||||||
))}
|
{sActions.map((item, index) => (
|
||||||
</Steps>
|
<StepItem key={index} title={item.title} item={outputs.local} step={index + 1}/>
|
||||||
</div>
|
))}
|
||||||
)}>
|
</Steps>
|
||||||
<OutView setTerm={term => handleSetTerm(term, 'local')}/>
|
</div>
|
||||||
</Collapse.Panel>
|
)}>
|
||||||
</Collapse>
|
<OutView setTerm={term => handleSetTerm(term, 'local')}/>
|
||||||
{hostOutputs.length > 0 && (
|
</Collapse.Panel>
|
||||||
<Collapse
|
|
||||||
accordion
|
|
||||||
defaultActiveKey="0"
|
|
||||||
className={styles.collapse}
|
|
||||||
style={{marginTop: 24}}
|
|
||||||
expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}>
|
|
||||||
{hostOutputs.map((item, index) => (
|
|
||||||
<Collapse.Panel
|
|
||||||
key={index}
|
|
||||||
header={
|
|
||||||
<div className={styles.header}>
|
|
||||||
<b className={styles.title}>{item.title}</b>
|
|
||||||
<Steps size="small" className={styles.step} current={item.step} status={item.status}>
|
|
||||||
<StepItem title="等待调度" item={item} step={0}/>
|
|
||||||
{hActions.map((action, index) => (
|
|
||||||
<StepItem key={index} title={action.title} item={item} step={index + 1}/>
|
|
||||||
))}
|
|
||||||
</Steps>
|
|
||||||
</div>}>
|
|
||||||
<OutView setTerm={term => handleSetTerm(term, item.id)}/>
|
|
||||||
</Collapse.Panel>
|
|
||||||
))}
|
|
||||||
</Collapse>
|
</Collapse>
|
||||||
)}
|
{hostOutputs.length > 0 && (
|
||||||
</Skeleton>
|
<Collapse
|
||||||
</Modal>
|
accordion
|
||||||
|
defaultActiveKey="0"
|
||||||
|
className={styles.collapse}
|
||||||
|
style={{marginTop: 24}}
|
||||||
|
expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}>
|
||||||
|
{hostOutputs.map((item, index) => (
|
||||||
|
<Collapse.Panel
|
||||||
|
key={index}
|
||||||
|
header={
|
||||||
|
<div className={styles.header}>
|
||||||
|
<b className={styles.title}>{item.title}</b>
|
||||||
|
<Steps size="small" className={styles.step} current={item.step} status={item.status}>
|
||||||
|
<StepItem title="等待调度" item={item} step={0}/>
|
||||||
|
{hActions.map((action, index) => (
|
||||||
|
<StepItem key={index} title={action.title} item={item} step={index + 1}/>
|
||||||
|
))}
|
||||||
|
</Steps>
|
||||||
|
</div>}>
|
||||||
|
<OutView setTerm={term => handleSetTerm(term, item.id)}/>
|
||||||
|
</Collapse.Panel>
|
||||||
|
))}
|
||||||
|
</Collapse>
|
||||||
|
)}
|
||||||
|
</Skeleton>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { BranchesOutlined, BuildOutlined, TagOutlined, PlusOutlined, TagsOutline
|
||||||
import { Radio, Modal, Popover, Tag, Popconfirm, Tooltip, message } from 'antd';
|
import { Radio, Modal, Popover, Tag, Popconfirm, Tooltip, message } from 'antd';
|
||||||
import { http, hasPermission } from 'libs';
|
import { http, hasPermission } from 'libs';
|
||||||
import { Action, AuthButton, TableCard } from 'components';
|
import { Action, AuthButton, TableCard } from 'components';
|
||||||
import styles from './index.module.less';
|
|
||||||
import store from './store';
|
import store from './store';
|
||||||
|
|
||||||
function ComTable() {
|
function ComTable() {
|
||||||
|
@ -146,18 +145,7 @@ function ComTable() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeploy(e, info) {
|
function handleDeploy(e, info) {
|
||||||
const right = document.body.clientWidth - 25 - e.target.getBoundingClientRect().x;
|
store.showConsole(info);
|
||||||
const bottom = document.body.clientHeight - 40 - e.target.getBoundingClientRect().y;
|
|
||||||
store.box.setAttribute('style', `display: block; bottom: ${bottom}px; right: ${right}px;`);
|
|
||||||
setTimeout(() => {
|
|
||||||
store.box.setAttribute('class', `${styles.floatBox} ${styles.floatBoxAnimate}`)
|
|
||||||
}, 10);
|
|
||||||
setTimeout(() => {
|
|
||||||
store.showConsole(info);
|
|
||||||
store.box.setAttribute('style', 'display: none');
|
|
||||||
store.box.setAttribute('class', styles.floatBox)
|
|
||||||
}, 300)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -90,16 +90,14 @@ function Index() {
|
||||||
{store.rollbackVisible && <Rollback/>}
|
{store.rollbackVisible && <Rollback/>}
|
||||||
{store.tabs.length > 0 && (
|
{store.tabs.length > 0 && (
|
||||||
<Space className={styles.miniConsole}>
|
<Space className={styles.miniConsole}>
|
||||||
{store.tabs.map(item => (
|
{store.tabs.map(item => item.id ?
|
||||||
item.app_extend === '1' ? (
|
item.app_extend === '1' ? (
|
||||||
<Ext1Console key={item.id} request={item}/>
|
<Ext1Console key={item.id} request={item}/>
|
||||||
) : (
|
) : (
|
||||||
<Ext2Console key={item.id} request={item}/>
|
<Ext2Console key={item.id} request={item}/>
|
||||||
)
|
) : null)}
|
||||||
))}
|
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
<div ref={el => store.box = el} id='floatBox' className={styles.floatBox}/>
|
|
||||||
</AuthDiv>
|
</AuthDiv>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,20 +101,3 @@
|
||||||
.upload :global(.ant-upload-list-item) {
|
.upload :global(.ant-upload-list-item) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floatBox {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
width: 20px;
|
|
||||||
height: 10px;
|
|
||||||
transition: right 0.3s, width 0.3s, height0 .3s, bottom 0.3s ease-in-out;
|
|
||||||
background: rgba(0, 0, 0, 0.1);
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floatBoxAnimate {
|
|
||||||
width: 180px;
|
|
||||||
height: 64px;
|
|
||||||
bottom: 12px !important;
|
|
||||||
right: 24px !important;
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,9 +12,7 @@ class Store {
|
||||||
@observable records = [];
|
@observable records = [];
|
||||||
@observable record = {};
|
@observable record = {};
|
||||||
@observable counter = {};
|
@observable counter = {};
|
||||||
@observable box = null;
|
|
||||||
@observable tabs = [];
|
@observable tabs = [];
|
||||||
@observable tabModes = {};
|
|
||||||
@observable isFetching = false;
|
@observable isFetching = false;
|
||||||
@observable addVisible = false;
|
@observable addVisible = false;
|
||||||
@observable ext1Visible = false;
|
@observable ext1Visible = false;
|
||||||
|
@ -55,6 +53,19 @@ class Store {
|
||||||
.finally(() => this.isFetching = false)
|
.finally(() => this.isFetching = false)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fetchInfo = (id) => {
|
||||||
|
http.get('/api/deploy/request/info/', {params: {id}})
|
||||||
|
.then(res => {
|
||||||
|
for (let item of this.records) {
|
||||||
|
if (item.id === id) {
|
||||||
|
Object.assign(item, res)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(this._updateCounter)
|
||||||
|
}
|
||||||
|
|
||||||
_updateCounter = () => {
|
_updateCounter = () => {
|
||||||
const counter = {'all': 0, '-3': 0, '0': 0, '1': 0, '3': 0, '99': 0};
|
const counter = {'all': 0, '-3': 0, '0': 0, '1': 0, '3': 0, '99': 0};
|
||||||
for (let item of this.records) {
|
for (let item of this.records) {
|
||||||
|
@ -122,18 +133,15 @@ class Store {
|
||||||
const index = lds.findIndex(this.tabs, x => x.id === info.id);
|
const index = lds.findIndex(this.tabs, x => x.id === info.id);
|
||||||
if (isClose) {
|
if (isClose) {
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.tabs.splice(index, 1)
|
this.tabs[index] = {}
|
||||||
delete this.tabModes[info.id]
|
|
||||||
}
|
}
|
||||||
this.fetchRecords()
|
this.fetchInfo(info.id)
|
||||||
} else if (index === -1) {
|
} else if (index === -1) {
|
||||||
this.tabModes[info.id] = true
|
|
||||||
this.tabs.push(info)
|
this.tabs.push(info)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
readConsole = (info) => {
|
readConsole = (info) => {
|
||||||
this.tabModes[info.id] = false
|
|
||||||
const index = lds.findIndex(this.tabs, x => x.id === info.id);
|
const index = lds.findIndex(this.tabs, x => x.id === info.id);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
info = Object.assign({}, info, {mode: 'read'})
|
info = Object.assign({}, info, {mode: 'read'})
|
||||||
|
@ -142,9 +150,7 @@ class Store {
|
||||||
};
|
};
|
||||||
|
|
||||||
leaveConsole = () => {
|
leaveConsole = () => {
|
||||||
for (let item of this.tabs) {
|
this.tabs = []
|
||||||
item.mode = 'read'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue