Merge pull request #3019 from jumpserver/dev

v3.2.0 rc2
This commit is contained in:
Jiangjie.Bai
2023-04-14 19:01:37 +08:00
committed by GitHub
13 changed files with 272 additions and 91 deletions

View File

@@ -14,6 +14,7 @@ import Select2 from '@/components/FormFields/Select2'
import AssetSelect from '@/components/AssetSelect'
import { encryptPassword } from '@/utils/crypto'
import { Required, RequiredChange } from '@/components/DataForm/rules'
import AutomationParamsForm from '@/views/assets/Platform/AutomationParamsSetting.vue'
export default {
name: 'AccountCreateForm',
@@ -69,7 +70,7 @@ export default {
'secret_type', 'secret', 'ssh_key',
'token', 'access_key', 'passphrase'
]],
[this.$t('common.Other'), ['push_now', 'on_invalid', 'is_active', 'comment']]
[this.$t('common.Other'), ['push_now', 'params', 'on_invalid', 'is_active', 'comment']]
],
fieldsMeta: {
assets: {
@@ -206,6 +207,21 @@ export default {
return !automation.push_account_enabled || !automation.ansible_enabled || !this.$hasPerm('accounts.push_account') || this.addTemplate
}
},
params: {
label: this.$t('assets.PushParams'),
component: AutomationParamsForm,
el: {
method: this.asset?.auto_config?.push_account_method
},
hidden: (formValue) => {
const automation = this.iPlatform.automation || {}
return !formValue.push_now ||
!automation.push_account_enabled ||
!automation.ansible_enabled ||
!this.$hasPerm('accounts.push_account') ||
this.addTemplate
}
},
comment: {
hidden: () => {
return this.addTemplate

View File

@@ -0,0 +1,83 @@
<template>
<div>
<div v-for="(command, index) in value" :key="index" :prop="'value.' + index + '.value'" class="command-item">
<el-input v-model="value[index]" size="mini">
<template slot="prepend"> {{ inputTitle + ' ' + (index + 1) }}</template>
</el-input>
<div class="input-button">
<el-button
:disabled="deleteDisabled()"
icon="el-icon-minus"
size="mini"
style="flex-shrink: 0;"
type="danger"
@click="handleDelete(command)"
/>
<el-button
v-if="index === value.length - 1"
icon="el-icon-plus"
size="mini"
style="flex-shrink: 0;"
type="primary"
@click="handleAdd()"
/>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: Array,
default: () => ['']
},
inputTitle: {
type: String,
default: () => ''
}
},
data() {
return {}
},
methods: {
handleDelete(command) {
const index = this.value.indexOf(command)
if (index !== -1) {
this.value.splice(index, 1)
}
},
handleAdd() {
this.value.push('')
},
deleteDisabled() {
return this.value.length <= 1
}
}
}
</script>
<style lang="scss" scoped>
.el-input {
width: 85%;
}
.command-item {
display: flex;
margin: 5px 0;
}
.input-button {
margin-top: 2px;
display: flex;
margin-left: 20px
}
.input-button ::v-deep .el-button.el-button--mini {
height: 25px;
padding: 5px;
}
.el-input-group__append .el-button {
font-size: 14px;
color: #1a1a1a;
padding: 9px 20px;
}
</style>

View File

