mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-14 03:46:26 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c093e7d621 | ||
|
|
b4183b421f | ||
|
|
9c6281cf02 | ||
|
|
e7bdf6276d | ||
|
|
fa860dbd85 | ||
|
|
2bb4ca3ec0 | ||
|
|
93a08d265b | ||
|
|
b7be7784c6 | ||
|
|
375c6bf44b | ||
|
|
542e34208e | ||
|
|
f041b0de74 | ||
|
|
7ff75280a5 | ||
|
|
d76009de3a | ||
|
|
851f4508e2 | ||
|
|
a36e5196f8 | ||
|
|
d22d16681e | ||
|
|
31c86fb281 | ||
|
|
f83739b496 | ||
|
|
1d9eddb11f | ||
|
|
da14abc83b | ||
|
|
7b705dfc75 | ||
|
|
4516d83ce4 | ||
|
|
db753bd1c2 | ||
|
|
ac14e5964d | ||
|
|
232c22c8ae | ||
|
|
610da9f9b2 | ||
|
|
29631b1eaf | ||
|
|
537887553d | ||
|
|
c1ddc0f8e0 | ||
|
|
316b39d15b | ||
|
|
b18b05b8fb | ||
|
|
cd5534c20a | ||
|
|
048fee7ede | ||
|
|
083b4bc499 | ||
|
|
eb44856807 | ||
|
|
fff2288069 | ||
|
|
d5fc939a40 | ||
|
|
6489cbe488 | ||
|
|
941f1d855e | ||
|
|
ff88a0bd42 | ||
|
|
c87412f4c0 | ||
|
|
2da4e4f7a0 | ||
|
|
848bdcba36 | ||
|
|
96206f10a2 | ||
|
|
a6012eba4a | ||
|
|
2570aa63fc | ||
|
|
43797c2f46 | ||
|
|
e98e23dad8 | ||
|
|
11c845c0fd | ||
|
|
6310fae385 | ||
|
|
c20453a1de | ||
|
|
b1733835a4 | ||
|
|
4e1c921451 | ||
|
|
0a4238fe93 | ||
|
|
2aad1110d0 | ||
|
|
03031e94c1 | ||
|
|
aa94c1792c | ||
|
|
f13bc4fccb | ||
|
|
def153cc42 | ||
|
|
8add1b7f92 | ||
|
|
0cafadd230 | ||
|
|
61c777532b | ||
|
|
e13f2b34ff | ||
|
|
fa3daea82d | ||
|
|
16c6f4c77f | ||
|
|
31e699bd2d | ||
|
|
4ecdf1575d | ||
|
|
ec00a541f8 | ||
|
|
ef97df09cb |
10
README.md
10
README.md
@@ -46,12 +46,4 @@ server {
|
||||
|
||||
|
||||
## License & Copyright
|
||||
Copyright (c) 2014-2021 飞致云 FIT2CLOUD, All rights reserved.
|
||||
|
||||
Licensed under The GNU General Public License version 2 (GPLv2) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||||
|
||||
https://github.com/jumpserver/lina/blob/dev/LICENSE
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
|
||||
Be consistent with [jumpserver](https://github.com/jumpserver/jumpserver)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.SSHKey')">
|
||||
<el-form-item :label="this.$t('users.SSHKey')">
|
||||
<el-input v-model="authInfo['private_key']" class="item-textarea" type="textarea" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@cancel="handleCancel()"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<el-form label-position="right" label-width="80px">
|
||||
<el-form label-position="right" label-width="90px">
|
||||
<el-form-item :label="this.$t('assets.Hostname')">
|
||||
<el-input v-model="account.hostname" readonly />
|
||||
</el-form-item>
|
||||
@@ -16,21 +16,27 @@
|
||||
<el-input v-model="account['username']" readonly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Password')">
|
||||
<el-input v-model="authInfo.password" type="password" />
|
||||
<UpdateToken v-model="authInfo.password" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.SSHKey')">
|
||||
<input type="file" @change="onPrivateKeyLoaded">
|
||||
<el-form-item :label="this.$t('assets.SSHSecretKey')">
|
||||
<UploadKey @input="getFile" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="this.$t('assets.Passphrase')">
|
||||
<UpdateToken v-model="authInfo.passphrase" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { Dialog, UploadKey } from '@/components'
|
||||
import { UpdateToken } from '@/components/FormFields'
|
||||
export default {
|
||||
name: 'UpdateSecretInfo',
|
||||
components: {
|
||||
Dialog
|
||||
Dialog,
|
||||
UploadKey,
|
||||
UpdateToken
|
||||
},
|
||||
props: {
|
||||
account: {
|
||||
@@ -42,7 +48,8 @@ export default {
|
||||
return {
|
||||
authInfo: {
|
||||
password: '',
|
||||
private_key: ''
|
||||
private_key: '',
|
||||
passphrase: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -54,10 +61,12 @@ export default {
|
||||
}
|
||||
if (this.authInfo.private_key !== '') {
|
||||
data.private_key = this.authInfo.private_key
|
||||
if (this.authInfo.passphrase) data.passphrase = this.authInfo.passphrase
|
||||
}
|
||||
this.$axios.patch(
|
||||
`/api/v1/assets/accounts/${this.account.id}/`,
|
||||
data
|
||||
data,
|
||||
{ disableFlashErrorMsg: true }
|
||||
).then(res => {
|
||||
this.authInfo = { password: '', private_key: '' }
|
||||
this.$message.success(this.$tc('common.updateSuccessMsg'))
|
||||
@@ -72,16 +81,8 @@ export default {
|
||||
handleCancel() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
onPrivateKeyLoaded(e) {
|
||||
const vm = this
|
||||
// TODO 校验文件类型
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
vm.authInfo.private_key = this.result
|
||||
}
|
||||
reader.readAsText(
|
||||
e.target.files[0]
|
||||
)
|
||||
getFile(file) {
|
||||
this.authInfo.private_key = file
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ export default {
|
||||
watch: {
|
||||
url(iNew) {
|
||||
this.$set(this.tableConfig, 'url', iNew)
|
||||
this.$set(this.headerActions.exportOptions, 'url', iNew.replace('/accounts/', '/account-secrets/'))
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -135,7 +135,7 @@ export default {
|
||||
mfaVerifyRequired: true
|
||||
},
|
||||
searchConfig: {
|
||||
exclude: ['systemuser', 'asset']
|
||||
exclude: ['systemuser', 'app']
|
||||
},
|
||||
hasSearch: true
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ export class FormFieldGenerator {
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'multiple choice':
|
||||
field.el.choices = fieldRemoteMeta['choices']
|
||||
break
|
||||
case 'datetime':
|
||||
type = 'date-picker'
|
||||
field.el = {
|
||||
|
||||
@@ -274,8 +274,10 @@ export default {
|
||||
})
|
||||
},
|
||||
generatePopoverColumns() {
|
||||
this.popoverColumns.totalColumnsList = this.totalColumns.map(obj => {
|
||||
return { prop: obj.prop, label: obj.label }
|
||||
this.popoverColumns.totalColumnsList = this.totalColumns.filter(obj => {
|
||||
if (obj.label) {
|
||||
return { prop: obj.prop, label: obj.label }
|
||||
}
|
||||
})
|
||||
this.popoverColumns.currentCols = this.cleanedColumnsShow.show
|
||||
this.popoverColumns.minCols = this.cleanedColumnsShow.min
|
||||
|
||||
@@ -110,9 +110,11 @@ export default {
|
||||
this.currentNode = treeNode
|
||||
this.currentNodeId = treeNode.meta.data.id
|
||||
query['node'] = this.currentNodeId
|
||||
query['asset'] = ''
|
||||
url = `${this.setting.url}${combinator}node_id=${treeNode.meta.data.id}&show_current_asset=${show_current_asset}`
|
||||
} else if (treeNode.meta.type === 'asset') {
|
||||
query['asset'] = treeNode.meta.data.id
|
||||
query['node'] = ''
|
||||
url = `${this.setting.url}${combinator}asset_id=${treeNode.meta.data.id}&show_current_asset=${show_current_asset}`
|
||||
}
|
||||
this.$router.push({ query })
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<template v-for="option in action.dropdown">
|
||||
<div v-if="option.group" :key="'group:'+option.name" class="dropdown-menu-title">
|
||||
<div v-if="option.group" :key="'group:'+option.name" class="dropdown-menu-title" style="width:130px">
|
||||
{{ option.group }}
|
||||
</div>
|
||||
<el-dropdown-item
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
<script>
|
||||
import ElFormRender from './components/el-form-renderer'
|
||||
import { scrollToError } from '@/utils'
|
||||
export default {
|
||||
components: {
|
||||
ElFormRender
|
||||
@@ -76,6 +77,7 @@ export default {
|
||||
this.$emit('submit', form.getFormValue(), form, addContinue)
|
||||
} else {
|
||||
this.$emit('invalid', valid)
|
||||
scrollToError(form.$el)
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<input type="file" @change="Onchange">
|
||||
<input ref="upLoadFile" type="file" style="display: none" @change="Onchange">
|
||||
<el-button size="mini" @click.native.stop="onUpLoad">
|
||||
{{ this.$t('common.SelectFile') }}
|
||||
</el-button>
|
||||
<span>{{ fileName }}</span>
|
||||
<div v-if="tip !== ''">{{ tip }}</div>
|
||||
<input v-model="value" type="text" hidden v-on="$listeners">
|
||||
<div>
|
||||
@@ -23,6 +27,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fileName: '',
|
||||
initial: this.value,
|
||||
preview: this.value
|
||||
}
|
||||
@@ -34,16 +39,21 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onUpLoad() {
|
||||
this.$refs.upLoadFile.click()
|
||||
},
|
||||
onInput(val) {
|
||||
this.$emit('input', val)
|
||||
},
|
||||
Onchange(e) {
|
||||
if (e.target.files[0] === undefined) {
|
||||
const upLoadFile = e.target.files[0]
|
||||
if (upLoadFile === undefined) {
|
||||
this.$emit('input', this.initial)
|
||||
return
|
||||
}
|
||||
this.$emit('fileChange', e.target.files[0])
|
||||
this.$emit('input', this.getObjectURL(e.target.files[0]))
|
||||
this.fileName = upLoadFile?.name || ''
|
||||
this.$emit('fileChange', upLoadFile)
|
||||
this.$emit('input', this.getObjectURL(upLoadFile))
|
||||
},
|
||||
getObjectURL(file) {
|
||||
let url = null
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<div class="upload-key">
|
||||
<input type="file" @change="onChange">
|
||||
<input ref="upLoadFile" type="file" style="display: none" @change="onChange">
|
||||
<el-button size="mini" @click.native.stop="onUpLoad">
|
||||
{{ this.$t('common.SelectFile') }}
|
||||
</el-button>
|
||||
<span>{{ fileName }}</span>
|
||||
<div v-if="tip !== ''">{{ tip }}</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -21,12 +25,22 @@ export default {
|
||||
default: () => 'string'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fileName: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onUpLoad() {
|
||||
this.$refs.upLoadFile.click()
|
||||
},
|
||||
onChange(e) {
|
||||
if (e.target.files.length === 0) {
|
||||
const upLoadFile = e.target.files
|
||||
if (upLoadFile.length === 0) {
|
||||
return
|
||||
}
|
||||
const vm = this
|
||||
this.fileName = upLoadFile[0].name || ''
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
let result = this.result
|
||||
@@ -36,7 +50,7 @@ export default {
|
||||
vm.$emit('input', result)
|
||||
}
|
||||
reader.readAsText(
|
||||
e.target.files[0]
|
||||
upLoadFile[0]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
@blur="focus = false"
|
||||
@focus="focus = true"
|
||||
@change="handleConfirm"
|
||||
@keyup.enter.native="handleConfirm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -85,7 +86,11 @@ export default {
|
||||
if (key === '') {
|
||||
key = 'search'
|
||||
}
|
||||
data[key] = value
|
||||
if (key.startsWith('search')) {
|
||||
data['search'] = (data.search ? data.search + ',' : '') + value
|
||||
} else {
|
||||
data[key] = value
|
||||
}
|
||||
}
|
||||
return data
|
||||
},
|
||||
@@ -108,6 +113,19 @@ export default {
|
||||
handler(val) {
|
||||
if (val && val.length > 0) {
|
||||
const routeFilter = this.checkInTableColumns()
|
||||
const routerSearch = routeFilter.search || {}
|
||||
const routerSearchArrs = routerSearch?.value?.split(',') || []
|
||||
const routerSearchArrsLength = routerSearchArrs.length || 0
|
||||
if (routerSearch && routerSearchArrsLength > 0) {
|
||||
for (let i = 0; i < routerSearchArrsLength; i++) {
|
||||
const cur = routerSearchArrs[i]
|
||||
routeFilter[`search_${cur}`] = {
|
||||
...routerSearch,
|
||||
value: cur
|
||||
}
|
||||
}
|
||||
delete routeFilter.search
|
||||
}
|
||||
const asFilterTags = _.cloneDeep(this.filterTags)
|
||||
this.filterTags = {
|
||||
...asFilterTags,
|
||||
@@ -129,37 +147,43 @@ export default {
|
||||
if (Object.keys(this.filterMaps).length > 0) {
|
||||
return this.$emit('tagSearch', this.filterMaps)
|
||||
}
|
||||
}
|
||||
, 400)
|
||||
}, 400)
|
||||
// this.$nextTick(() => this.$emit('tagSearch', this.filterMaps))
|
||||
},
|
||||
methods: {
|
||||
// 判断url中的查询条件
|
||||
// 获取url中的查询条件,判断是不是包含在当前查询条件里
|
||||
checkInTableColumns() {
|
||||
const routeQuery = this.getUrlQuery ? this.$route?.query : {}
|
||||
const routeQueryKeys = Object.keys(routeQuery)
|
||||
const routeQueryKeysLength = routeQueryKeys.length
|
||||
const keys = {}
|
||||
if (routeQueryKeys.length < 1) return keys
|
||||
if (routeQueryKeys.length > 0) {
|
||||
for (let i = 0; i < routeQueryKeys.length; i++) {
|
||||
if (routeQueryKeysLength < 1) return keys
|
||||
if (routeQueryKeysLength > 0) {
|
||||
for (let i = 0; i < routeQueryKeysLength; i++) {
|
||||
const key = routeQueryKeys[i]
|
||||
const valueDecode = decodeURI(routeQuery[key])
|
||||
let valueDecode = decodeURI(routeQuery[key])
|
||||
const isSearch = key !== 'search'
|
||||
for (let k = 0, len = this.options.length; k < len; k++) {
|
||||
const cur = this.options[k]
|
||||
const curOptions = this.options || []
|
||||
for (let k = 0, len = curOptions.length; k < len; k++) {
|
||||
const cur = curOptions[k]
|
||||
if (cur?.type === 'boolean') {
|
||||
valueDecode = !!valueDecode
|
||||
}
|
||||
if (key === cur.value || !isSearch) {
|
||||
const curChildren = cur.children || []
|
||||
keys[key] = {
|
||||
...cur,
|
||||
key,
|
||||
label: isSearch ? cur.label : '',
|
||||
value: valueDecode
|
||||
}
|
||||
if (isSearch && curChildren.length > 0) {
|
||||
curChildren.forEach(item => {
|
||||
for (const item of curChildren) {
|
||||
if (valueDecode === item.value) {
|
||||
keys[key].valueLabel = item.label
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,22 +223,28 @@ export default {
|
||||
this.$nextTick(() => this.$refs.Cascade.handleClear())
|
||||
},
|
||||
handleTagClose(evt) {
|
||||
this.checkUrlFilds(evt)
|
||||
this.$delete(this.filterTags, evt)
|
||||
this.checkUrlFilds(evt)
|
||||
this.$emit('tagSearch', this.filterMaps)
|
||||
return true
|
||||
},
|
||||
handleConfirm() {
|
||||
if (this.filterValue === '') return
|
||||
if (this.filterValue && !this.filterKey) {
|
||||
this.filterKey = 'search'
|
||||
this.filterKey = 'search' + '_' + this.filterValue
|
||||
}
|
||||
const tag = { key: this.filterKey, label: this.keyLabel, value: this.filterValue, valueLabel: this.valueLabel }
|
||||
this.$set(this.filterTags, this.filterKey, tag)
|
||||
this.$emit('tagSearch', this.filterMaps)
|
||||
|
||||
// 修改查询参数时改变url中保存的参数
|
||||
if (this.getUrlQuery) {
|
||||
let newQuery = _.cloneDeep(this.$route.query)
|
||||
newQuery = { ...newQuery, [this.filterKey]: encodeURI(this.filterValue) }
|
||||
if (this.filterKey.startsWith('search')) {
|
||||
newQuery = { ...newQuery, search: encodeURI(this.filterMaps.search) }
|
||||
} else {
|
||||
newQuery = { ...newQuery, [this.filterKey]: encodeURI(this.filterValue) }
|
||||
}
|
||||
this.$router.replace({ query: newQuery })
|
||||
}
|
||||
|
||||
@@ -247,7 +277,15 @@ export default {
|
||||
},
|
||||
// 删除查询条件时改变url
|
||||
checkUrlFilds(evt) {
|
||||
const newQuery = _.omit(this.$route.query, evt)
|
||||
let newQuery = _.omit(this.$route.query, evt)
|
||||
if (this.getUrlQuery && evt.startsWith('search')) {
|
||||
if (newQuery.search) delete newQuery.search
|
||||
const filterMapsSearch = this.filterMaps.search || ''
|
||||
newQuery = {
|
||||
...newQuery,
|
||||
...(filterMapsSearch && { search: encodeURI(filterMapsSearch) })
|
||||
}
|
||||
}
|
||||
this.$router.replace({ query: newQuery })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"vmware_client":"Vmware Client",
|
||||
"custom":"Custom",
|
||||
"mysql": "MySQL",
|
||||
"redis": "Redis",
|
||||
"oracle": "Oracle",
|
||||
"postgresql": "PostgreSQL",
|
||||
"mariadb": "MariaDB",
|
||||
@@ -88,7 +89,9 @@
|
||||
"cluster": "集群",
|
||||
"kubernetes":"Kubernetes",
|
||||
"clusterHelpTextMessage": "例如:https://172.16.8.8:8443",
|
||||
"DBInfo": "数据库信息"
|
||||
"DBInfo": "数据库信息",
|
||||
"RDBProtocol": "关系型数据库",
|
||||
"NoSQLProtocol": "非关系数据库"
|
||||
},
|
||||
"assets": {
|
||||
"AppList": "应用列表",
|
||||
@@ -168,6 +171,7 @@
|
||||
"Other": "其它",
|
||||
"Hardware": "硬件信息",
|
||||
"Password": "密码",
|
||||
"Passphrase": "密钥密码",
|
||||
"PasswordWithoutSpecialCharHelpText": "不能包含特殊字符",
|
||||
"Pending": "等待",
|
||||
"Platform": "系统平台",
|
||||
@@ -220,7 +224,7 @@
|
||||
"sshKeyFingerprint": "SSH 指纹",
|
||||
"ip": "IP",
|
||||
"sshkey": "sshkey",
|
||||
"SSHKey": "SSH 密钥",
|
||||
"SSHSecretKey": "SSH 密钥",
|
||||
"GroupsHelpMessage": "请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)",
|
||||
"HomeHelpMessage": "默认家目录 /home/系统用户名: /home/username",
|
||||
"Home": "家目录",
|
||||
@@ -228,6 +232,8 @@
|
||||
"ipDomain": "IP(域名)",
|
||||
"HostProtocol": "主机协议",
|
||||
"DatabaseProtocol": "数据库协议",
|
||||
"RDBProtocol": "关系型数据库",
|
||||
"NoSQLProtocol": "非关系数据库",
|
||||
"OtherProtocol": "其它协议",
|
||||
"PasswordOrToken": "密码 / 令牌"
|
||||
},
|
||||
@@ -296,6 +302,7 @@
|
||||
"Close": "关闭",
|
||||
"Command filter": "命令过滤器",
|
||||
"Comment": "备注",
|
||||
"Number": "编号",
|
||||
"Confirm": "确认",
|
||||
"Create": "创建",
|
||||
"CreatedBy": "创建者",
|
||||
@@ -784,6 +791,7 @@
|
||||
"accountName": "账户名称",
|
||||
"active": "激活中",
|
||||
"alive": "在线",
|
||||
"noAlive": "离线",
|
||||
"asset": "资产",
|
||||
"target": "目标",
|
||||
"bucket": "桶名称",
|
||||
@@ -839,6 +847,10 @@
|
||||
"riskLevels": {
|
||||
"common": "普通"
|
||||
},
|
||||
"SessionID": "会话ID",
|
||||
"TargetResources": "目标资源",
|
||||
"UseProtocol": "使用协议",
|
||||
"SessionState": "会话状态",
|
||||
"Monitor": "监控",
|
||||
"XRDPNotSupport": "RDP 客户端会话, 暂不支持监控",
|
||||
"sessionMonitor": "监控",
|
||||
@@ -1302,6 +1314,10 @@
|
||||
"Name": "名称",
|
||||
"NodeAmount": "节点数量",
|
||||
"PasswordLength": "密码长度",
|
||||
"ChangePassword": "更改密码",
|
||||
"ModifySSHKey": "修改 SSH Key",
|
||||
"Addressee": "收件人",
|
||||
"OnlyMailSend": "当前只支持邮件发送",
|
||||
"PasswordStrategy": "密码策略",
|
||||
"SecretKeyStrategy": "密钥策略",
|
||||
"RegularlyPerform": "定期执行",
|
||||
@@ -1314,6 +1330,17 @@
|
||||
"TimerPeriod": "定时执行周期",
|
||||
"Username": "用户名"
|
||||
},
|
||||
"AccountBackupPlan": {
|
||||
"Types": "类型",
|
||||
"Backup": "备份",
|
||||
"AccountBackupPlan": "账号备份",
|
||||
"AccountBackupPlanCreate": "创建账号备份",
|
||||
"AccountBackupPlanUpdate": "更新账号备份",
|
||||
"ExecutionDetail": "执行详情",
|
||||
"MailRecipient": "邮件收件人",
|
||||
"IsSuccess": "是否成功",
|
||||
"Reason": "原因"
|
||||
},
|
||||
"Cloud": {
|
||||
"ServerAccountKey": "服务账号密钥",
|
||||
"IPNetworkSegment": "IP网段",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"vmware_client":"Vmware Client",
|
||||
"custom":"Custom",
|
||||
"mysql": "MySQL",
|
||||
"redis": "Redis",
|
||||
"oracle": "Oracle",
|
||||
"postgresql": "PostgreSQL",
|
||||
"mariadb": "MariaDB",
|
||||
@@ -83,7 +84,9 @@
|
||||
"cluster": "Cluster",
|
||||
"kubernetes":"Kubernetes",
|
||||
"clusterHelpTextMessage": "Tips: https://172.16.8.8:8443",
|
||||
"DBInfo": "Database Info"
|
||||
"DBInfo": "Database Info",
|
||||
"RDBProtocol": "RDS Protocol",
|
||||
"NoSQLProtocol": "NoSQL Protocol"
|
||||
},
|
||||
"assets": {
|
||||
"AppList": "Application list",
|
||||
@@ -165,6 +168,7 @@
|
||||
"Os": "Os",
|
||||
"Other": "Other",
|
||||
"Password": "Password",
|
||||
"Passphrase": "Passphrase",
|
||||
"PasswordWithoutSpecialCharHelpText": "Password can't has special chars ",
|
||||
"Pending": "Pending",
|
||||
"Platform": "Platform",
|
||||
@@ -215,13 +219,16 @@
|
||||
"sshKeyFingerprint": "SSH fingerprint",
|
||||
"ip": "IP",
|
||||
"sshkey": "sshkey",
|
||||
"SSHSecretKey": "SSh key",
|
||||
"GroupsHelpMessage": "Please fill in user groups, separated by commas if there are multiple user groups(Please fill in the existing user groups)",
|
||||
"HomeHelpMessage": "Default home directory: /home/system username",
|
||||
"Home": "Home",
|
||||
"LinuxUserAffiliateGroup": "Linux user affiliate group",
|
||||
"ipDomain": "IP(Domain)",
|
||||
"ipDomain": "IP(Host)",
|
||||
"HostProtocol": "Host Protocol",
|
||||
"DatabaseProtocol": "Database Protocol",
|
||||
"RDBProtocol": "RDS Protocol",
|
||||
"NoSQLProtocol": "NoSQL Protocol",
|
||||
"OtherProtocol": "Other Protocol",
|
||||
"PasswordOrToken": "Password / Token"
|
||||
},
|
||||
@@ -283,6 +290,7 @@
|
||||
"Close": "Close",
|
||||
"Command filter": "Command filter",
|
||||
"Comment": "Comment",
|
||||
"Number": "Number",
|
||||
"Confirm": "Confirm",
|
||||
"Create": "Create",
|
||||
"CreatedBy": "Created by",
|
||||
@@ -644,7 +652,7 @@
|
||||
"Assets": "Assets",
|
||||
"Audits": "Audits",
|
||||
"BatchCommand": "Batch Command",
|
||||
"BatchCommandLog": "Batch Command Log",
|
||||
"BatchCommandLog": "Batch Command Logs",
|
||||
"CeleryTaskLog": "Celery task log",
|
||||
"CommandExecutions": "CommandExecutions ",
|
||||
"CommandFilterCreate": "Command filter create",
|
||||
@@ -765,6 +773,7 @@
|
||||
"accountName": "Account name",
|
||||
"active": "active",
|
||||
"alive": "alive",
|
||||
"noAlive": "no alive",
|
||||
"asset": "Asset",
|
||||
"target": "Target",
|
||||
"bucket": "Bucket",
|
||||
@@ -820,6 +829,10 @@
|
||||
"riskLevels": {
|
||||
"common": "common"
|
||||
},
|
||||
"SessionID": "Session ID",
|
||||
"TargetResources": "Target resources",
|
||||
"UseProtocol": "Use protocol",
|
||||
"SessionState": "Session state",
|
||||
"Monitor": "Monitor",
|
||||
"XRDPNotSupport": "RDP Client session not support now",
|
||||
"sessionMonitor": "Session Monitor",
|
||||
@@ -1259,6 +1272,10 @@
|
||||
"Name": "Name",
|
||||
"NodeAmount": "Node",
|
||||
"PasswordLength": "Password length",
|
||||
"ChangePassword": "Change password",
|
||||
"ModifySSHKey": "Modify SSH Key",
|
||||
"Addressee": "Addressee",
|
||||
"OnlyMailSend": "Currently only mail sending is supported",
|
||||
"PasswordStrategy": "Password strategy",
|
||||
"SecretKeyStrategy": "Secret key strategy",
|
||||
"RegularlyPerform": "Regularly perform",
|
||||
@@ -1271,6 +1288,17 @@
|
||||
"TimerPeriod": "Timer period",
|
||||
"Username": "Username"
|
||||
},
|
||||
"AccountBackupPlan": {
|
||||
"Types": "Types",
|
||||
"Backup": "Backup",
|
||||
"AccountBackupPlan": "Account backup plan",
|
||||
"AccountBackupPlanreate": "Account backup plan",
|
||||
"AccountBackupPlanUpdate": "Account backup plan",
|
||||
"ExecutionDetail": "Execution detail",
|
||||
"MailRecipient": "Mail recipient",
|
||||
"IsSuccess": "Is success",
|
||||
"Reason": "Reason"
|
||||
},
|
||||
"Cloud": {
|
||||
"ServerAccountKey": "Server Account Key",
|
||||
"IPNetworkSegment": "Ip Network Segment",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<div class="pull-right">
|
||||
Version <strong> dev </strong> <span v-if="!publicSettings.XPACK_LICENSE_IS_VALID"> GPLv2. </span>
|
||||
</div>
|
||||
<div v-if="!publicSettings.XPACK_LICENSE_IS_VALID" style="padding-left:20px;">
|
||||
<strong>Copyright</strong> FIT2CLOUD 飞致云 © 2014-2021
|
||||
<div style="padding-left:20px;">
|
||||
{{ publicSettings.XPACK_LICENSE_INFO.corporation }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -12,6 +12,11 @@
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'Footer',
|
||||
data() {
|
||||
return {
|
||||
curYear: this.$moment().year() || ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'sidebar',
|
||||
|
||||
@@ -283,8 +283,8 @@ export default {
|
||||
let object = this.object
|
||||
if (!object || Object.keys(object).length === 0) {
|
||||
if (cloneFrom) {
|
||||
this.$log.debug('Clone from: ', cloneFrom)
|
||||
const url = `${this.url}${cloneFrom}/`
|
||||
const [curUrl, query] = this.url.split('?')
|
||||
const url = `${curUrl}${cloneFrom}/${query ? ('?' + query) : ''}`
|
||||
object = await this.getObjectDetail(url)
|
||||
if (object['name']) {
|
||||
object.name = this.$t('common.cloneFrom') + ' ' + object.name
|
||||
|
||||
@@ -161,5 +161,54 @@ export default [
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'backup',
|
||||
component: empty,
|
||||
redirect: '',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlan') },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/index.vue'),
|
||||
name: 'AccountBackupPlanIndex',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlan'), activeMenu: '/accounts/backup' }
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanList.vue'),
|
||||
name: 'AccountBackupPlanList',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlan'), activeMenu: '/accounts/backup' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'create',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanCreateUpdate.vue'),
|
||||
name: 'AccountBackupPlanCreate',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlanCreate'), activeMenu: '/accounts/backup', action: 'create' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id/update',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanCreateUpdate.vue'),
|
||||
name: 'AccountBackupPlanUpdate',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlanUpdate'), activeMenu: '/accounts/backup', action: 'update' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanDetail/index.vue'),
|
||||
name: 'AccountBackupPlanDetail',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.AccountBackupPlan'), activeMenu: '/accounts/backup' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'plan-execution/:id',
|
||||
component: () => import('@/views/accounts/AccountBackupPlan/AccountBackupPlanDetail/AccountBackupPlanExecution/AccountBackupPlanExecutionDetail/index.vue'),
|
||||
name: 'AccountBackupPlanExecutionDetail',
|
||||
meta: { title: i18n.t('xpack.AccountBackupPlan.ExecutionDetail'), activeMenu: '/accounts/backup' },
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -115,8 +115,7 @@ export default {
|
||||
title: this.$t('users.SetMFA'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('common.Setting'),
|
||||
disabled: this.object.mfa_force_enabled
|
||||
label: this.$t('common.Setting')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
|
||||
@@ -110,3 +110,24 @@ export function param2Obj(url) {
|
||||
export function getDateTimeStamp(dateStr) {
|
||||
return Date.parse(dateStr.replace(/-/gi, '/'))
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动滚动到错误位置
|
||||
* @param {*} el 目标元素
|
||||
* @param {Object} 滚动参数 scrollOption={
|
||||
* behavior: 'smooth',
|
||||
* block: 'center'
|
||||
* }
|
||||
*/
|
||||
export const scrollToError = (
|
||||
el,
|
||||
scrollOption = {
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
}
|
||||
) => {
|
||||
setTimeout(() => {
|
||||
const isError = el.getElementsByClassName('is-error')
|
||||
isError[0].scrollIntoView(scrollOption)
|
||||
}, 0)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<GenericCreateUpdatePage v-bind="$data" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import getFields from '@/views/accounts/AccountBackupPlan/fields'
|
||||
import FormTypeField from './components/FormTypeField'
|
||||
|
||||
export default {
|
||||
name: 'AccountBackupPlanUpdate',
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
data() {
|
||||
const fields = getFields.bind(this)()
|
||||
return {
|
||||
url: '/api/v1/assets/backup/',
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name']],
|
||||
[this.$t('xpack.AccountBackupPlan.Types'), ['types']],
|
||||
[this.$t('xpack.AccountBackupPlan.Backup'), ['recipients']],
|
||||
[this.$t('xpack.Timer'), ['is_periodic', 'crontab', 'interval']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
initial: {
|
||||
is_periodic: true,
|
||||
interval: 24,
|
||||
types: ['all', 'asset', 'application']
|
||||
},
|
||||
fieldsMeta: {
|
||||
is_periodic: fields.is_periodic,
|
||||
crontab: fields.crontab,
|
||||
interval: fields.interval,
|
||||
recipients: fields.recipients,
|
||||
types: {
|
||||
label: this.$t('xpack.AccountBackupPlan.Types'),
|
||||
component: FormTypeField
|
||||
}
|
||||
},
|
||||
createSuccessNextRoute: { name: 'AccountBackupPlanIndex' },
|
||||
updateSuccessNextRoute: { name: 'AccountBackupPlanIndex' },
|
||||
cleanFormValue(data) {
|
||||
if (data['interval'] === '') {
|
||||
delete data['interval']
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="14" :sm="24">
|
||||
<DetailCard :items="detailItems" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DetailCard from '@/components/DetailCard'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'AccountBackupPlanExecutionInfo',
|
||||
components: {
|
||||
DetailCard
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
detailItems() {
|
||||
return [
|
||||
{
|
||||
key: this.$t('xpack.ChangeAuthPlan.TimeDelta'),
|
||||
value: this.object.timedelta.toFixed(2) + 's'
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.ChangeAuthPlan.DateStart'),
|
||||
value: toSafeLocalDateStr(this.object.date_start)
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.AccountBackupPlan.IsSuccess'),
|
||||
value: this.object.is_success
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.AccountBackupPlan.Reason'),
|
||||
value: this.object.reason
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.ChangeAuthPlan.MailRecipient'),
|
||||
value: this.object.recipients ? this.object.recipients.map(
|
||||
i => `${i[0]}` + `${i[1] ? ': ' + this.$t('xpack.ChangeAuthPlan.ContainAttachment') : ''}`).join(', ') : ''
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<GenericDetailPage :object.sync="execution" :active-menu.sync="config.activeMenu" v-bind="config" v-on="$listeners">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="execution" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericDetailPage } from '@/layout/components'
|
||||
import AccountBackupPlanExecutionInfo from './AccountBackupPlanExecutionInfo'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
AccountBackupPlanExecutionInfo
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
execution: { id: '' },
|
||||
config: {
|
||||
activeMenu: 'AccountBackupPlanExecutionInfo',
|
||||
actions: {
|
||||
detailApiUrl: `/api/v1/assets/backup-execution/${this.$route.params.id}/`,
|
||||
hasUpdate: false,
|
||||
hasDelete: false
|
||||
},
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('common.BasicInfo'),
|
||||
name: 'AccountBackupPlanExecutionInfo'
|
||||
}
|
||||
],
|
||||
getTitle: this.getExecutionTitle
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getExecutionTitle() {
|
||||
return `${this.$route.meta.title}: ${this.execution.id}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<GenericListTable :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
|
||||
export default {
|
||||
name: 'AccountBackupPlanExecution',
|
||||
components: {
|
||||
GenericListTable
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: `/api/v1/assets/backup-execution/?plan_id=${this.object.id}`,
|
||||
columns: [
|
||||
'timedelta', 'trigger_display', 'date_start', 'is_success', 'reason', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
timedelta: {
|
||||
label: this.$t('xpack.ChangeAuthPlan.TimeDelta'),
|
||||
width: '90px',
|
||||
formatter: function(row) {
|
||||
return row.timedelta.toFixed(2) + 's'
|
||||
}
|
||||
},
|
||||
date_start: {
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
actions: {
|
||||
formatterArgs: {
|
||||
hasDelete: false,
|
||||
hasUpdate: false,
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'log',
|
||||
type: 'primary',
|
||||
title: this.$t('xpack.ChangeAuthPlan.Log'),
|
||||
callback: function({ row }) {
|
||||
window.open(`/#/ops/celery/task/${row.id}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'detail',
|
||||
title: this.$t('xpack.ChangeAuthPlan.Detail'),
|
||||
type: 'info',
|
||||
callback: function({ row }) {
|
||||
return this.$router.push({ name: 'AccountBackupPlanExecutionDetail', params: { id: row.id }})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasSearch: true,
|
||||
hasRefresh: true,
|
||||
hasRightActions: true,
|
||||
hasLeftActions: true,
|
||||
hasMoreActions: false,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasCreate: false,
|
||||
hasBulkDelete: false,
|
||||
hasBulkUpdate: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="14" :sm="24">
|
||||
<DetailCard :items="detailItems" />
|
||||
</el-col>
|
||||
<el-col :md="10" :sm="24">
|
||||
<QuickActions :actions="quickActions" type="primary" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { DetailCard, QuickActions } from '@/components'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'AccountBackupPlanInfo',
|
||||
components: {
|
||||
DetailCard,
|
||||
QuickActions
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
quickActions: [
|
||||
{
|
||||
title: this.$t('xpack.ChangeAuthPlan.ManualExecutePlan'),
|
||||
attrs: {
|
||||
type: 'primary',
|
||||
label: this.$t('xpack.ChangeAuthPlan.Execute')
|
||||
},
|
||||
callbacks: {
|
||||
click: function() {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/backup-execution/`,
|
||||
{ plan: this.object.id }
|
||||
).then(res => {
|
||||
window.open(`/#/ops/celery/task/${res.task}/log/`, '_blank', 'toolbar=yes, width=900, height=600')
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
detailItems() {
|
||||
return [
|
||||
{
|
||||
key: this.$t('xpack.ChangeAuthPlan.Name'),
|
||||
value: this.object.name
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.ChangeAuthPlan.RegularlyPerform'),
|
||||
value: this.object.crontab,
|
||||
formatter: (item, val) => {
|
||||
return <span>{this.object.is_periodic ? val : ''}</span>
|
||||
}
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.ChangeAuthPlan.CyclePerform'),
|
||||
value: this.object.interval,
|
||||
formatter: (item, val) => {
|
||||
return <span>{this.object.is_periodic ? val : ''}</span>
|
||||
}
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.ChangeAuthPlan.DateJoined'),
|
||||
value: toSafeLocalDateStr(this.object.date_created)
|
||||
},
|
||||
{
|
||||
key: this.$t('xpack.ChangeAuthPlan.DateUpdated'),
|
||||
value: toSafeLocalDateStr(this.object.date_updated)
|
||||
},
|
||||
{
|
||||
key: this.$t('common.Comment'),
|
||||
value: this.object.comment
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<GenericDetailPage :object.sync="plan" :active-menu.sync="config.activeMenu" v-bind="config">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" :object="plan" />
|
||||
</keep-alive>
|
||||
</GenericDetailPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericDetailPage } from '@/layout/components'
|
||||
import AccountBackupPlanInfo from './AccountBackupPlanInfo'
|
||||
import AccountBackupPlanExecutionList from './AccountBackupPlanExecution/AccountBackupPlanExecutionList'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericDetailPage,
|
||||
AccountBackupPlanInfo,
|
||||
AccountBackupPlanExecutionList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
plan: { name: '', comment: '' },
|
||||
config: {
|
||||
activeMenu: 'AccountBackupPlanInfo',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('common.BasicInfo'),
|
||||
name: 'AccountBackupPlanInfo'
|
||||
},
|
||||
{
|
||||
title: this.$t('xpack.ChangeAuthPlan.ExecutionList'),
|
||||
name: 'AccountBackupPlanExecutionList'
|
||||
}
|
||||
],
|
||||
actions: {
|
||||
detailApiUrl: `/api/v1/assets/backup/${this.$route.params.id}/`,
|
||||
deleteApiUrl: `/api/v1/assets/backup/${this.$route.params.id}/`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<GenericListTable :table-config="tableConfig" :header-actions="headerActions" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericListTable } from '@/layout/components'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'AccountBackupPlanList',
|
||||
components: {
|
||||
GenericListTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/assets/backup/',
|
||||
columns: [
|
||||
'name', 'is_periodic', 'periodic_display', 'org_name', 'comment', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'org_name', 'is_periodic', 'periodic_display', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
route: 'AccountBackupPlanDetail'
|
||||
}
|
||||
},
|
||||
is_periodic: {
|
||||
label: vm.$t('xpack.ChangeAuthPlan.Timer'),
|
||||
formatterArgs: {
|
||||
showFalse: false
|
||||
},
|
||||
width: '80px'
|
||||
},
|
||||
periodic_display: {
|
||||
label: vm.$t('xpack.ChangeAuthPlan.TimerPeriod'),
|
||||
showOverflowTooltip: true,
|
||||
width: '150px'
|
||||
},
|
||||
comment: {
|
||||
width: '90px'
|
||||
},
|
||||
actions: {
|
||||
width: '164px',
|
||||
formatterArgs: {
|
||||
onClone: ({ row }) => {
|
||||
vm.$router.push({ name: 'AccountBackupPlanCreate', query: { clone_from: row.id }})
|
||||
},
|
||||
onUpdate: ({ row }) => {
|
||||
vm.$router.push({ name: 'AccountBackupPlanUpdate', params: { id: row.id }})
|
||||
},
|
||||
extraActions: [
|
||||
{
|
||||
title: vm.$t('xpack.Execute'),
|
||||
name: 'execute',
|
||||
type: 'info',
|
||||
callback: function({ row }) {
|
||||
this.$axios.post(
|
||||
`/api/v1/assets/backup-execution/`,
|
||||
{ plan: row.id }
|
||||
).then(res => {
|
||||
openTaskPage(res['task'])
|
||||
})
|
||||
}.bind(this)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasMoreActions: false,
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'AccountBackupPlanCreate'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<el-tree
|
||||
:data="iData"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:default-expand-all="false"
|
||||
:default-checked-keys="value"
|
||||
:props="defaultProps"
|
||||
v-bind="$attrs"
|
||||
@check="handleCheckChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FormTypeField',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
choices: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'label'
|
||||
},
|
||||
fullChoicesTreeNodes: [
|
||||
{
|
||||
id: 'all',
|
||||
label: this.$t('perms.all'),
|
||||
children: [
|
||||
{
|
||||
id: 'asset',
|
||||
label: this.$t('route.AssetAccount')
|
||||
},
|
||||
{
|
||||
id: 'application',
|
||||
label: this.$t('route.ApplicationAccount')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
choicesIDs() {
|
||||
return this.choices.map((v) => v.value)
|
||||
},
|
||||
iData() {
|
||||
this.$log.debug('choices: ', this.choicesIDs)
|
||||
const fullTreeNodes = _.cloneDeep(this.fullChoicesTreeNodes)
|
||||
const treeNodes = this.trimChoicesTreeNodes(fullTreeNodes)
|
||||
this.$log.debug('choicesTreeNodes: ', treeNodes)
|
||||
return treeNodes
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
trimChoicesTreeNodes(treeNodes) {
|
||||
const newTreeNodes = []
|
||||
for (const treeNode of treeNodes) {
|
||||
if (!this.choicesIDs.includes(treeNode.id)) {
|
||||
continue
|
||||
}
|
||||
let children = treeNode.children || []
|
||||
if (children.length !== 0) {
|
||||
children = this.trimChoicesTreeNodes(children)
|
||||
treeNode.children = children
|
||||
}
|
||||
newTreeNodes.push(treeNode)
|
||||
}
|
||||
return newTreeNodes
|
||||
},
|
||||
handleCheckChange(data, obj) {
|
||||
const checkedKeys = obj.checkedKeys
|
||||
if (checkedKeys.length !== 0) {
|
||||
checkedKeys.push('connect')
|
||||
}
|
||||
this.$emit('input', checkedKeys)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
59
src/views/accounts/AccountBackupPlan/fields.js
Normal file
59
src/views/accounts/AccountBackupPlan/fields.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
import { CronTab } from '@/components'
|
||||
|
||||
var validatorInterval = (rule, value, callback) => {
|
||||
if (parseInt(value) < 1) {
|
||||
return callback(new Error(i18n.t('xpack.ChangeAuthPlan.validatorMessage.EnsureThisValueIsGreaterThanOrEqualTo1')))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
function getFields() {
|
||||
const recipients = {
|
||||
label: i18n.t('xpack.ChangeAuthPlan.Addressee'),
|
||||
helpText: i18n.t('xpack.ChangeAuthPlan.OnlyMailSend'),
|
||||
el: {
|
||||
value: [],
|
||||
ajax: {
|
||||
url: '/api/v1/users/users/?fields_size=mini',
|
||||
transformOption: (item) => {
|
||||
return { label: item.name + '(' + item.username + ')', value: item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const is_periodic = {
|
||||
type: 'switch'
|
||||
}
|
||||
|
||||
const crontab = {
|
||||
type: 'cronTab',
|
||||
component: CronTab,
|
||||
label: i18n.t('xpack.RegularlyPerform'),
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_periodic === false
|
||||
},
|
||||
helpText: i18n.t('xpack.HelpText.CrontabOfCreateUpdatePage')
|
||||
}
|
||||
|
||||
const interval = {
|
||||
label: i18n.t('xpack.CyclePerform'),
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_periodic === false
|
||||
},
|
||||
helpText: i18n.t('xpack.HelpText.IntervalOfCreateUpdatePage'),
|
||||
rules: [
|
||||
{ validator: validatorInterval }
|
||||
]
|
||||
}
|
||||
|
||||
return {
|
||||
is_periodic: is_periodic,
|
||||
crontab: crontab,
|
||||
interval: interval,
|
||||
recipients: recipients
|
||||
}
|
||||
}
|
||||
|
||||
export default getFields
|
||||
36
src/views/accounts/AccountBackupPlan/index.vue
Normal file
36
src/views/accounts/AccountBackupPlan/index.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu">
|
||||
<keep-alive>
|
||||
<component :is="config.activeMenu" />
|
||||
</keep-alive>
|
||||
</TabPage>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { TabPage } from '@/layout/components'
|
||||
import AccountBackupPlanList from './AccountBackupPlanList'
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
TabPage,
|
||||
AccountBackupPlanList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
config: {
|
||||
activeMenu: 'AccountBackupPlanList',
|
||||
submenu: [
|
||||
{
|
||||
title: this.$t('xpack.AccountBackupPlan.AccountBackupPlan'),
|
||||
name: 'AccountBackupPlanList'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -17,7 +17,6 @@ export default {
|
||||
GenericTreeListPage, AccountListTable
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
isInit: true,
|
||||
clickedRow: null,
|
||||
@@ -30,24 +29,25 @@ export default {
|
||||
url: '/api/v1/assets/accounts/',
|
||||
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1',
|
||||
callback: {
|
||||
onSelected: function(event, treeNode) {
|
||||
let url = '/api/v1/assets/accounts/'
|
||||
if (treeNode.meta.type === 'node') {
|
||||
const nodeId = treeNode.meta.data.id
|
||||
url = setUrlParam(url, 'asset', '')
|
||||
url = setUrlParam(url, 'node', nodeId)
|
||||
} else if (treeNode.meta.type === 'asset') {
|
||||
const assetId = treeNode.meta.data.id
|
||||
url = setUrlParam(url, 'node', '')
|
||||
url = setUrlParam(url, 'asset', assetId)
|
||||
}
|
||||
setTimeout(() => {
|
||||
vm.accountsUrl = url
|
||||
}, 100)
|
||||
}
|
||||
onSelected: (event, treeNode) => this.getAccountsUrl(event, treeNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAccountsUrl(event, treeNode) {
|
||||
let url = '/api/v1/assets/accounts/'
|
||||
if (treeNode.meta.type === 'node') {
|
||||
const nodeId = treeNode.meta.data.id
|
||||
url = setUrlParam(url, 'asset', '')
|
||||
url = setUrlParam(url, 'node', nodeId)
|
||||
} else if (treeNode.meta.type === 'asset') {
|
||||
const assetId = treeNode.meta.data.id
|
||||
url = setUrlParam(url, 'node', '')
|
||||
url = setUrlParam(url, 'asset', assetId)
|
||||
}
|
||||
this.accountsUrl = url
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { GenericListTable } from '@/layout/components'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { DATABASE } from '@/views/perms/const'
|
||||
import { AppPlanDatabase } from '@/views/perms/const'
|
||||
|
||||
export default {
|
||||
name: 'AppChangeAuthPlanList',
|
||||
@@ -126,7 +126,7 @@ export default {
|
||||
type: option.name.toLowerCase()
|
||||
}})
|
||||
},
|
||||
dropdown: DATABASE
|
||||
dropdown: AppPlanDatabase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export default {
|
||||
tableConfig: {
|
||||
url: `/api/v1/xpack/change-auth-plan/app-plan-execution-subtask/?plan_execution_id=${this.object.id}`,
|
||||
columns: [
|
||||
'app_display', 'system_user_display', 'is_success', 'reason', 'timedelta', 'date_start', 'actions'
|
||||
'app_display', 'system_user_display', 'is_success', 'timedelta', 'date_start', 'reason_display', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
app_display: {
|
||||
@@ -35,6 +35,9 @@ export default {
|
||||
return <router-link to={ to } >{ row.app_display }</router-link>
|
||||
}
|
||||
},
|
||||
reason_display: {
|
||||
label: this.$t('xpack.AccountBackupPlan.Reason')
|
||||
},
|
||||
system_user_display: {
|
||||
label: this.$t('xpack.ChangeAuthPlan.SystemUser')
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
[this.$t('common.Basic'), ['name']],
|
||||
[this.$t('xpack.Asset'), ['username', 'assets', 'nodes']],
|
||||
[this.$t('xpack.ChangeAuthPlan.PasswordStrategy'), ['is_password', 'password_strategy', 'password', 'password_rules']],
|
||||
[this.$t('xpack.ChangeAuthPlan.SecretKeyStrategy'), ['is_ssh_key', 'ssh_key_strategy', 'private_key']],
|
||||
[this.$t('xpack.ChangeAuthPlan.SecretKeyStrategy'), ['is_ssh_key', 'ssh_key_strategy', 'private_key', 'passphrase']],
|
||||
[this.$t('xpack.Timer'), ['is_periodic', 'crontab', 'interval']],
|
||||
[this.$t('common.Other'), ['recipients', 'comment']]
|
||||
],
|
||||
@@ -38,6 +38,7 @@ export default {
|
||||
username: fields.username,
|
||||
assets: fields.assets,
|
||||
password: fields.password,
|
||||
passphrase: fields.passphrase,
|
||||
password_rules: fields.asset_password_rules,
|
||||
private_key: fields.private_key,
|
||||
nodes: fields.nodes,
|
||||
|
||||
@@ -22,7 +22,7 @@ export default {
|
||||
tableConfig: {
|
||||
url: `/api/v1/xpack/change-auth-plan/plan-execution-subtask/?plan_execution_id=${this.object.id}`,
|
||||
columns: [
|
||||
'username', 'asset', 'is_success', 'reason', 'timedelta', 'date_start', 'actions'
|
||||
'username', 'asset', 'is_success', 'timedelta', 'date_start', 'reason_display', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
asset: {
|
||||
@@ -42,6 +42,9 @@ export default {
|
||||
return row.timedelta.toFixed(2) + 's'
|
||||
}
|
||||
},
|
||||
reason_display: {
|
||||
label: this.$t('xpack.AccountBackupPlan.Reason')
|
||||
},
|
||||
actions: {
|
||||
formatterArgs: {
|
||||
hasDelete: false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
import { AssetSelect, CronTab } from '@/components'
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
import { AssetSelect, CronTab, UploadKey } from '@/components'
|
||||
import { Select2, UpdateToken } from '@/components/FormFields'
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
|
||||
var validatorInterval = (rule, value, callback) => {
|
||||
@@ -103,6 +103,14 @@ function getFields() {
|
||||
]
|
||||
}
|
||||
|
||||
const passphrase = {
|
||||
label: i18n.t('assets.Passphrase'),
|
||||
component: UpdateToken,
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_ssh_key === false
|
||||
}
|
||||
}
|
||||
|
||||
const asset_password_rules = {
|
||||
type: 'group',
|
||||
items: generatePasswordRulesItemsFields('asset')
|
||||
@@ -114,11 +122,7 @@ function getFields() {
|
||||
}
|
||||
|
||||
const private_key = {
|
||||
el: {
|
||||
type: 'textarea',
|
||||
placeholder: '-----BEGIN OPENSSH PRIVATE KEY-----',
|
||||
autosize: { minRows: 3 }
|
||||
},
|
||||
component: UploadKey,
|
||||
hidden: (formValue) => {
|
||||
return formValue.is_ssh_key === false
|
||||
},
|
||||
@@ -128,6 +132,8 @@ function getFields() {
|
||||
}
|
||||
|
||||
const recipients = {
|
||||
label: i18n.t('xpack.ChangeAuthPlan.Addressee'),
|
||||
helpText: i18n.t('xpack.ChangeAuthPlan.OnlyMailSend'),
|
||||
el: {
|
||||
value: [],
|
||||
ajax: {
|
||||
@@ -153,10 +159,12 @@ function getFields() {
|
||||
}
|
||||
|
||||
const is_password = {
|
||||
label: i18n.t('xpack.ChangeAuthPlan.ChangePassword'),
|
||||
type: 'switch'
|
||||
}
|
||||
|
||||
const is_ssh_key = {
|
||||
label: i18n.t('xpack.ChangeAuthPlan.ModifySSHKey'),
|
||||
type: 'switch'
|
||||
}
|
||||
|
||||
@@ -225,6 +233,7 @@ function getFields() {
|
||||
password_strategy: password_strategy,
|
||||
ssh_key_strategy: ssh_key_strategy,
|
||||
private_key: private_key,
|
||||
passphrase: passphrase,
|
||||
asset_password_rules: asset_password_rules,
|
||||
database_password_rules: database_password_rules,
|
||||
nodes: nodes,
|
||||
|
||||
@@ -11,6 +11,41 @@ export default {
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
const appType = [
|
||||
{
|
||||
name: 'mysql',
|
||||
title: 'MySQL',
|
||||
has: true,
|
||||
group: this.$t('assets.RDBProtocol')
|
||||
},
|
||||
{
|
||||
name: 'postgresql',
|
||||
title: 'PostgreSQL',
|
||||
has: this.$store.getters.hasValidLicense
|
||||
},
|
||||
{
|
||||
name: 'mariadb',
|
||||
title: 'MariaDB',
|
||||
type: 'primary',
|
||||
has: this.$store.getters.hasValidLicense
|
||||
},
|
||||
{
|
||||
name: 'oracle',
|
||||
title: 'Oracle',
|
||||
has: this.$store.getters.hasValidLicense
|
||||
},
|
||||
{
|
||||
name: 'sqlserver',
|
||||
title: 'SQLServer',
|
||||
has: this.$store.getters.hasValidLicense
|
||||
},
|
||||
{
|
||||
name: 'redis',
|
||||
title: 'Redis',
|
||||
has: true,
|
||||
group: this.$t('assets.NoSQLProtocol')
|
||||
}
|
||||
]
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/applications/applications/?category=db',
|
||||
@@ -24,8 +59,7 @@ export default {
|
||||
},
|
||||
columnsMeta: {
|
||||
type_display: {
|
||||
label: this.$t('applications.type'),
|
||||
width: '120px'
|
||||
label: this.$t('applications.type')
|
||||
},
|
||||
'attrs.host': {
|
||||
label: this.$t('applications.host'),
|
||||
@@ -69,43 +103,37 @@ export default {
|
||||
hasBulkDelete: true,
|
||||
createRoute: 'DatabaseAppCreate',
|
||||
searchConfig: {
|
||||
exclude: ['category', 'type']
|
||||
exclude: ['category', 'type'],
|
||||
options: [
|
||||
{
|
||||
value: 'type',
|
||||
label: this.$t('applications.type'),
|
||||
children: this.getAppType(appType)
|
||||
}
|
||||
]
|
||||
},
|
||||
moreCreates: {
|
||||
callback: (item) => {
|
||||
vm.$router.push({ name: 'DatabaseAppCreate', query: { type: item.name.toLowerCase() }})
|
||||
},
|
||||
dropdown: [
|
||||
{
|
||||
name: 'MySQL',
|
||||
title: 'MySQL',
|
||||
has: true
|
||||
},
|
||||
{
|
||||
name: 'PostgreSQL',
|
||||
title: 'PostgreSQL',
|
||||
has: this.$store.getters.hasValidLicense
|
||||
},
|
||||
{
|
||||
name: 'MariaDB',
|
||||
title: 'MariaDB',
|
||||
type: 'primary',
|
||||
has: this.$store.getters.hasValidLicense
|
||||
},
|
||||
{
|
||||
name: 'Oracle',
|
||||
title: 'Oracle',
|
||||
has: this.$store.getters.hasValidLicense
|
||||
},
|
||||
{
|
||||
name: 'SQLServer',
|
||||
title: 'SQLServer',
|
||||
has: this.$store.getters.hasValidLicense
|
||||
}
|
||||
]
|
||||
dropdown: appType
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAppType(arr) {
|
||||
const searchAppType = []
|
||||
if (arr.length < 1) return searchAppType
|
||||
arr.forEach((i) => {
|
||||
const option = {
|
||||
value: i.name,
|
||||
label: i.title
|
||||
}
|
||||
searchAppType.push(option)
|
||||
})
|
||||
return searchAppType
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -15,12 +15,12 @@ export default {
|
||||
tableConfig: {
|
||||
url: '/api/v1/applications/applications/?category=cloud',
|
||||
columns: [
|
||||
'name', 'type', 'attrs.cluster',
|
||||
'name', 'type_display', 'attrs.cluster',
|
||||
'created_by', 'date_created', 'date_updated', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'type', 'attrs.cluster', 'comment', 'actions']
|
||||
default: ['name', 'type_display', 'attrs.cluster', 'comment', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
'attrs.cluster': {
|
||||
@@ -29,8 +29,8 @@ export default {
|
||||
comment: {
|
||||
width: '340px'
|
||||
},
|
||||
type: {
|
||||
width: '140px'
|
||||
type_display: {
|
||||
label: this.$t('applications.type')
|
||||
},
|
||||
actions: {
|
||||
prop: 'actions',
|
||||
|
||||
@@ -17,17 +17,16 @@ export default {
|
||||
tableConfig: {
|
||||
url: '/api/v1/applications/applications/?category=remote_app',
|
||||
columns: [
|
||||
'name', 'type', 'attrs.asset',
|
||||
'name', 'type_display', 'attrs.asset',
|
||||
'created_by', 'date_created', 'date_updated', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'type', 'attrs.asset', 'comment', 'actions']
|
||||
default: ['name', 'type_display', 'attrs.asset', 'comment', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
type: {
|
||||
displayKey: 'get_type_display',
|
||||
width: '140px'
|
||||
type_display: {
|
||||
label: this.$t('applications.type')
|
||||
},
|
||||
'attrs.asset': {
|
||||
label: this.$t('assets.Assets'),
|
||||
@@ -67,7 +66,14 @@ export default {
|
||||
hasImport: false,
|
||||
// createRoute: 'RemoteAppCreate',
|
||||
searchConfig: {
|
||||
exclude: ['category', 'type']
|
||||
exclude: ['category', 'type'],
|
||||
options: [
|
||||
{
|
||||
value: 'type',
|
||||
label: this.$t('applications.type'),
|
||||
children: this.getCreateAppType()
|
||||
}
|
||||
]
|
||||
},
|
||||
moreCreates: {
|
||||
dropdown: this.getCreateAppType(),
|
||||
@@ -85,6 +91,8 @@ export default {
|
||||
const item = { ...REMOTE_APP_TYPE_META_MAP[value] }
|
||||
item.can = true
|
||||
item.has = true
|
||||
item.value = item.name
|
||||
item.label = item.title
|
||||
extraMoreActions.push(item)
|
||||
}
|
||||
return extraMoreActions
|
||||
|
||||
@@ -88,7 +88,7 @@ export default {
|
||||
'protocols', 'platform', 'hardware_info', 'model',
|
||||
'cpu_model', 'cpu_cores', 'cpu_count', 'cpu_vcpus',
|
||||
'disk_info', 'disk_total', 'memory', 'os', 'os_arch',
|
||||
'os_version', 'number', 'vendor', 'sn',
|
||||
'os_version', 'number', 'vendor', 'sn', 'is_active',
|
||||
'connectivity', 'labels_display',
|
||||
'created_by', 'date_created', 'comment', 'org_name', 'actions'
|
||||
],
|
||||
|
||||
@@ -20,11 +20,17 @@ export default {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: `/api/v1/assets/cmd-filters/${this.object.id}/rules/`,
|
||||
columns: ['type', 'content', 'action', 'priority', 'pattern', 'comment', 'actions'],
|
||||
columns: ['type', 'content', 'ignore_case', 'action', 'priority', 'pattern', 'comment', 'actions'],
|
||||
columnsMeta: {
|
||||
type: {
|
||||
width: '100px'
|
||||
},
|
||||
ignore_case: {
|
||||
width: '100px',
|
||||
formatterArgs: {
|
||||
showFalse: false
|
||||
}
|
||||
},
|
||||
priority: {
|
||||
width: '70px'
|
||||
},
|
||||
|
||||
@@ -28,7 +28,7 @@ export default {
|
||||
action: 0
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['filter', 'type', 'content', 'priority', 'action', 'reviewers', 'comment']]
|
||||
[this.$t('common.Basic'), ['filter', 'type', 'content', 'ignore_case', 'priority', 'action', 'reviewers', 'comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
filter: {
|
||||
|
||||
@@ -20,7 +20,7 @@ export default {
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'ip', 'port', 'protocol', 'domain']],
|
||||
[this.$t('assets.Auth'), ['username', 'password', 'private_key']],
|
||||
[this.$t('assets.Auth'), ['username', 'password', 'private_key', 'passphrase']],
|
||||
[this.$t('common.Other'), ['is_active', 'comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
|
||||
@@ -27,7 +27,7 @@ export default {
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'type']],
|
||||
[this.$t('common.Auth'), ['password', 'private_key']],
|
||||
[this.$t('common.Auth'), ['password', 'private_key', 'passphrase']],
|
||||
[this.$t('common.Command filter'), ['cmd_filters']],
|
||||
[this.$t('common.Other'), ['priority', 'sftp_root', 'comment']]
|
||||
],
|
||||
@@ -63,6 +63,9 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
passphrase: {
|
||||
component: UpdateToken
|
||||
},
|
||||
private_key: {
|
||||
component: UploadKey
|
||||
},
|
||||
|
||||
@@ -25,8 +25,8 @@ export default {
|
||||
auto_push: false
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'login_mode', 'username', 'priority', 'protocol']],
|
||||
[this.$t('common.Auth'), ['password']],
|
||||
[this.$t('common.Basic'), ['name', 'username', 'priority', 'protocol']],
|
||||
[this.$t('common.Auth'), ['login_mode', 'password']],
|
||||
[this.$t('common.Command filter'), ['cmd_filters']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
|
||||
@@ -47,6 +47,7 @@ export default {
|
||||
case 'postgresql':
|
||||
case 'mariadb':
|
||||
case 'sqlserver':
|
||||
case 'redis':
|
||||
return Database
|
||||
case 'k8s':
|
||||
return K8S
|
||||
|
||||
@@ -31,7 +31,7 @@ export default {
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'username_same_with_user']],
|
||||
[this.$t('common.Auth'), ['login_mode', 'auto_generate_key', 'password', 'private_key']],
|
||||
[this.$t('common.Auth'), ['login_mode', 'auto_generate_key', 'password', 'private_key', 'passphrase']],
|
||||
[this.$t('assets.AutoPush'), ['auto_push', 'sudo', 'shell', 'home', 'system_groups']],
|
||||
[this.$t('common.Command filter'), ['cmd_filters']],
|
||||
[this.$t('assets.UserSwitch'), ['su_enabled', 'su_from']],
|
||||
@@ -41,6 +41,7 @@ export default {
|
||||
login_mode: fields.login_mode,
|
||||
username: fields.username,
|
||||
private_key: fields.private_key,
|
||||
passphrase: fields.passphrase,
|
||||
username_same_with_user: fields.username_same_with_user,
|
||||
auto_generate_key: fields.auto_generate_key,
|
||||
protocol: fields.protocol,
|
||||
|
||||
@@ -31,6 +31,8 @@ function getFields() {
|
||||
this.fieldsMeta.username.rules[0].required = false
|
||||
} else if (form.username_same_with_user) {
|
||||
this.fieldsMeta.username.rules[0].required = false
|
||||
} else if (form.protocol === 'redis') {
|
||||
this.fieldsMeta.username.rules[0].required = false
|
||||
} else {
|
||||
this.fieldsMeta.username.rules[0].required = true
|
||||
}
|
||||
@@ -152,7 +154,6 @@ function getFields() {
|
||||
}
|
||||
|
||||
const password = {
|
||||
helpText: this.$t('assets.PasswordHelpMessage'),
|
||||
component: UpdateToken,
|
||||
hidden: form => {
|
||||
if (form.login_mode !== 'auto' || form.auto_generate_key) {
|
||||
@@ -164,6 +165,16 @@ function getFields() {
|
||||
}
|
||||
}
|
||||
|
||||
const passphrase = {
|
||||
component: UpdateToken,
|
||||
hidden: (form) => {
|
||||
if (form.login_mode !== 'auto') {
|
||||
return true
|
||||
}
|
||||
return form.auto_generate_key === true
|
||||
}
|
||||
}
|
||||
|
||||
const system_groups = {
|
||||
label: this.$t('assets.LinuxUserAffiliateGroup'),
|
||||
hidden: (item) => !item.auto_push || item.username_same_with_user,
|
||||
@@ -184,6 +195,7 @@ function getFields() {
|
||||
auto_push: auto_push,
|
||||
update_password: update_password,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
system_groups: system_groups,
|
||||
type: type
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ export default {
|
||||
title: 'MySQL',
|
||||
type: 'primary',
|
||||
has: true,
|
||||
group: this.$t('assets.DatabaseProtocol')
|
||||
group: this.$t('assets.RDBProtocol')
|
||||
},
|
||||
{
|
||||
name: 'PostgreSQL',
|
||||
@@ -131,6 +131,13 @@ export default {
|
||||
type: 'primary',
|
||||
has: this.$store.getters.hasValidLicense
|
||||
},
|
||||
{
|
||||
name: 'Redis',
|
||||
title: 'Redis',
|
||||
type: 'primary',
|
||||
has: true,
|
||||
group: this.$t('assets.NoSQLProtocol')
|
||||
},
|
||||
{
|
||||
name: 'K8S',
|
||||
title: 'K8S',
|
||||
|
||||
@@ -18,7 +18,7 @@ export default {
|
||||
tableConfig: {
|
||||
url: '/api/v1/audits/login-logs/',
|
||||
columns: [
|
||||
'username', 'type', 'backend', 'ip', 'city',
|
||||
'username', 'type', 'backend_display', 'ip', 'city',
|
||||
'user_agent', 'mfa', 'reason_display', 'status', 'datetime'
|
||||
],
|
||||
columnsMeta: {
|
||||
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/audits/operate-logs/',
|
||||
columns: ['user', 'action', 'resource_type', 'resource', 'remote_addr', 'datetime'],
|
||||
columns: ['user', 'action_display', 'resource_type_display', 'resource', 'remote_addr', 'datetime'],
|
||||
columnsMeta: {
|
||||
user: {
|
||||
showOverflowTooltip: true
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
remote_addr: {
|
||||
width: '140px'
|
||||
},
|
||||
action: {
|
||||
action_display: {
|
||||
width: '90px'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -15,10 +15,18 @@ export default {
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/ops/tasks/',
|
||||
columns: ['name', 'runtimes', 'host_amount', 'is_success', 'date_start', 'time', 'actions'],
|
||||
columns: [
|
||||
'name', 'runtimes', 'host_amount', 'is_success',
|
||||
'date_start', 'time', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
showOverflowTooltip: true
|
||||
showOverflowTooltip: true,
|
||||
formatterArgs: {
|
||||
getTitle({ row }) {
|
||||
return row['display_name']
|
||||
}
|
||||
}
|
||||
},
|
||||
runtimes: {
|
||||
label: this.$t('ops.runTimes'),
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { getDayFuture } from '@/utils/common'
|
||||
import PermissionFormActionField from '../components/PermissionFormActionField'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -29,9 +30,10 @@ export default {
|
||||
[this.$t('common.Basic'), ['name']],
|
||||
[this.$t('perms.User'), ['users', 'user_groups']],
|
||||
[this.$t('assets.Applications'), ['category', 'type', 'applications', 'system_users']],
|
||||
[this.$t('common.action'), ['actions']],
|
||||
[this.$t('common.Other'), ['is_active', 'date_start', 'date_expired', 'comment']]
|
||||
],
|
||||
url: '/api/v1/perms/application-permissions/',
|
||||
url: `/api/v1/perms/application-permissions/?category=${this.$route.query.category}&type=${this.$route.query.type}`,
|
||||
fieldsMeta: {
|
||||
users: {
|
||||
el: {
|
||||
@@ -101,7 +103,9 @@ export default {
|
||||
label: this.$t('common.dateExpired')
|
||||
},
|
||||
actions: {
|
||||
label: this.$t('perms.Actions')
|
||||
label: this.$t('perms.Actions'),
|
||||
component: PermissionFormActionField,
|
||||
helpText: this.$t('common.actionsTips')
|
||||
},
|
||||
is_active: {
|
||||
type: 'checkbox'
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import AssetPermissionFormActionField from './components/AssetPermissionFormActionField'
|
||||
import PermissionFormActionField from '../components/PermissionFormActionField'
|
||||
import AssetSelect from '@/components/AssetSelect'
|
||||
import { getDayFuture } from '@/utils/common'
|
||||
|
||||
@@ -24,7 +24,6 @@ export default {
|
||||
return {
|
||||
initial: {
|
||||
is_active: true,
|
||||
actions: ['all', 'connect', 'updownload', 'upload_file', 'download_file'],
|
||||
date_start: new Date().toISOString(),
|
||||
date_expired: getDayFuture(36500, new Date()).toISOString(),
|
||||
nodes: nodesInitial,
|
||||
@@ -92,7 +91,7 @@ export default {
|
||||
},
|
||||
actions: {
|
||||
label: this.$t('perms.Actions'),
|
||||
component: AssetPermissionFormActionField,
|
||||
component: PermissionFormActionField,
|
||||
helpText: this.$t('common.actionsTips')
|
||||
},
|
||||
date_start: {
|
||||
|
||||
@@ -70,6 +70,7 @@ export default {
|
||||
return { label: item.name + '(' + item.username + ')', value: item.id }
|
||||
}
|
||||
},
|
||||
showHasMore: false,
|
||||
hasObjectsId: this.object.users,
|
||||
showHasObjects: false,
|
||||
performAdd: (items) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-tree
|
||||
:data="data"
|
||||
:data="iData"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:default-expand-all="false"
|
||||
@@ -13,11 +13,15 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AssetPermissionFormActionField',
|
||||
name: 'PermissionFormActionField',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => ['all', 'connect', 'upload_file', 'download_file', 'updownload', 'clipboard_copy_paste', 'clipboard_copy', 'clipboard_paste']
|
||||
default: () => []
|
||||
},
|
||||
choices: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -26,7 +30,7 @@ export default {
|
||||
children: 'children',
|
||||
label: 'label'
|
||||
},
|
||||
data: [
|
||||
fullChoicesTreeNodes: [
|
||||
{
|
||||
id: 'all',
|
||||
label: this.$t('perms.all'),
|
||||
@@ -68,7 +72,34 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
choicesIDs() {
|
||||
return this.choices.map((v) => v.value)
|
||||
},
|
||||
iData() {
|
||||
this.$log.debug('choices: ', this.choicesIDs)
|
||||
const fullTreeNodes = _.cloneDeep(this.fullChoicesTreeNodes)
|
||||
const treeNodes = this.trimChoicesTreeNodes(fullTreeNodes)
|
||||
this.$log.debug('choicesTreeNodes: ', treeNodes)
|
||||
return treeNodes
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
trimChoicesTreeNodes(treeNodes) {
|
||||
const newTreeNodes = []
|
||||
for (const treeNode of treeNodes) {
|
||||
if (!this.choicesIDs.includes(treeNode.id)) {
|
||||
continue
|
||||
}
|
||||
let children = treeNode.children || []
|
||||
if (children.length !== 0) {
|
||||
children = this.trimChoicesTreeNodes(children)
|
||||
treeNode.children = children
|
||||
}
|
||||
newTreeNodes.push(treeNode)
|
||||
}
|
||||
return newTreeNodes
|
||||
},
|
||||
handleCheckChange(data, obj) {
|
||||
const checkedKeys = obj.checkedKeys
|
||||
if (checkedKeys.length !== 0) {
|
||||
@@ -43,6 +43,7 @@ export const REMOTE_APP = [
|
||||
]
|
||||
|
||||
export const MYSQL = 'mysql'
|
||||
export const REDIS = 'redis'
|
||||
export const ORACLE = 'oracle'
|
||||
export const POSTGRESQL = 'postgresql'
|
||||
export const MARIADB = 'mariadb'
|
||||
@@ -56,7 +57,54 @@ export const DATABASE = [
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: true,
|
||||
group: i18n.t('applications.Database')
|
||||
group: i18n.t('applications.RDBProtocol')
|
||||
},
|
||||
{
|
||||
name: ORACLE,
|
||||
title: i18n.t(`applications.applicationsType.${ORACLE}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: hasLicence
|
||||
},
|
||||
{
|
||||
name: POSTGRESQL,
|
||||
title: i18n.t(`applications.applicationsType.${POSTGRESQL}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: hasLicence
|
||||
},
|
||||
{
|
||||
name: MARIADB,
|
||||
title: i18n.t(`applications.applicationsType.${MARIADB}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: hasLicence
|
||||
},
|
||||
{
|
||||
name: SQLSERVER,
|
||||
title: i18n.t(`applications.applicationsType.${SQLSERVER}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: hasLicence
|
||||
},
|
||||
{
|
||||
name: REDIS,
|
||||
title: i18n.t(`applications.applicationsType.${REDIS}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: true,
|
||||
group: i18n.t('applications.NoSQLProtocol')
|
||||
}
|
||||
]
|
||||
|
||||
export const AppPlanDatabase = [
|
||||
{
|
||||
name: MYSQL,
|
||||
title: i18n.t(`applications.applicationsType.${MYSQL}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: true,
|
||||
group: i18n.t('applications.RDBProtocol')
|
||||
},
|
||||
{
|
||||
name: ORACLE,
|
||||
|
||||
@@ -49,7 +49,7 @@ export default {
|
||||
}
|
||||
},
|
||||
columns: [
|
||||
'expandCol', 'input', 'risk_level', 'user',
|
||||
'expandCol', 'input', 'risk_level', 'user', 'remote_addr',
|
||||
'asset', 'system_user', 'session', 'timestamp'
|
||||
],
|
||||
extraQuery: {
|
||||
@@ -76,7 +76,6 @@ export default {
|
||||
}
|
||||
},
|
||||
user: {
|
||||
width: '140px',
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
asset: {
|
||||
@@ -210,7 +209,6 @@ export default {
|
||||
const queryStr = (url.indexOf('?') > -1 ? '&' : '?') + queryUtil.stringify(_query, '=', '&')
|
||||
const treeUrl = url + queryStr
|
||||
this.$set(this.treeSetting, 'treeUrl', treeUrl)
|
||||
this.treeTable.forceRerenderTree()
|
||||
},
|
||||
handleDateChange(object) {
|
||||
this.query = {
|
||||
|
||||
@@ -84,7 +84,7 @@ export default {
|
||||
// 这个页面不去提交auth这些
|
||||
const removeFields = [
|
||||
'AUTH_CAS', 'AUTH_OPENID', 'AUTH_WECOM', 'AUTH_DINGTALK',
|
||||
'AUTH_FEISHU', 'AUTH_RADIUS', 'AUTH_SSO'
|
||||
'AUTH_FEISHU', 'AUTH_RADIUS', 'AUTH_SSO', 'AUTH_SAML2'
|
||||
]
|
||||
for (const i of removeFields) {
|
||||
delete data[i]
|
||||
|
||||
@@ -105,11 +105,17 @@ export default {
|
||||
settings: {
|
||||
visible: false,
|
||||
url: '/api/v1/settings/setting/?category=ldap',
|
||||
fields: ['AUTH_LDAP_SYNC_IS_PERIODIC', 'AUTH_LDAP_SYNC_INTERVAL', 'AUTH_LDAP_SYNC_CRONTAB'],
|
||||
fields: ['AUTH_LDAP_SYNC_IS_PERIODIC', 'AUTH_LDAP_SYNC_CRONTAB', 'AUTH_LDAP_SYNC_INTERVAL'],
|
||||
fieldsMeta: {
|
||||
AUTH_LDAP_SYNC_IS_PERIODIC: {
|
||||
type: 'switch'
|
||||
},
|
||||
AUTH_LDAP_SYNC_CRONTAB: {
|
||||
component: CronTab,
|
||||
helpText: this.$t('xpack.HelpText.CrontabOfCreateUpdatePage')
|
||||
},
|
||||
AUTH_LDAP_SYNC_INTERVAL: {
|
||||
helpText: this.$t('xpack.HelpText.IntervalOfCreateUpdatePage')
|
||||
}
|
||||
},
|
||||
submitMethod: () => 'patch'
|
||||
|
||||
@@ -41,6 +41,7 @@ export default {
|
||||
[
|
||||
'SECURITY_MFA_AUTH',
|
||||
'SECURITY_MFA_IN_LOGIN_PAGE',
|
||||
'SECURITY_MFA_AUTH_ENABLED_FOR_THIRD_PARTY',
|
||||
'SECURITY_LOGIN_CHALLENGE_ENABLED',
|
||||
'SECURITY_LOGIN_CAPTCHA_ENABLED',
|
||||
'SECURITY_PASSWORD_EXPIRATION_TIME',
|
||||
|
||||
@@ -75,7 +75,7 @@ export default {
|
||||
key: this.$t('tickets.ApplyFromSession'),
|
||||
value: this.object.meta.apply_from_session_id,
|
||||
formatter: function(item, value) {
|
||||
const to = { name: 'SessionDetail', params: { id: value }}
|
||||
const to = { name: 'SessionDetail', params: { id: value }, query: { oid: vm.object.org_id }}
|
||||
return <router-link to={to}>{vm.$t('sessions.session')}</router-link>
|
||||
}
|
||||
},
|
||||
|
||||
@@ -132,6 +132,10 @@ export default {
|
||||
{
|
||||
label: 'SQLServer',
|
||||
value: 'sqlserver'
|
||||
},
|
||||
{
|
||||
label: 'Redis',
|
||||
value: 'redis'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -232,10 +236,14 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
performSubmit(validValues) {
|
||||
const applyCategoryType = validValues.meta.apply_category_type
|
||||
validValues.meta.apply_category = applyCategoryType && applyCategoryType.length > 0 ? applyCategoryType[0] : ''
|
||||
validValues.meta.apply_type = applyCategoryType && applyCategoryType.length > 0 ? applyCategoryType[1] : ''
|
||||
delete validValues.meta['apply_category_type']
|
||||
const validMeta = validValues.meta
|
||||
const applyCategoryType = validMeta.apply_category_type
|
||||
const filter = (len, field) => {
|
||||
return applyCategoryType && applyCategoryType.length > 0 ? applyCategoryType[len] : validMeta[field]
|
||||
}
|
||||
validMeta.apply_category = filter(0, 'apply_category')
|
||||
validMeta.apply_type = filter(1, 'apply_type')
|
||||
delete validMeta['apply_category_type']
|
||||
return this.$axios['post'](`/api/v1/tickets/tickets/open/?type=apply_application`, validValues)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,66 +40,73 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
detailCardItems() {
|
||||
const obj = this.object || {}
|
||||
return [
|
||||
{
|
||||
key: this.$t('common.Number'),
|
||||
value: obj.serial_num
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.status'),
|
||||
value: this.object.status,
|
||||
value: obj.status,
|
||||
formatter: (item, val) => {
|
||||
return <el-tag type={this.statusMap.type} size='mini'> { this.statusMap.title }</el-tag>
|
||||
}
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.type'),
|
||||
value: this.object.type_display
|
||||
value: obj.type_display
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.user'),
|
||||
value: this.object['applicant_display']
|
||||
value: obj['applicant_display']
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.OrgName'),
|
||||
value: this.object['org_name']
|
||||
value: obj['org_name']
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateCreated'),
|
||||
value: toSafeLocalDateStr(this.object.date_created)
|
||||
value: toSafeLocalDateStr(obj.date_created)
|
||||
},
|
||||
{
|
||||
key: this.$t('common.Comment'),
|
||||
value: this.object.comment
|
||||
value: obj.comment
|
||||
}
|
||||
]
|
||||
},
|
||||
specialCardItems() {
|
||||
const meta = this.object.meta || {}
|
||||
return [
|
||||
{
|
||||
key: this.$t('applications.appType'),
|
||||
value: `${this.object.meta['apply_category_display']} / ${this.object.meta['apply_type_display']} `
|
||||
value: `${meta['apply_category_display']} / ${meta['apply_type_display']} `
|
||||
},
|
||||
{
|
||||
key: this.$t('applications.appName'),
|
||||
value: this.object.meta.apply_applications_display.join(', ')
|
||||
value: meta?.apply_applications_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.SystemUser'),
|
||||
value: this.object.meta.apply_system_users_display.join(', ')
|
||||
value: meta?.apply_system_users_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateStart'),
|
||||
value: toSafeLocalDateStr(this.object.meta.apply_date_start)
|
||||
value: toSafeLocalDateStr(meta.apply_date_start)
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateExpired'),
|
||||
value: toSafeLocalDateStr(this.object.meta.apply_date_expired)
|
||||
value: toSafeLocalDateStr(meta.apply_date_expired)
|
||||
}
|
||||
]
|
||||
},
|
||||
assignedCardItems() {
|
||||
const vm = this
|
||||
const meta = this.object.meta || {}
|
||||
return [
|
||||
{
|
||||
key: this.$t('tickets.PermissionName'),
|
||||
value: this.object.meta.apply_permission_name,
|
||||
value: meta.apply_permission_name,
|
||||
formatter: function(item, value) {
|
||||
const to = { name: 'ApplicationPermissionDetail', params: { id: vm.object.id }, query: { oid: vm.object.org_id }}
|
||||
if (vm.object.status === 'closed' && vm.object.state === 'approved') {
|
||||
@@ -111,19 +118,19 @@ export default {
|
||||
},
|
||||
{
|
||||
key: this.$t('applications.appName'),
|
||||
value: this.object.meta.apply_applications_display.join(', ')
|
||||
value: meta?.apply_applications_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.SystemUser'),
|
||||
value: this.object.meta.apply_system_users_display.join(', ')
|
||||
value: meta?.apply_system_users_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateStart'),
|
||||
value: toSafeLocalDateStr(this.object.meta.apply_date_start)
|
||||
value: toSafeLocalDateStr(meta.apply_date_start)
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateExpired'),
|
||||
value: toSafeLocalDateStr(this.object.meta.apply_date_expired)
|
||||
value: toSafeLocalDateStr(meta.apply_date_expired)
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import Select2 from '@/components/FormFields/Select2'
|
||||
import { getDaysFuture } from '@/utils/common'
|
||||
import AssetPermissionFormActionField from '@/views/perms/AssetPermission/components/AssetPermissionFormActionField'
|
||||
import PermissionFormActionField from '@/views/perms/components/PermissionFormActionField'
|
||||
export default {
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
fieldsMeta: {
|
||||
apply_actions: {
|
||||
label: this.$t('perms.Actions'),
|
||||
component: AssetPermissionFormActionField,
|
||||
component: PermissionFormActionField,
|
||||
helpText: this.$t('common.actionsTips')
|
||||
},
|
||||
apply_nodes: {
|
||||
|
||||
@@ -38,66 +38,73 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
detailCardItems() {
|
||||
const obj = this.object || {}
|
||||
return [
|
||||
{
|
||||
key: this.$t('common.Number'),
|
||||
value: obj.serial_num
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.status'),
|
||||
value: this.object.state,
|
||||
value: obj.state,
|
||||
formatter: (item, val) => {
|
||||
return <el-tag type={this.statusMap.type} size='mini'> { this.statusMap.title }</el-tag>
|
||||
}
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.type'),
|
||||
value: this.object.type_display
|
||||
value: obj.type_display
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.user'),
|
||||
value: this.object['applicant_display']
|
||||
value: obj['applicant_display']
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.OrgName'),
|
||||
value: this.object.org_name
|
||||
value: obj.org_name
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateCreated'),
|
||||
value: toSafeLocalDateStr(this.object.date_created)
|
||||
value: toSafeLocalDateStr(obj.date_created)
|
||||
},
|
||||
{
|
||||
key: this.$t('common.Comment'),
|
||||
value: this.object.comment
|
||||
value: obj.comment
|
||||
}
|
||||
]
|
||||
},
|
||||
specialCardItems() {
|
||||
const meta = this.object.meta || {}
|
||||
return [
|
||||
{
|
||||
key: this.$t('perms.Node'),
|
||||
value: this.object.meta.apply_nodes_display.join(', ')
|
||||
value: meta?.apply_nodes_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.Asset'),
|
||||
value: this.object.meta.apply_assets_display.join(', ')
|
||||
value: meta?.apply_assets_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.SystemUser'),
|
||||
value: this.object.meta.apply_system_users_display.join(', ')
|
||||
value: meta?.apply_system_users_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Action'),
|
||||
value: forMatAction(this, this.object.meta['apply_actions_display'])
|
||||
value: forMatAction(this, meta['apply_actions_display'])
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateStart'),
|
||||
value: toSafeLocalDateStr(this.object.meta.apply_date_start)
|
||||
value: toSafeLocalDateStr(meta.apply_date_start)
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateExpired'),
|
||||
value: toSafeLocalDateStr(this.object.meta.apply_date_expired)
|
||||
value: toSafeLocalDateStr(meta.apply_date_expired)
|
||||
}
|
||||
]
|
||||
},
|
||||
assignedCardItems() {
|
||||
const vm = this
|
||||
const meta = this.object.meta || {}
|
||||
return [
|
||||
{
|
||||
key: this.$t('tickets.PermissionName'),
|
||||
@@ -113,27 +120,27 @@ export default {
|
||||
},
|
||||
{
|
||||
key: this.$t('perms.Node'),
|
||||
value: this.object.meta.apply_nodes_display.join(', ')
|
||||
value: meta?.apply_nodes_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Asset'),
|
||||
value: this.object.meta.apply_assets_display.join(', ')
|
||||
value: meta?.apply_assets_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('tickets.SystemUser'),
|
||||
value: this.object.meta.apply_system_users_display.join(', ')
|
||||
value: meta?.apply_system_users_display?.join(', ') || ''
|
||||
},
|
||||
{
|
||||
key: this.$t('assets.Action'),
|
||||
value: forMatAction(this, this.object.meta['apply_actions_display'])
|
||||
value: forMatAction(this, meta['apply_actions_display'])
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateStart'),
|
||||
value: toSafeLocalDateStr(this.object.meta.apply_date_start)
|
||||
value: toSafeLocalDateStr(meta?.apply_date_start)
|
||||
},
|
||||
{
|
||||
key: this.$t('common.dateExpired'),
|
||||
value: toSafeLocalDateStr(this.object.meta.apply_date_expired)
|
||||
value: toSafeLocalDateStr(meta?.apply_date_expired)
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -16,17 +16,23 @@ export default {
|
||||
tableConfig: {
|
||||
url: '/api/v1/tickets/flows/',
|
||||
columns: [
|
||||
'type_display', 'created_by',
|
||||
'type_display', 'created_by', 'org_name',
|
||||
'date_created', 'date_updated', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['actions'],
|
||||
default: [
|
||||
'type_display', 'created_by',
|
||||
'type_display', 'created_by', 'org_name',
|
||||
'date_created', 'date_updated', 'actions'
|
||||
]
|
||||
},
|
||||
columnsMeta: {
|
||||
org_name: {
|
||||
formatter: function(row, col, cell) {
|
||||
var currentOrg = vm.$store.getters.currentOrg
|
||||
return currentOrg.is_root ? row.org_name : currentOrg.name
|
||||
}
|
||||
},
|
||||
type_display: {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
icon: 'fa-gear',
|
||||
name: 'TicketFlow',
|
||||
hidden: () => {
|
||||
return !(vm.$store.getters.currentUserIsSuperAdmin || vm.$store.getters.currentUserIsAdmin)
|
||||
return !vm.$store.getters.currentUserIsSuperAdmin
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -26,6 +26,11 @@ export default {
|
||||
ticketTableConfig: {
|
||||
url: this.url,
|
||||
columns: [
|
||||
{
|
||||
prop: 'serial_num',
|
||||
label: this.$t('common.Number'),
|
||||
sortable: 'custom'
|
||||
},
|
||||
{
|
||||
prop: 'title',
|
||||
label: this.$t('tickets.title'),
|
||||
@@ -119,6 +124,7 @@ export default {
|
||||
{
|
||||
value: 'state',
|
||||
label: this.$t('tickets.action'),
|
||||
type: 'choice',
|
||||
children: [
|
||||
{
|
||||
default: true,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
</el-col>
|
||||
<el-col :span="6" :offset="1">
|
||||
<Steps :object="object" />
|
||||
<Session :object="object" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
@@ -17,9 +18,11 @@
|
||||
import Details from './Details'
|
||||
import Comments from './Comments'
|
||||
import Steps from './Steps'
|
||||
import Session from './Session'
|
||||
|
||||
export default {
|
||||
name: 'GenericTicketDetail',
|
||||
components: { Steps, Comments, Details },
|
||||
components: { Steps, Comments, Details, Session },
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
|
||||
156
src/views/tickets/components/Session.vue
Normal file
156
src/views/tickets/components/Session.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<IBox v-if="session.id" v-loading="loading" class="box">
|
||||
<div slot="header" class="clearfix ibox-title">
|
||||
<i class="fa fa-rocket" />
|
||||
{{ $t('sessions.session') }}
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-row class="item">
|
||||
<el-col>
|
||||
<span class="item-label">{{ $t('sessions.SessionID') }}:</span>
|
||||
<span class="item-value">{{ session.id }}</span>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<span class="item-label">{{ $t('sessions.TargetResources') }}:</span>
|
||||
<span class="item-value">{{ session.asset }}</span>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<span class="item-label">{{ $t('tickets.SystemUser') }}:</span>
|
||||
<span class="item-value">{{ session.system_user }}</span>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<span class="item-label">{{ $t('sessions.UseProtocol') }}:</span>
|
||||
<span class="item-value">{{ session.protocol }}</span>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<span class="item-label">{{ $t('sessions.remoteAddr') }}:</span>
|
||||
<span class="item-value">{{ session.remote_addr }}</span>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<span class="item-label">{{ $t('sessions.SessionState') }}:</span>
|
||||
<span class="item-value cur-color" :style="{ 'background': session.is_finished ? '#ed5565' : '#1ab394' }" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-divider />
|
||||
<div class="bottom-btn">
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
:disabled="!session.can_terminate"
|
||||
@click="onConnect"
|
||||
>
|
||||
{{ $t('sessions.terminate') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!session.can_join"
|
||||
@click="onMonitor"
|
||||
>
|
||||
{{ $t('sessions.Monitor') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</IBox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IBox from '@/components/IBox'
|
||||
|
||||
export default {
|
||||
components: { IBox },
|
||||
props: {
|
||||
object: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
session: {},
|
||||
curTimer: null,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.object.state !== 'open') {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearTimeout(this.curTimer)
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.loading = true
|
||||
const url = `/api/v1/tickets/tickets/${this.object.id}/session/`
|
||||
this.$axios({
|
||||
url,
|
||||
method: 'get',
|
||||
disableFlashErrorMsg: true
|
||||
}).then(res => {
|
||||
this.session = res || {}
|
||||
}).catch(err => {
|
||||
this.curTimer = setTimeout(() => {
|
||||
this.init()
|
||||
}, 1400)
|
||||
this.$log.debug('error', err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
onConnect() {
|
||||
const url = '/api/v1/terminal/tasks/kill-session-for-ticket/'
|
||||
const data = [this.session.id] || []
|
||||
this.$axios.post(url, data).then(res => {
|
||||
this.$message.success(this.$t('sessions.TerminateTaskSendSuccessMsg'))
|
||||
this.curTimer = setTimeout(() => {
|
||||
this.init()
|
||||
}, 50000)
|
||||
}).catch(err => {
|
||||
this.$message.error(err)
|
||||
})
|
||||
},
|
||||
onMonitor() {
|
||||
const joinUrl = `/luna/monitor/${this.session.id}`
|
||||
window.open(joinUrl, 'height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.box {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
&>>> .el-divider--horizontal {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
line-height: 2.5;
|
||||
font-size: 13px;
|
||||
color: #676A6C;
|
||||
.item-label {
|
||||
font-weight: 700;
|
||||
}
|
||||
.item-value {
|
||||
color: #676A6C;
|
||||
}
|
||||
&>>> .el-col {
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
.bottom-btn {
|
||||
text-align: right;
|
||||
}
|
||||
.cur-color {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
vertical-align: text-top;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
12
yarn.lock
12
yarn.lock
@@ -10793,6 +10793,13 @@ vue-jest@^3.0.4:
|
||||
tsconfig "^7.0.0"
|
||||
vue-template-es2015-compiler "^1.6.0"
|
||||
|
||||
vue-json-editor@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-json-editor/-/vue-json-editor-1.4.3.tgz#004c9037ac91f16dd8fc558c9914e5f33a41d71c"
|
||||
integrity sha512-st9HdXBgCnyEmmfWrZQiKzp4KuYXzmYVUNDn5h6Fa18MrrGS1amnyUFyv7hQFsNBDW27B7BKkdGOqszYT1srAg==
|
||||
dependencies:
|
||||
vue "^2.2.6"
|
||||
|
||||
vue-loader@^15.7.0:
|
||||
version "15.9.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.1.tgz#bd2ab8f3d281e51d7b81d15390a58424d142243e"
|
||||
@@ -10850,6 +10857,11 @@ vue@2.6.11:
|
||||
resolved "https://registry.npm.taobao.org/vue/download/vue-2.6.11.tgz?cache=0&sync_timestamp=1587135947786&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue%2Fdownload%2Fvue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
|
||||
integrity sha1-dllNh31LEiNEBuhONSdcbVFBJcU=
|
||||
|
||||
vue@^2.2.6:
|
||||
version "2.6.14"
|
||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
|
||||
integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==
|
||||
|
||||
vuejs-logger@^1.5.4:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.npm.taobao.org/vuejs-logger/download/vuejs-logger-1.5.4.tgz#c8bb12ed29ca90b8087144a44ad852d9bd170c6e"
|
||||
|
||||
Reference in New Issue
Block a user