mirror of https://github.com/openspug/spug
fix issue
parent
3485d36b85
commit
4e900006be
|
@ -8,6 +8,7 @@ from .views import *
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('request/', RequestView.as_view()),
|
path('request/', RequestView.as_view()),
|
||||||
path('request/ext1/', post_request_ext1),
|
path('request/ext1/', post_request_ext1),
|
||||||
|
path('request/ext1/rollback/', post_request_ext1_rollback),
|
||||||
path('request/ext2/', post_request_ext2),
|
path('request/ext2/', post_request_ext2),
|
||||||
path('request/upload/', do_upload),
|
path('request/upload/', do_upload),
|
||||||
path('request/<int:r_id>/', RequestDetailView.as_view()),
|
path('request/<int:r_id>/', RequestDetailView.as_view()),
|
||||||
|
|
|
@ -22,7 +22,7 @@ import os
|
||||||
|
|
||||||
class RequestView(View):
|
class RequestView(View):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
data, query = [], {}
|
data, query, counter = [], {}, {}
|
||||||
if not request.user.is_supper:
|
if not request.user.is_supper:
|
||||||
perms = request.user.deploy_perms
|
perms = request.user.deploy_perms
|
||||||
query['deploy__app_id__in'] = perms['apps']
|
query['deploy__app_id__in'] = perms['apps']
|
||||||
|
@ -48,6 +48,9 @@ class RequestView(View):
|
||||||
tmp['app_host_ids'] = json.loads(item.app_host_ids)
|
tmp['app_host_ids'] = json.loads(item.app_host_ids)
|
||||||
tmp['status_alias'] = item.get_status_display()
|
tmp['status_alias'] = item.get_status_display()
|
||||||
tmp['created_by_user'] = item.created_by_user
|
tmp['created_by_user'] = item.created_by_user
|
||||||
|
if item.app_extend == '1':
|
||||||
|
tmp['visible_rollback'] = item.deploy_id not in counter
|
||||||
|
counter[item.deploy_id] = True
|
||||||
data.append(tmp)
|
data.append(tmp)
|
||||||
return json_response(data)
|
return json_response(data)
|
||||||
|
|
||||||
|
@ -261,6 +264,37 @@ def post_request_ext1(request):
|
||||||
return json_response(error=error)
|
return json_response(error=error)
|
||||||
|
|
||||||
|
|
||||||
|
def post_request_ext1_rollback(request):
|
||||||
|
form, error = JsonParser(
|
||||||
|
Argument('request_id', type=int, help='参数错误'),
|
||||||
|
Argument('name', help='请输入申请标题'),
|
||||||
|
Argument('host_ids', type=list, filter=lambda x: len(x), help='请选择要部署的主机'),
|
||||||
|
Argument('desc', required=False),
|
||||||
|
).parse(request.body)
|
||||||
|
if error is None:
|
||||||
|
req = DeployRequest.objects.get(pk=form.pop('request_id'))
|
||||||
|
requests = DeployRequest.objects.filter(deploy=req.deploy, status__in=('3', '-3'))
|
||||||
|
versions = list({x.spug_version: 1 for x in requests}.keys())
|
||||||
|
if req.spug_version not in versions[:req.deploy.extend_obj.versions + 1]:
|
||||||
|
return json_response(error='选择的版本超出了发布配置中设置的版本数量,无法快速回滚,可通过新建发布申请选择构建仓库里的该版本再次发布。')
|
||||||
|
|
||||||
|
form.status = '0' if req.deploy.is_audit else '1'
|
||||||
|
form.host_ids = json.dumps(sorted(form.host_ids))
|
||||||
|
new_req = DeployRequest.objects.create(
|
||||||
|
deploy_id=req.deploy_id,
|
||||||
|
repository_id=req.repository_id,
|
||||||
|
type='2',
|
||||||
|
extra=req.extra,
|
||||||
|
version=req.version,
|
||||||
|
spug_version=req.spug_version,
|
||||||
|
created_by=request.user,
|
||||||
|
**form
|
||||||
|
)
|
||||||
|
if req.deploy.is_audit:
|
||||||
|
Thread(target=Helper.send_deploy_notify, args=(new_req, 'approve_req')).start()
|
||||||
|
return json_response(error=error)
|
||||||
|
|
||||||
|
|
||||||
def post_request_ext2(request):
|
def post_request_ext2(request):
|
||||||
form, error = JsonParser(
|
form, error = JsonParser(
|
||||||
Argument('id', type=int, required=False),
|
Argument('id', type=int, required=False),
|
||||||
|
|
|
@ -192,7 +192,7 @@ export default observer(function () {
|
||||||
repositories.map(item => (
|
repositories.map(item => (
|
||||||
<Select.Option key={item.id} value={item.id} disabled={type === '2' && item.id >= rb_id}>
|
<Select.Option key={item.id} value={item.id} disabled={type === '2' && item.id >= rb_id}>
|
||||||
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||||
<span>{item.remarks ? `${item.version} (${item.remarks})` : item.version}</span>
|
<span>{item.version}</span>
|
||||||
<span style={{color: '#999', fontSize: 12}}>构建于 {moment(item.created_at).fromNow()}</span>
|
<span style={{color: '#999', fontSize: 12}}>构建于 {moment(item.created_at).fromNow()}</span>
|
||||||
</div>
|
</div>
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||||
|
* Copyright (c) <spug.dev@gmail.com>
|
||||||
|
* Released under the AGPL-3.0 License.
|
||||||
|
*/
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { Modal, Form, Input, Select, Button, message } from 'antd';
|
||||||
|
import HostSelector from './HostSelector';
|
||||||
|
import hostStore from 'pages/host/store';
|
||||||
|
import { http, includes } from 'libs';
|
||||||
|
import store from './store';
|
||||||
|
import lds from 'lodash';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
export default observer(function () {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [host_ids, setHostIds] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const {app_host_ids, host_ids} = store.record;
|
||||||
|
setHostIds(lds.clone(host_ids || app_host_ids));
|
||||||
|
if (!hostStore.records || hostStore.records.length === 0) hostStore.fetchRecords()
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
if (host_ids.length === 0) {
|
||||||
|
return message.error('请至少选择一个要发布的主机')
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
const formData = form.getFieldsValue();
|
||||||
|
formData['host_ids'] = host_ids;
|
||||||
|
http.post('/api/deploy/request/ext1/rollback/', formData)
|
||||||
|
.then(res => {
|
||||||
|
message.success('操作成功');
|
||||||
|
store.rollbackVisible = false;
|
||||||
|
store.fetchRecords()
|
||||||
|
}, () => setLoading(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
const {app_host_ids, deploy_id} = store.record;
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible
|
||||||
|
width={600}
|
||||||
|
maskClosable={false}
|
||||||
|
title="新建回滚发布申请"
|
||||||
|
onCancel={() => store.rollbackVisible = false}
|
||||||
|
confirmLoading={loading}
|
||||||
|
onOk={handleSubmit}>
|
||||||
|
<Form form={form} initialValues={store.record} labelCol={{span: 5}} wrapperCol={{span: 17}}>
|
||||||
|
<Form.Item required name="name" label="申请标题">
|
||||||
|
<Input placeholder="请输入申请标题"/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item required name="request_id" label="选择版本">
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
placeholder="请选择回滚至哪个版本"
|
||||||
|
filterOption={(input, option) => includes(option.props.children, input)}>
|
||||||
|
{store.records.filter(x => x.deploy_id === deploy_id && ['3', '-3'].includes(x.status)).map((item, index) => (
|
||||||
|
<Select.Option key={item.id} value={item.id} record={item} disabled={index === 0}>
|
||||||
|
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||||
|
<span>{`${item.name} (${item.version})`}</span>
|
||||||
|
<span style={{color: '#999', fontSize: 12}}>创建于 {moment(item.created_at).fromNow()}</span>
|
||||||
|
</div>
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item required label="目标主机" tooltip="可以通过创建多个发布申请单,选择主机分批发布。">
|
||||||
|
{host_ids.length > 0 && `已选择 ${host_ids.length} 台(可选${app_host_ids.length})`}
|
||||||
|
<Button type="link" onClick={() => setVisible(true)}>选择主机</Button>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="desc" label="备注信息">
|
||||||
|
<Input placeholder="请输入备注信息"/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
{visible && <HostSelector
|
||||||
|
host_ids={host_ids}
|
||||||
|
app_host_ids={app_host_ids}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
onOk={ids => setHostIds(ids)}/>}
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
})
|
|
@ -92,18 +92,16 @@ function ComTable() {
|
||||||
<Popconfirm title="确认要执行该发布申请?" onConfirm={e => handleDeploy(e, info)}>
|
<Popconfirm title="确认要执行该发布申请?" onConfirm={e => handleDeploy(e, info)}>
|
||||||
<Action.Button auth="deploy.request.do">发布</Action.Button>
|
<Action.Button auth="deploy.request.do">发布</Action.Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
<Action.Button
|
{info.visible_rollback && (
|
||||||
auth="deploy.request.do"
|
<Action.Button auth="deploy.request.do" onClick={() => store.rollback(info)}>回滚</Action.Button>
|
||||||
disabled={info.type === '2'}
|
)}
|
||||||
onClick={() => store.rollback(info)}>回滚</Action.Button>
|
|
||||||
</Action>;
|
</Action>;
|
||||||
case '3':
|
case '3':
|
||||||
return <Action>
|
return <Action>
|
||||||
<Action.Button auth="deploy.request.do" onClick={() => store.readConsole(info)}>查看</Action.Button>
|
<Action.Button auth="deploy.request.do" onClick={() => store.readConsole(info)}>查看</Action.Button>
|
||||||
<Action.Button
|
{info.visible_rollback && (
|
||||||
auth="deploy.request.do"
|
<Action.Button auth="deploy.request.do" onClick={() => store.rollback(info)}>回滚</Action.Button>
|
||||||
disabled={info.type === '2'}
|
)}
|
||||||
onClick={() => store.rollback(info)}>回滚</Action.Button>
|
|
||||||
</Action>;
|
</Action>;
|
||||||
case '-1':
|
case '-1':
|
||||||
return <Action>
|
return <Action>
|
||||||
|
|
|
@ -15,6 +15,7 @@ import ComTable from './Table';
|
||||||
import Ext1Console from './Ext1Console';
|
import Ext1Console from './Ext1Console';
|
||||||
import Ext2Console from './Ext2Console';
|
import Ext2Console from './Ext2Console';
|
||||||
import BatchDelete from './BatchDelete';
|
import BatchDelete from './BatchDelete';
|
||||||
|
import Rollback from './Rollback';
|
||||||
import { includes } from 'libs';
|
import { includes } from 'libs';
|
||||||
import envStore from 'pages/config/environment/store';
|
import envStore from 'pages/config/environment/store';
|
||||||
import appStore from 'pages/config/app/store';
|
import appStore from 'pages/config/app/store';
|
||||||
|
@ -86,6 +87,7 @@ function Index() {
|
||||||
{store.ext2Visible && <Ext2Form/>}
|
{store.ext2Visible && <Ext2Form/>}
|
||||||
{store.batchVisible && <BatchDelete/>}
|
{store.batchVisible && <BatchDelete/>}
|
||||||
{store.approveVisible && <Approve/>}
|
{store.approveVisible && <Approve/>}
|
||||||
|
{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 => (
|
||||||
|
|
|
@ -21,6 +21,7 @@ class Store {
|
||||||
@observable ext2Visible = false;
|
@observable ext2Visible = false;
|
||||||
@observable batchVisible = false;
|
@observable batchVisible = false;
|
||||||
@observable approveVisible = false;
|
@observable approveVisible = false;
|
||||||
|
@observable rollbackVisible = false;
|
||||||
|
|
||||||
@observable f_status = 'all';
|
@observable f_status = 'all';
|
||||||
@observable f_app_id;
|
@observable f_app_id;
|
||||||
|
@ -96,15 +97,10 @@ class Store {
|
||||||
};
|
};
|
||||||
|
|
||||||
rollback = (info) => {
|
rollback = (info) => {
|
||||||
this.record = lds.pick(info, ['deploy_id', 'app_host_ids', 'host_ids']);
|
this.record = lds.pick(info, ['deploy_id', 'host_ids']);
|
||||||
this.record.type = '2';
|
this.record.app_host_ids = info.host_ids;
|
||||||
this.record.rb_id = info.repository_id;
|
|
||||||
this.record.name = `${info.name} - 回滚`;
|
this.record.name = `${info.name} - 回滚`;
|
||||||
if (info.app_extend === '1') {
|
this.rollbackVisible = true
|
||||||
this.ext1Visible = true
|
|
||||||
} else {
|
|
||||||
this.ext2Visible = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showForm = (info) => {
|
showForm = (info) => {
|
||||||
|
|
Loading…
Reference in New Issue