@@ -9,6 +9,7 @@ import PhoneInput from './PhoneInput'
import UploadField from './UploadField'
import UpdateToken from './UpdateToken'
import UserPassword from './UserPassword'
import DynamicInput from './DynamicInput'
import PasswordInput from './PasswordInput'
import UploadSecret from './UploadSecret'
import WeekCronSelect from './WeekCronSelect'
@@ -27,6 +28,7 @@ export default {
PhoneInput,
UploadField,
UserPassword,
DynamicInput,
PasswordInput,
UploadSecret,
WeekCronSelect,
@@ -46,6 +48,7 @@ export {
PhoneInput,
UploadField,
UserPassword,
DynamicInput,
PasswordInput,
UploadSecret,
WeekCronSelect,

View File

@@ -86,7 +86,7 @@
"AccountPushExecutionList": "Execution list",
"AccountPushCreate": "Account push create",
"AccountPushUpdate": "Account push update",
"AutoPushHelpText": "Please select an asset or node before setting it up",
"AutoPushHelpText": "The push parameter settings are currently only effective for assets with a platform type of host.",
"AccountPushList": "Account push list"
},
"AccountBackup": {
@@ -359,7 +359,8 @@
"Protocols": "Protocols",
"PublicIp": "Public ip",
"Push": "Push",
"PushSetting": "Push setting",
"PushParams": "Push params",
"ChangeSecretParams": "Change secret params",
"PushSystemUserNow": "Push system user now",
"PushAllSystemUsersToAsset": "Push all system users to asset",
"QuickUpdate": "Quick update",

View File

@@ -86,7 +86,7 @@
"AccountPushExecutionList": "実行リスト",
"AccountPushCreate": "アカウントのプッシュ作成",
"AccountPushUpdate": "アカウントプッシュ更新",
"AutoPushHelpText": "まず資産またはノードを選択して設定してください",
"AutoPushHelpText": "Pushパラメータの設定は、プラットフォームの種類がホストである資産に対してのみ有効です。",
"AccountPushList": "アカウントプッシュ"
},
"AccountBackup": {
@@ -353,7 +353,8 @@
"LoginOption": "ログインオプション",
"PublicIp": "パブリックネットワークIP",
"Push": "プッシュ",
"PushSetting": "プッシュ設定",
"PushParams": "プッシュパラメータ",
"ChangeSecretParams": "改密パラメータです",
"PushSystemUserNow": "プッシュシステムユーザー",
"PushAllSystemUsersToAsset": "すべてのシステムユーザーをアセットにプッシュ",
"QuickUpdate": "クイックアップデート",

View File

@@ -21,7 +21,7 @@
"AccountPushList": "账号推送",
"AccountPushCreate": "账号推送创建",
"AccountPushUpdate": "账号推送更新",
"AutoPushHelpText": "请先选择资产或者节点在进行设置",
"AutoPushHelpText": "推送参数设置,目前仅对平台种类为主机的资产生效。",
"AccountPushExecutionList": "执行列表"
},
"AccountBackup": {
@@ -371,7 +371,8 @@
"LoginOption": "登录选项",
"PublicIp": "公网IP",
"Push": "推送",
"PushSetting": "推送设置",
"PushParams": "推送参数",
"ChangeSecretParams": "改密参数",
"PushSystemUserNow": "推送系统用户",
"PushAllSystemUsersToAsset": "推送所有系统用户到资产",
"QuickUpdate": "快速更新",

View File

@@ -11,6 +11,13 @@
:url="secretUrl"
:visible.sync="showViewSecretDialog"
/>
<AutomationParamsForm
:visible.sync="autoPushVisible"
:has-button="false"
:method="pushAccountMethod"
@canSetting="onCanSetting"
@submit="onSubmit"
/>
</el-col>
</el-row>
</template>
@@ -20,12 +27,14 @@ import AutoDetailCard from '@/components/DetailCard/auto'
import QuickActions from '@/components/QuickActions'
import ViewSecret from '@/components/AccountListTable/ViewSecret'
import { openTaskPage } from '@/utils/jms'
import AutomationParamsForm from '@/views/assets/Platform/AutomationParamsSetting.vue'
export default {
name: 'Detail',
components: {
AutoDetailCard,
QuickActions,
AutomationParamsForm,
ViewSecret
},
props: {
@@ -39,6 +48,8 @@ export default {
const filterSuFrom = ['database', 'device', 'cloud', 'web', 'windows']
return {
needSetAutoPushParams: false,
autoPushVisible: false,
secretUrl: `/api/v1/accounts/account-secrets/${this.object.id}/`,
showViewSecretDialog: false,
quickActions: [
@@ -117,12 +128,16 @@ export default {
},
callbacks: Object.freeze({
click: () => {
this.$axios.post(
`/api/v1/accounts/accounts/tasks/`,
{ action: 'push', accounts: [this.object.id] }
).then(res => {
openTaskPage(res['task'])
})
if (this.needSetAutoPushParams) {
this.autoPushVisible = true
} else {
this.$axios.post(
`/api/v1/accounts/accounts/tasks/`,
{ action: 'push', accounts: [this.object.id] }
).then(res => {
openTaskPage(res['task'])
})
}
}
})
},
@@ -205,6 +220,26 @@ export default {
}
},
computed: {
pushAccountMethod() {
return this.object.asset?.auto_config?.push_account_method || ''
}
},
methods: {
onCanSetting(item) {
this.needSetAutoPushParams = item
},
onSubmit(form) {
this.$axios.post(
`/api/v1/accounts/accounts/tasks/`,
{
action: 'push',
accounts: [this.object.id],
params: form
}
).then(res => {
openTaskPage(res['task'])
})
}
}
}
</script>

View File

@@ -12,7 +12,7 @@
v-if="visible"
width="60%"
:visible.sync="visible"
:title="$tc('assets.AutoPush')"
:title="title"
:show-cancel="false"
:show-confirm="false"
:destroy-on-close="true"
@@ -43,6 +43,12 @@ export default {
type: Object,
default: () => ({})
},
title: {
type: String,
default: function() {
return this.$t('assets.PushParams')
}
},
assets: {
type: Array,
default: () => []
@@ -117,9 +123,10 @@ export default {
const platforms = await this.getFilterPlatforms()
let pushAccountMethods = platforms.map(i => i.automation?.push_account_method)
pushAccountMethods = _.uniq(pushAccountMethods)
this.setFormConfig(pushAccountMethods)
if (pushAccountMethods.length > 0) {
// 检测是否有可设置的推送方式
const hasCanSettingPushMethods = _.intersection(pushAccountMethods, Object.keys(this.remoteMeta))
this.setFormConfig(hasCanSettingPushMethods)
if (hasCanSettingPushMethods.length > 0) {
this.isDisabled = false
this.$emit('input', this.form)
} else {
@@ -134,27 +141,25 @@ export default {
this.config.fields = []
for (const method of methods) {
const filterField = this.remoteMeta[method]
if (filterField) {
// 修改资产、节点时不点击设置按钮也需要获取form表单值暴露出去
if (this.form.hasOwnProperty(method)) {
newForm[method] = this.form[method]
}
fields.push([method, [method]])
fieldsMeta[method] = {
fields: [],
fieldsMeta: {}
}
if (Object.keys(filterField?.children || {}).length > 0) {
for (const [k, v] of Object.entries(filterField.children)) {
const item = {
...v,
type: 'input'
}
delete item.default
fieldsMeta[method].fields.push(k)
fieldsMeta[method].fieldsMeta[k] = item
const filterField = this.remoteMeta[method] || {}
// 修改资产、节点时不点击设置按钮也需要获取form表单值暴露出去
if (this.form.hasOwnProperty(method)) {
newForm[method] = this.form[method]
}
fields.push([filterField.label, [method]])
fieldsMeta[method] = {
fields: [],
fieldsMeta: {}
}
if (Object.keys(filterField?.children || {}).length > 0) {
for (const [k, v] of Object.entries(filterField.children)) {
const item = {
...v,
type: 'input'
}
delete item.default
fieldsMeta[method].fields.push(k)
fieldsMeta[method].fieldsMeta[k] = item
}
}
}

View File

@@ -97,7 +97,7 @@ export default {
},
params: {
component: AccountAutoPush,
label: this.$t('assets.AutoPush'),
label: this.$t('assets.PushParams'),
el: {
assets: this.asset_ids,
nodes: this.node_ids

View File

@@ -1,18 +1,20 @@
<template>
<div class="content">
<el-button
:disabled="!isDisabled"
size="small"
v-if="hasButton"
:disabled="!canSetting"
size="mini"
class="setting"
:icon="icon"
type="primary"
@click="onSetting"
>
{{ btnText }}
</el-button>
<Dialog
v-if="visible"
v-if="isVisible"
width="60%"
:visible.sync="visible"
:visible.sync="isVisible"
:title="title"
:show-cancel="false"
:show-confirm="false"
@@ -25,6 +27,7 @@
:form="form"
class="data-form"
v-bind="config"
v-on="$listeners"
@submit="onSubmit"
/>
</Dialog>
@@ -33,6 +36,7 @@
<script>
import { Dialog, AutoDataForm } from '@/components'
import { DynamicInput } from '@/components/FormFields'
export default {
components: {
@@ -47,16 +51,18 @@ export default {
title: {
type: String,
default: function() {
return this.$t('assets.PushSetting')
return this.$t('assets.PushParams')
}
},
btnText: {
type: String,
default: null
default: function() {
return this.$t('common.Setting')
}
},
icon: {
type: String,
default: 'el-icon-setting'
default: ''
},
url: {
type: String,
@@ -65,51 +71,67 @@ export default {
method: {
type: String,
default: ''
},
visible: {
type: Boolean,
default: false
},
hasButton: {
type: Boolean,
default: true
}
},
data() {
return {
visible: false,
isDisabled: true,
isVisible: this.visible,
canSetting: true,
form: this.value,
remoteMeta: {},
config: {
url: this.url,
hasSaveContinue: false,
hasButtons: true,
hasReset: false,
fields: [],
method: 'get',
fieldsMeta: {}
},
iValue: this.value
}
}
},
watch: {
visible(val) {
this.isVisible = val
},
method(iNew, iOld) {
if (iNew !== iOld) {
this.getUrlMeta()
}
}
},
mounted() {
this.init()
this.getUrlMeta()
},
methods: {
async init() {
async getUrlMeta() {
const data = await this.$store.dispatch('common/getUrlMeta', { url: this.url })
this.remoteMeta = data.actions[this.config.method.toUpperCase()] || {}
if (this.hasDisabled()) {
if (this.onCanSetting()) {
this.setFormConfig()
}
},
hasDisabled() {
onCanSetting() {
const filterField = Object.keys(this.remoteMeta)
this.isDisabled = filterField.includes(this.method)
return this.isDisabled
this.canSetting = filterField.includes(this.method)
this.$emit('canSetting', this.canSetting)
return this.canSetting
},
setFormConfig() {
const { method } = this
this.config.fieldsMeta = this.getFieldsMeta()
this.config.fields = [[method, [method]]]
},
getFieldsMeta() {
let fields = []
const fieldsMeta = {}
const { method } = this
const filterField = this.remoteMeta[method]
fields = [[filterField.label, [method]]]
fieldsMeta[method] = {
fields: [],
fieldsMeta: {}
@@ -117,46 +139,32 @@ export default {
if (Object.keys(filterField?.children || {}).length > 0) {
for (const [k, v] of Object.entries(filterField.children)) {
const item = {
...v,
type: 'input'
let component = 'el-input'
switch (v?.type) {
case 'list':
component = DynamicInput
break
}
const item = { ...v, component: component }
fieldsMeta[method].fields.push(k)
fieldsMeta[method].fieldsMeta[k] = item
}
}
return fieldsMeta
this.config.fields = fields
this.config.fieldsMeta = fieldsMeta
},
onSetting() {
this.visible = true
console.log('method: ', this.method)
console.log('this.value', this.value)
this.isVisible = true
},
onSubmit(form) {
this.$emit('input', form)
this.visible = false
this.isVisible = false
this.$emit('update:visible', this.isVisible)
}
}
}
</script>
<style lang="scss" scoped>
.content {
display: flex;
}
.setting {
background-color: #F5F7FA;
border: 1px solid #DCDFE6;
font-size: 14px;
color: #1a1a1a;
padding: 9px 20px;
}
>>> .el-button.is-disabled, .el-button.is-disabled:hover {
color: #C0C4CC!important;
cursor: not-allowed!important;
background-image: none!important;
background-color: #FFF!important;
border-color: #EBEEF5!important;
}
</style>

View File

@@ -15,7 +15,7 @@
<script>
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
import { platformFieldsMeta, setAutomations } from './const'
import { platformFieldsMeta, setAutomations, updateAutomationParams } from './const'
export default {
name: 'PlatformCreateUpdate',
@@ -77,6 +77,7 @@ export default {
return values
},
afterGetFormValue: (obj) => {
updateAutomationParams(this, obj)
if (obj['category'] && obj['type']) {
obj['category_type'] = [obj['category'].value, obj['type'].value]
}

View File

@@ -7,7 +7,7 @@
<script>
import IBox from '@/components/IBox'
import { GenericCreateUpdateForm } from '@/layout/components'
import { platformFieldsMeta, setAutomations } from '../const'
import { updateAutomationParams, platformFieldsMeta, setAutomations } from '../const'
import { mapGetters } from 'vuex'
export default {
@@ -39,7 +39,11 @@ export default {
fieldsMeta: platformFieldsMeta(this),
onSubmit: this.submit,
canSubmit: !this.object.internal,
defaultOptions: {}
defaultOptions: {},
afterGetFormValue: (obj) => {
updateAutomationParams(this, obj)
return obj
}
}
},
computed: {

View File

@@ -4,7 +4,7 @@ import { JsonEditor } from '@/components/FormFields'
import { assetFieldsMeta } from '@/views/assets/const'
import AutomationParamsSetting from './AutomationParamsSetting'
const needSettingParamsFields = ['push_account']
const needSettingParamsFields = ['push_account', 'change_secret']
export const platformFieldsMeta = (vm) => {
const assetMeta = assetFieldsMeta(vm)
@@ -17,7 +17,7 @@ export const platformFieldsMeta = (vm) => {
'ansible_enabled', 'ansible_config',
'ping_enabled', 'ping_method',
'gather_facts_enabled', 'gather_facts_method',
'change_secret_enabled', 'change_secret_method',
'change_secret_enabled', 'change_secret_method', 'change_secret_params',
'push_account_enabled', 'push_account_method', 'push_account_params',
'verify_account_enabled', 'verify_account_method',
'gather_accounts_enabled', 'gather_accounts_method'
@@ -31,7 +31,23 @@ export const platformFieldsMeta = (vm) => {
ping_method: {},
gather_facts_method: {},
push_account_method: {},
change_secret_method: {},
push_account_params: {
label: vm.$t('assets.PushParams')
},
change_secret_method: {
on: {
change: ([val]) => {
vm.fieldsMeta.automation.fieldsMeta.change_secret_params.el.method = val
}
}
},
change_secret_params: {
label: vm.$t('assets.ChangeSecretParams'),
el: {
title: vm.$t('assets.ChangeSecretParams'),
method: 'change_secret_posix'
}
},
verify_account_method: {}
}
},
@@ -128,3 +144,10 @@ export const setAutomations = (vm) => {
}
}
}
export const updateAutomationParams = (vm, obj) => {
needSettingParamsFields.forEach((v) => {
const value = _.get(obj.automation, `${v}_method`)
_.set(vm.fieldsMeta.automation.fieldsMeta, `${v}_params.el.method`, value)
})
}