mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-14 03:46:26 +00:00
Compare commits
140 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7250a6be01 | ||
|
|
5667c39bcb | ||
|
|
982ce90a9a | ||
|
|
86ce758adb | ||
|
|
582a84178d | ||
|
|
73de782756 | ||
|
|
9b9f7c936c | ||
|
|
55100e64a1 | ||
|
|
6d74959b64 | ||
|
|
2a6100957f | ||
|
|
6a3f6f8914 | ||
|
|
704ae0b294 | ||
|
|
9bf4597d8d | ||
|
|
16606d6a27 | ||
|
|
bb1d19610e | ||
|
|
ca871b16c4 | ||
|
|
0a612f50e6 | ||
|
|
d566adb644 | ||
|
|
242f958428 | ||
|
|
f97d46814d | ||
|
|
5cc501f5af | ||
|
|
31fd0ecde0 | ||
|
|
9d6ac3907a | ||
|
|
784bd16e87 | ||
|
|
4a11ea2c57 | ||
|
|
4a2ffc754f | ||
|
|
d31929eb33 | ||
|
|
fe36fa9390 | ||
|
|
a97de8afb4 | ||
|
|
ba109900ec | ||
|
|
f8649457d6 | ||
|
|
8863693541 | ||
|
|
c2cbd3f5b0 | ||
|
|
0b84b96afb | ||
|
|
ec7768267f | ||
|
|
4aefb6779f | ||
|
|
06fd007c62 | ||
|
|
6f4e029537 | ||
|
|
cc58b374ab | ||
|
|
07f35dbbb2 | ||
|
|
04ffbb8fd6 | ||
|
|
12a501d559 | ||
|
|
7dcd4490ba | ||
|
|
dd1ac7c7f8 | ||
|
|
471910e0b8 | ||
|
|
a514e88b78 | ||
|
|
b79fefdee8 | ||
|
|
d2b3025709 | ||
|
|
386e2417e3 | ||
|
|
fd57b37cea | ||
|
|
a6222f87d2 | ||
|
|
5cd4e5b40b | ||
|
|
eb3d9089e0 | ||
|
|
56c22cffe6 | ||
|
|
fe1e26957a | ||
|
|
bc366947f0 | ||
|
|
86150cc571 | ||
|
|
f11fc947af | ||
|
|
49880f6739 | ||
|
|
82faf0f99e | ||
|
|
e6f98d58c4 | ||
|
|
3536a94976 | ||
|
|
45276010e0 | ||
|
|
2e47f42366 | ||
|
|
dd6e9a1512 | ||
|
|
fd1f16d43c | ||
|
|
24931a9f5a | ||
|
|
f51924bf1d | ||
|
|
f82257edb8 | ||
|
|
286e9894c0 | ||
|
|
968b2415b1 | ||
|
|
e0f6fb305d | ||
|
|
e0fd33f376 | ||
|
|
7ad86062b4 | ||
|
|
1fce7561db | ||
|
|
3b17235b6e | ||
|
|
52c9b9503b | ||
|
|
48a2b20320 | ||
|
|
e6cc8cd2e8 | ||
|
|
3a183ddf53 | ||
|
|
7eb77487d1 | ||
|
|
a5da581317 | ||
|
|
6bda9a372e | ||
|
|
a8de087137 | ||
|
|
34d9790a04 | ||
|
|
5bfe2497fd | ||
|
|
e16a775037 | ||
|
|
42fab92237 | ||
|
|
e2eac83615 | ||
|
|
04465a1da3 | ||
|
|
2adb1ee980 | ||
|
|
776090d6ba | ||
|
|
2c66bec7c9 | ||
|
|
b10f7faf25 | ||
|
|
450014ab16 | ||
|
|
8ad0d2ac58 | ||
|
|
bd41f96df3 | ||
|
|
3a37952288 | ||
|
|
5346eb1ef1 | ||
|
|
62b8fc0e3b | ||
|
|
f532d624e0 | ||
|
|
4ce6f49b30 | ||
|
|
2fb160b5f7 | ||
|
|
4ed9fb2acc | ||
|
|
722fc083cb | ||
|
|
4356b79ecc | ||
|
|
5eff817b05 | ||
|
|
4f08fef25a | ||
|
|
b2028869cb | ||
|
|
325f21f8b5 | ||
|
|
802d6f6cd3 | ||
|
|
f2120521d6 | ||
|
|
cbdeea9be7 | ||
|
|
8d2d38327a | ||
|
|
5c564c42dc | ||
|
|
0c47f8f962 | ||
|
|
65d625ad3c | ||
|
|
0e65b7f1be | ||
|
|
22cfaf8665 | ||
|
|
b3670fe528 | ||
|
|
2f80b66b07 | ||
|
|
5277a725f8 | ||
|
|
b400c13965 | ||
|
|
c71f0c17fc | ||
|
|
a952477c3b | ||
|
|
1153a3b38b | ||
|
|
2e69cd86aa | ||
|
|
a5ea6360bd | ||
|
|
0639b139d7 | ||
|
|
a8867dee83 | ||
|
|
c4d0462362 | ||
|
|
db97240ad3 | ||
|
|
5b6f06ac0e | ||
|
|
51de827c2b | ||
|
|
49566cdae7 | ||
|
|
b6786687b6 | ||
|
|
c83abe32c6 | ||
|
|
1b19e0201d | ||
|
|
7681250fff | ||
|
|
9fe4e0b613 |
2
.github/workflows/release-drafter.yml
vendored
2
.github/workflows/release-drafter.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build it and upload
|
||||
uses: jumpserver/action-build-upload-assets@node10
|
||||
uses: jumpserver/action-build-upload-assets@node14.16
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
25
Dockerfile
25
Dockerfile
@@ -1,23 +1,26 @@
|
||||
FROM node:10 as stage-build
|
||||
FROM node:14.16 as stage-build
|
||||
ARG TARGETARCH
|
||||
ARG NPM_REGISTRY="https://registry.npmmirror.com"
|
||||
ENV NPM_REGISTY=$NPM_REGISTRY
|
||||
ARG SASS_BINARY_SITE="https://npmmirror.com/mirrors/node-sass"
|
||||
ENV SASS_BINARY_SITE=$SASS_BINARY_SITE
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
RUN npm config set sass_binary_site=${SASS_BINARY_SITE}
|
||||
RUN npm config set registry ${NPM_REGISTRY}
|
||||
RUN yarn config set registry ${NPM_REGISTRY}
|
||||
COPY package.json yarn.lock /data/
|
||||
RUN yarn install
|
||||
RUN npm rebuild node-sass
|
||||
RUN set -ex \
|
||||
&& npm config set registry ${NPM_REGISTRY} \
|
||||
&& yarn config set registry ${NPM_REGISTRY} \
|
||||
&& yarn config set cache-folder /root/.cache/yarn/lina
|
||||
|
||||
ADD package.json yarn.lock /data
|
||||
RUN --mount=type=cache,target=/root/.cache/yarn \
|
||||
yarn install
|
||||
|
||||
ARG VERSION
|
||||
ENV VERSION=$VERSION
|
||||
ADD . /data
|
||||
RUN cd utils && bash -xieu build.sh build
|
||||
RUN --mount=type=cache,target=/root/.cache/yarn \
|
||||
sed -i "s@Version <strong>.*</strong>@Version <strong>${VERSION}</strong>@g" src/layout/components/Footer/index.vue \
|
||||
&& yarn build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=stage-build /data/release/lina /opt/lina
|
||||
COPY --from=stage-build /data/lina /opt/lina
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"build:prod": "vue-cli-service build",
|
||||
"build:stage": "vue-cli-service build --mode staging",
|
||||
"preview": "node build/index.js --preview",
|
||||
@@ -32,13 +33,13 @@
|
||||
"element-ui": "2.13.2",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"install": "^0.13.0",
|
||||
"jquery": "^3.5.0",
|
||||
"jquery": "^3.6.1",
|
||||
"js-cookie": "2.2.0",
|
||||
"jsencrypt": "^3.2.1",
|
||||
"krry-transfer": "^1.7.3",
|
||||
"less": "^3.10.3",
|
||||
"less-loader": "^5.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.frompairs": "^4.0.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
@@ -50,8 +51,8 @@
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash.topairs": "^4.3.0",
|
||||
"lodash.values": "^4.3.0",
|
||||
"moment": "^2.29.1",
|
||||
"moment-parseformat": "^3.0.0",
|
||||
"moment": "^2.29.4",
|
||||
"moment-parseformat": "^4.0.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"npm": "^7.8.0",
|
||||
"nprogress": "0.2.0",
|
||||
|
||||
@@ -247,6 +247,15 @@ td .el-button.el-button--mini {
|
||||
border-top-color: #676a6c;
|
||||
}
|
||||
|
||||
.text-link {
|
||||
color: info!important;
|
||||
}
|
||||
|
||||
.text-link:hover {
|
||||
color: info!important;
|
||||
filter: opacity(65%)!important;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: danger;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.3 KiB |
@@ -75,6 +75,8 @@ export default {
|
||||
showMenu: false,
|
||||
showRefresh: true,
|
||||
showAssets: false,
|
||||
showSearch: true,
|
||||
customTreeHeader: true,
|
||||
url: '/api/v1/assets/assets/?fields_size=mini',
|
||||
nodeUrl: '/api/v1/assets/nodes/',
|
||||
// ?assets=0不显示资产. =1显示资产
|
||||
|
||||
@@ -78,15 +78,22 @@ export default {
|
||||
},
|
||||
_cleanFormValue(form, remoteMeta) {
|
||||
for (const [k, v] of Object.entries(remoteMeta)) {
|
||||
if (v.default === undefined) {
|
||||
continue
|
||||
let valueSet = form[k]
|
||||
if (v.type === 'nested object' && v.children) {
|
||||
// 有一些字段属性时 nested object 类型,但是没有 children,没有children的不需要走递归逻辑,
|
||||
// 比如:认证配置中的属性映射字段
|
||||
if (typeof valueSet !== 'object') {
|
||||
// 处理一些前端没有设置初始值的情况
|
||||
valueSet = {}
|
||||
}
|
||||
form[k] = valueSet
|
||||
this._cleanFormValue(valueSet, v.children)
|
||||
}
|
||||
const valueSet = form[k]
|
||||
if (valueSet !== undefined) {
|
||||
continue
|
||||
}
|
||||
if (v.type === 'nested object' && typeof valueSet === 'object') {
|
||||
this._cleanFormValue(valueSet, v.children)
|
||||
if (v.default === undefined) {
|
||||
continue
|
||||
}
|
||||
form[k] = v.default
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<script>
|
||||
import DataZTree from '../DataZTree'
|
||||
import $ from '@/utils/jquery-vendor'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'AutoDataZTree',
|
||||
@@ -37,6 +38,10 @@ export default {
|
||||
showCreate: true,
|
||||
showDelete: true,
|
||||
showUpdate: true,
|
||||
showSearch: false,
|
||||
// 自定义header
|
||||
customTreeHeader: false,
|
||||
customTreeHeaderName: this.$t('assets.AssetTree'),
|
||||
async: {
|
||||
enable: true,
|
||||
url: (process.env.VUE_APP_ENV === 'production') ? (`${this.setting.treeUrl}`) : (`${process.env.VUE_APP_BASE_API}${this.setting.treeUrl}`),
|
||||
@@ -66,6 +71,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'currentOrg'
|
||||
]),
|
||||
treeSetting() {
|
||||
this.$log.debug('Settings: ', this.setting)
|
||||
return _.merge(this.defaultSetting, this.setting)
|
||||
@@ -172,7 +180,7 @@ export default {
|
||||
const rMenuID = this.$refs.dataztree.$refs.ztree.iRMenuID
|
||||
const zTreeID = this.$refs.dataztree.$refs.ztree.iZTreeID
|
||||
const offset = $(`#${zTreeID}`).offset()
|
||||
const scrollTop = document.querySelector('.treebox').scrollTop
|
||||
const scrollTop = document.querySelector('.treebox')?.scrollTop
|
||||
x -= offset.left
|
||||
// Tmp
|
||||
y -= (offset.top + scrollTop) / 3 - 10
|
||||
@@ -192,7 +200,7 @@ export default {
|
||||
return
|
||||
}
|
||||
// 屏蔽收藏资产
|
||||
if (treeNode.id === '-12') {
|
||||
if (treeNode?.id === '-12') {
|
||||
return
|
||||
}
|
||||
if (!treeNode && event.target.tagName.toLowerCase() !== 'button' && $(event.target).parents('a').length === 0) {
|
||||
|
||||
@@ -14,10 +14,23 @@ export const EmailCheck = {
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
|
||||
export const specialEmojiCheck = {
|
||||
validator: (rule, value, callback) => {
|
||||
value = value.trim()
|
||||
if (/[\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/.test(value)) {
|
||||
callback(new Error(i18n.t('common.NotSpecialEmoji')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
|
||||
export default {
|
||||
Required,
|
||||
RequiredChange,
|
||||
EmailCheck
|
||||
EmailCheck,
|
||||
specialEmojiCheck
|
||||
}
|
||||
|
||||
export const JsonRequired = {
|
||||
@@ -32,3 +45,20 @@ export const JsonRequired = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const JsonRequiredUserNameMapped = {
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
validator: (rule, value, callback) => {
|
||||
try {
|
||||
JSON.parse(value)
|
||||
const hasUserName = _.map(JSON.parse(value), (value) => value)
|
||||
if (!hasUserName.includes('username')) {
|
||||
callback(new Error(i18n.t('common.requiredHasUserNameMapped')))
|
||||
}
|
||||
callback()
|
||||
} catch (e) {
|
||||
callback(new Error(i18n.t('common.InvalidJson')))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul v-show="loading" class="ztree">
|
||||
{{ this.$t('common.tree.Loading') }}...
|
||||
</ul>
|
||||
<div v-show="!loading" class="treebox">
|
||||
<ul :id="iZTreeID" class="ztree">
|
||||
<div
|
||||
v-if="treeSetting.customTreeHeader"
|
||||
class="tree-header treebox"
|
||||
>
|
||||
<div class="content">
|
||||
<span class="title">
|
||||
{{ treeSetting.customTreeHeaderName }}
|
||||
</span>
|
||||
<span class="tree-banner-icon-zone">
|
||||
<a id="searchIcon" class="tree-search special">
|
||||
<i
|
||||
class="fa fa-search tree-banner-icon"
|
||||
@click.stop="treeSearch"
|
||||
/>
|
||||
<input
|
||||
id="searchInput"
|
||||
v-model="treeSearchValue"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
class="tree-input"
|
||||
>
|
||||
</a>
|
||||
<i
|
||||
class="fa fa-refresh tree-banner-icon"
|
||||
style="margin-right: 2px;"
|
||||
@click.stop="refresh"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<ul v-show="loading" class="ztree">
|
||||
{{ this.$t('common.tree.Loading') }}...
|
||||
</ul>
|
||||
<div v-if="treeSetting.treeUrl===''">
|
||||
{{ this.$t('common.tree.Empty') }}<a id="tree-refresh"><i class="fa fa-refresh" /></a>
|
||||
<ul v-show="!loading" :id="iZTreeID" class="ztree" />
|
||||
<div v-if="treeSetting.treeUrl===''" class="tree-empty">
|
||||
{{ this.$t('common.tree.Empty') }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="treebox">
|
||||
<ul v-show="loading" class="ztree">
|
||||
{{ this.$t('common.tree.Loading') }}...
|
||||
</ul>
|
||||
<ul v-show="!loading" :id="iZTreeID" class="ztree" />
|
||||
<div v-if="treeSetting.treeUrl===''" class="tree-empty">
|
||||
{{ this.$t('common.tree.Empty') }}
|
||||
<a id="tree-refresh"><i class="fa fa-refresh" /></a>
|
||||
</div>
|
||||
</div>
|
||||
<div :id="iRMenuID" class="rMenu">
|
||||
@@ -24,6 +60,7 @@
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import $ from '@/utils/jquery-vendor.js'
|
||||
import '@ztree/ztree_v3/js/jquery.ztree.all.min.js'
|
||||
import '@ztree/ztree_v3/js/jquery.ztree.exhide.min.js'
|
||||
import '@/styles/ztree.css'
|
||||
import axiosRetry from 'axios-retry'
|
||||
|
||||
@@ -45,7 +82,8 @@ export default {
|
||||
zTree: '',
|
||||
rMenu: '',
|
||||
init: false,
|
||||
loading: false
|
||||
loading: false,
|
||||
treeSearchValue: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -54,8 +92,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.refresh = this.refresh
|
||||
window.treeSearch = this.treeSearch
|
||||
this.initTree()
|
||||
// $('.treebox').css('height', window.innerHeight - 60)
|
||||
},
|
||||
beforeDestroy() {
|
||||
$.fn.zTree.destroy(this.iZTreeID)
|
||||
@@ -82,9 +121,7 @@ export default {
|
||||
retryDelay: () => { return 5000 }
|
||||
}
|
||||
}).then(res => {
|
||||
if (!res) {
|
||||
res = []
|
||||
}
|
||||
if (!res) res = []
|
||||
if (res.length === 0) {
|
||||
res.push({
|
||||
name: this.$t('common.tree.Empty')
|
||||
@@ -94,15 +131,13 @@ export default {
|
||||
if (this.init) {
|
||||
vm.zTree.destroy()
|
||||
}
|
||||
|
||||
this.zTree = $.fn.zTree.init($(`#${this.iZTreeID}`), this.treeSetting, res)
|
||||
if (!this.treeSetting.customTreeHeader) {
|
||||
this.rootNodeAddDom(this.zTree)
|
||||
}
|
||||
// 手动上报事件, Tree加载完成
|
||||
this.$emit('TreeInitFinish', this.zTree)
|
||||
if (this.treeSetting.showRefresh) {
|
||||
this.rootNodeAddDom(
|
||||
this.zTree,
|
||||
this.treeSetting.callback.refresh
|
||||
)
|
||||
}
|
||||
|
||||
if (this.treeSetting.showMenu) {
|
||||
this.rMenu = $(`#${this.iRMenuID}`)
|
||||
@@ -115,43 +150,196 @@ export default {
|
||||
vm.init = true
|
||||
})
|
||||
},
|
||||
rootNodeAddDom: function(ztree, callback) {
|
||||
const vm = this
|
||||
const refreshIcon = "<a id='tree-refresh'><i class='fa fa-refresh'></i></a>"
|
||||
rootNodeAddDom(ztree) {
|
||||
const { showSearch, showRefresh } = this.treeSetting
|
||||
const searchIcon = `<a class="tree-search" id="searchIcon">
|
||||
<i class='fa fa-search tree-banner-icon' onclick="treeSearch()" /></i>
|
||||
<input type="text" autocomplete="off" id="searchInput" class="tree-input" />
|
||||
</a>`
|
||||
const refreshIcon = "<a id='tree-refresh' onclick='refresh()'><i class='fa fa-refresh'></i></a>"
|
||||
const treeActions = `${showSearch ? searchIcon : ''}${showRefresh ? refreshIcon : ''}`
|
||||
const icons = `<span class="">${treeActions}</span>`
|
||||
const rootNode = ztree.getNodes()[0]
|
||||
let $rootNodeRef
|
||||
if (rootNode) {
|
||||
$rootNodeRef = $('#' + rootNode.tId + '_a')
|
||||
$rootNodeRef.after(refreshIcon)
|
||||
} else {
|
||||
$rootNodeRef = $('#' + ztree.setting.treeId)
|
||||
$rootNodeRef.html(refreshIcon)
|
||||
const $rootNodeRef = $('#' + rootNode.tId + '_a')
|
||||
$rootNodeRef.after(icons)
|
||||
}
|
||||
const refreshIconRef = $('#tree-refresh')
|
||||
refreshIconRef.bind('click', function() {
|
||||
const result = callback()
|
||||
if (result && result.then) {
|
||||
result.finally(() => {
|
||||
vm.initTree()
|
||||
})
|
||||
} else {
|
||||
vm.initTree()
|
||||
}
|
||||
})
|
||||
},
|
||||
refresh: function() {
|
||||
const refreshIconRef = $('#tree-refresh')
|
||||
refreshIconRef.click()
|
||||
refresh() {
|
||||
this.treeSearchValue = ''
|
||||
const result = this.treeSetting?.callback?.refresh()
|
||||
if (result && result.then) {
|
||||
result.finally(() => {
|
||||
this.initTree()
|
||||
})
|
||||
} else {
|
||||
this.initTree()
|
||||
}
|
||||
},
|
||||
treeSearch() {
|
||||
const searchIcon = document.getElementById(`searchIcon`)
|
||||
const searchInput = document.getElementById(`searchInput`)
|
||||
searchIcon.classList.toggle('active')
|
||||
searchInput.focus()
|
||||
searchInput.onclick = (e) => {
|
||||
e.stopPropagation()
|
||||
}
|
||||
searchInput.onblur = (e) => {
|
||||
e.stopPropagation()
|
||||
if (!(e.target.value)) {
|
||||
searchIcon.classList.toggle('active')
|
||||
}
|
||||
}
|
||||
searchInput.oninput = _.debounce((e) => {
|
||||
e.stopPropagation()
|
||||
const value = e.target.value || ''
|
||||
if (this.treeSetting.async.enable) {
|
||||
this.filterAssetsServer(value)
|
||||
} else {
|
||||
this.filterTree(value)
|
||||
}
|
||||
}, 600)
|
||||
},
|
||||
getCheckedNodes: function() {
|
||||
return this.zTree.getCheckedNodes(true)
|
||||
},
|
||||
recurseParent(node) {
|
||||
const parentNode = node.getParentNode()
|
||||
if (parentNode && parentNode.pId) {
|
||||
return [parentNode, ...this.recurseParent(parentNode)]
|
||||
} else if (parentNode) {
|
||||
return [parentNode]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
},
|
||||
recurseChildren(node) {
|
||||
if (!node.isParent) {
|
||||
return []
|
||||
}
|
||||
const children = node.children
|
||||
if (!children) {
|
||||
return []
|
||||
}
|
||||
let allChildren = []
|
||||
children.forEach((n) => {
|
||||
allChildren = [...children, ...this.recurseChildren(n)]
|
||||
})
|
||||
return allChildren
|
||||
},
|
||||
groupBy(array, filter) {
|
||||
const groups = {}
|
||||
array.forEach(function(o) {
|
||||
const group = JSON.stringify(filter(o))
|
||||
groups[group] = groups[group] || []
|
||||
groups[group].push(o)
|
||||
})
|
||||
return Object.keys(groups).map(function(group) {
|
||||
return groups[group]
|
||||
})
|
||||
},
|
||||
filterTree(keyword, tree = this.zTree) {
|
||||
if (!this.zTree) return
|
||||
const searchNode = tree.getNodesByFilter((node) => node.id === 'search')
|
||||
if (searchNode) tree.removeNode(searchNode[0])
|
||||
const nodes = tree.transformToArray(tree.getNodes())
|
||||
if (!keyword) {
|
||||
tree.showNodes(nodes)
|
||||
return
|
||||
}
|
||||
|
||||
if (!keyword) {
|
||||
if (tree.hiddenNodes) {
|
||||
tree.showNodes(tree.hiddenNodes)
|
||||
tree.hiddenNodes = null
|
||||
}
|
||||
if (tree.expandNodes) {
|
||||
tree.expandNodes.forEach((node) => {
|
||||
if (node.id !== nodes[0].id) {
|
||||
tree.expandNode(node, false)
|
||||
}
|
||||
})
|
||||
tree.expandNodes = null
|
||||
}
|
||||
return null
|
||||
}
|
||||
let shouldShow = []
|
||||
const matchedNodes = tree.getNodesByFilter((node) => {
|
||||
return node.name.toLowerCase().indexOf(keyword.toLowerCase()) > -1
|
||||
})
|
||||
|
||||
if (matchedNodes.length < 1) {
|
||||
let name = this.$t('common.Search')
|
||||
const assetsAmount = matchedNodes.length
|
||||
name = `${name} (${assetsAmount})`
|
||||
const newNode = { id: 'search', name: name, isParent: false, open: false }
|
||||
tree.addNodes(null, newNode)
|
||||
}
|
||||
|
||||
matchedNodes.forEach((node) => {
|
||||
const parents = this.recurseParent(node)
|
||||
const children = this.recurseChildren(node)
|
||||
shouldShow = [...shouldShow, ...parents, ...children, node]
|
||||
})
|
||||
|
||||
tree.hiddenNodes = nodes
|
||||
tree.expandNodes = shouldShow
|
||||
tree.hideNodes(nodes)
|
||||
tree.showNodes(shouldShow)
|
||||
for (const node of shouldShow) {
|
||||
if (node.isParent) {
|
||||
tree.expandNode(node, true)
|
||||
}
|
||||
}
|
||||
},
|
||||
filterAssetsServer(keyword) {
|
||||
if (!this.zTree) return
|
||||
let searchNode = this.zTree.getNodesByFilter((node) => node.id === 'search')
|
||||
if (searchNode) {
|
||||
this.zTree.removeChildNodes(searchNode[0])
|
||||
this.zTree.removeNode(searchNode[0])
|
||||
}
|
||||
const treeNodes = this.zTree.getNodes()
|
||||
if (!keyword) {
|
||||
if (treeNodes.length !== 0) {
|
||||
this.zTree.showNodes(treeNodes)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (treeNodes.length !== 0) {
|
||||
this.zTree.hideNodes(treeNodes)
|
||||
}
|
||||
|
||||
let treeUrl = this.treeSetting.treeUrl
|
||||
const filterField = treeUrl.includes('?') ? `&search=${keyword}` : `?search=${keyword}`
|
||||
if (treeUrl.indexOf('assets/nodes/children/tree') > -1) {
|
||||
treeUrl = treeUrl + '&all=all'
|
||||
}
|
||||
const searchUrl = `${treeUrl}${filterField}`
|
||||
this.$axios.get(searchUrl).then(nodes => {
|
||||
let name = this.$t('common.Search')
|
||||
const assetsAmount = nodes.length
|
||||
name = `${name} (${assetsAmount})`
|
||||
const newNode = { id: 'search', name: name, isParent: true, open: true, zAsync: true }
|
||||
searchNode = this.zTree.addNodes(null, newNode)[0]
|
||||
searchNode.zAsync = true
|
||||
const nodesGroupByOrg = this.groupBy(nodes, (node) => {
|
||||
return node.meta.data.org_name
|
||||
})
|
||||
|
||||
for (const item of nodesGroupByOrg) {
|
||||
this.zTree.addNodes(searchNode, item)
|
||||
}
|
||||
searchNode.open = true
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
<style lang='scss' scoped>
|
||||
div.rMenu {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
@@ -191,7 +379,7 @@ export default {
|
||||
top: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
.ztree ::v-deep .fa-refresh {
|
||||
.ztree ::v-deep .fa {
|
||||
font: normal normal normal 14px/1 FontAwesome !important;
|
||||
}
|
||||
.dropdown a:hover {
|
||||
@@ -218,4 +406,106 @@ export default {
|
||||
height: 80vh;
|
||||
overflow: auto;
|
||||
}
|
||||
::v-deep #tree-refresh {
|
||||
margin-left: 3px;
|
||||
}
|
||||
::v-deep .tree-banner-icon-zone {
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
height: 30px;
|
||||
overflow: hidden;
|
||||
.fa {
|
||||
color: #838385!important;;
|
||||
&:hover {
|
||||
color: #606266!important;;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .tree-search {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
border-radius: 12px;
|
||||
vertical-align: sub;
|
||||
transition: .25s;
|
||||
overflow: hidden;
|
||||
.fa {
|
||||
width: 13px!important;
|
||||
}
|
||||
.fa-search {
|
||||
padding-top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .tree-search .tree-banner-icon {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 6px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 12px;
|
||||
padding: 10px 6px;
|
||||
overflow: hidden;
|
||||
background-color: transparent!important;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::v-deep .tree-search.active {
|
||||
width: 160px;
|
||||
background-color: #ffffff!important;
|
||||
}
|
||||
|
||||
::v-deep .tree-search.active:hover {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
::v-deep .tree-search input {
|
||||
position: relative;
|
||||
left: 20px;
|
||||
width: 133px;
|
||||
height: 100%;
|
||||
background-color: #ffffff!important;
|
||||
color: #606266;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
.tree-header {
|
||||
position: relative;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
}
|
||||
.content {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
border-radius: 3px;
|
||||
padding: 0 5px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
background-color: #D7D8DC;
|
||||
.rotate {
|
||||
transition: all .1.8s;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
.fa-caret-down {
|
||||
font-size: 16px;
|
||||
}
|
||||
.special {
|
||||
top: 1px!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tree-empty {
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
const reader = new FileReader()
|
||||
reader.onload = function() {
|
||||
let result = this.result
|
||||
if (vm.toFormat === 'object') {
|
||||
if (vm.toFormat === 'object' && vm.fileName.endsWith('.json')) {
|
||||
result = JSON.parse(result)
|
||||
}
|
||||
vm.$emit('input', result)
|
||||
|
||||
61
src/components/TableFormatters/TwoTabFormatter.vue
Normal file
61
src/components/TableFormatters/TwoTabFormatter.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<el-row :gutter="10">
|
||||
<div v-if="isAllEmpty()" style="text-align: center">
|
||||
{{ this.$t('common.NoContent') }}
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-col :span="rightEmpty() ? 24 : 12">
|
||||
<div v-if="!leftEmpty()">
|
||||
<el-tag type="primary" effect="dark" :closable="false" style="width: 100%;">{{ row.leftTitle }}</el-tag>
|
||||
<div v-for="(value, key, index) in row.left" :key="index">
|
||||
<el-tag type="primary"><strong>{{ key }}: </strong>{{ value }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="leftEmpty() ? 24 : 12">
|
||||
<div v-if="!rightEmpty()">
|
||||
<el-tag type="primary" effect="dark" :closable="false" style="width: 100%;">{{ row.rightTitle }}</el-tag>
|
||||
<div v-for="(value, key, index) in row.right" :key="index">
|
||||
<el-tag type="primary"><strong>{{ key }}: </strong>{{ value }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</div>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'TwoTabFormatter',
|
||||
props: {
|
||||
row: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isEmpty(content) {
|
||||
return !content || JSON.stringify(content) === '{}'
|
||||
},
|
||||
leftEmpty() {
|
||||
return this.isEmpty(this.row.left)
|
||||
},
|
||||
rightEmpty() {
|
||||
return this.isEmpty(this.row.right)
|
||||
},
|
||||
isAllEmpty() {
|
||||
return this.leftEmpty() && this.rightEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tag{
|
||||
width: 100%;
|
||||
white-space: normal;
|
||||
height:auto;
|
||||
}
|
||||
</style>
|
||||
@@ -12,6 +12,7 @@ import DialogDetailFormatter from './DialogDetailFormatter'
|
||||
import EditableInputFormatter from './EditableInputFormatter'
|
||||
import StatusFormatter from './StatusFormatter'
|
||||
import TagsFormatter from './TagsFormatter'
|
||||
import TwoTabFormatter from './TwoTabFormatter'
|
||||
|
||||
export default {
|
||||
DetailFormatter,
|
||||
@@ -27,7 +28,8 @@ export default {
|
||||
ArrayFormatter,
|
||||
EditableInputFormatter,
|
||||
StatusFormatter,
|
||||
TagsFormatter
|
||||
TagsFormatter,
|
||||
TwoTabFormatter
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -44,5 +46,6 @@ export {
|
||||
ArrayFormatter,
|
||||
EditableInputFormatter,
|
||||
StatusFormatter,
|
||||
TagsFormatter
|
||||
TagsFormatter,
|
||||
TwoTabFormatter
|
||||
}
|
||||
|
||||
@@ -119,6 +119,7 @@ export default {
|
||||
border-radius: 3px;
|
||||
line-height: 1.428;
|
||||
cursor:pointer;
|
||||
height: 30px;
|
||||
}
|
||||
.el-tree{
|
||||
background-color: inherit !important;
|
||||
@@ -134,5 +135,8 @@ export default {
|
||||
}
|
||||
.transition-box.left {
|
||||
background: #f3f3f3;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 3px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -55,19 +55,19 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24" style="margin: 0 auto;">
|
||||
<el-col :md="24 - smsWidth" :sm="24">
|
||||
<el-input v-model="SecretKey" :show-password="showPassword" :placeholder="HelpText" style="margin-bottom: 20px;" />
|
||||
</el-col>
|
||||
<el-col v-if="Select === 'sms'" :md="smsWidth" :sm="24">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
style="line-height:20px; float: right;"
|
||||
:disabled="smsBtndisabled"
|
||||
@click="sendChallengeCode"
|
||||
>
|
||||
{{ smsBtnText }}
|
||||
</el-button>
|
||||
<el-col :md="24" :sm="24" style="display: flex; margin-bottom: 20px;">
|
||||
<el-input v-model="SecretKey" :show-password="showPassword" :placeholder="HelpText" />
|
||||
<span v-if="Select === 'sms'" style="margin: -1px 0 0 20px;">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
style="line-height:20px; float: right;"
|
||||
:disabled="smsBtndisabled"
|
||||
@click="sendChallengeCode"
|
||||
>
|
||||
{{ smsBtnText }}
|
||||
</el-button>
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24" style="margin: 0 auto;">
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"sqlserver": "SQLServer",
|
||||
"redis": "Redis",
|
||||
"mongodb": "MongoDB",
|
||||
"clickhouse": "ClickHouse",
|
||||
"k8s": "kubernetes"
|
||||
},
|
||||
"applicationsCategory": {
|
||||
@@ -98,6 +99,7 @@
|
||||
"Action": "Action",
|
||||
"ActiveSelected": "Active selected",
|
||||
"AdminUser": "Admin user",
|
||||
"AssetTree": "Asset tree",
|
||||
"ReplaceNodeAssetsAdminUser":"Replace node assets admin user with this",
|
||||
"AdminUserDetail": "Admin user detail",
|
||||
"DynamicUsername": "Dynamic username",
|
||||
@@ -243,7 +245,9 @@
|
||||
"View": "View",
|
||||
"LoginIP": "Login IP",
|
||||
"LoginCity": "Login city",
|
||||
"LoginDate": "Login date"
|
||||
"LoginDate": "Login date",
|
||||
"BeforeChange": "Before change",
|
||||
"AfterChange": "After change"
|
||||
},
|
||||
"auth": {
|
||||
"LoginRequiredMsg": "You account has logout, Please login again",
|
||||
@@ -253,6 +257,7 @@
|
||||
"ReLoginErr": "Login time has exceeded 5 minutes, please login again"
|
||||
},
|
||||
"common": {
|
||||
"NoContent": "No content",
|
||||
"NeedAddAppsOrSystemUserErrMsg": "Please add apps or system user",
|
||||
"VerificationCodeSent": "The verification code has been sent",
|
||||
"SendVerificationCode": "Send verification code",
|
||||
@@ -266,6 +271,12 @@
|
||||
"IPLoginLimit": "IP login limit",
|
||||
"Setting": "Setting",
|
||||
"Certificate": "Certificate",
|
||||
"CACertificate": "CA Certificate",
|
||||
"ClientCertificate": "Client certificate",
|
||||
"CertificateKey": "Certificate key file",
|
||||
"AllowInvalidCert": "Allow invalid cert",
|
||||
"UseSSL": "Use SSL/TLS",
|
||||
"SecretKey": "Secret key",
|
||||
"Scope": "Type",
|
||||
"Builtin": "Builtin",
|
||||
"DateCreated": "Date created",
|
||||
@@ -407,6 +418,7 @@
|
||||
"disableSelected": "Disable selected",
|
||||
"disableSuccessMsg": "Disable success",
|
||||
"fieldRequiredError": "This field is required",
|
||||
"requiredHasUserNameMapped": "The mapping of the username field must be included, such as {'uid': 'username'}",
|
||||
"getErrorMsg": "Get failed",
|
||||
"fileType": "File type",
|
||||
"Status": "Status",
|
||||
@@ -423,6 +435,7 @@
|
||||
"downloadImportTemplateMsg": "Download import template",
|
||||
"downloadUpdateTemplateMsg": "Download update template",
|
||||
"onlyCSVFilesTips": "Only csv supported",
|
||||
"ImportFail": "Import fail",
|
||||
"updateSuccessMsg": "Update success, total: {count}",
|
||||
"dragUploadFileInfo": "Drag file here or click here to upload",
|
||||
"uploadCsvLth10MHelpText": "csv/xlsx files with a size less than 10M",
|
||||
@@ -515,6 +528,7 @@
|
||||
},
|
||||
"Cycle": "Cycle",
|
||||
"FormatError": "Format error",
|
||||
"NotSpecialEmoji": "Special emoticons are not allowed",
|
||||
"WeekCronSelect": {
|
||||
"Monday": "Monday",
|
||||
"Tuesday": "Tuesday",
|
||||
@@ -669,6 +683,7 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"AssignedTicketList": "Assigned tickets",
|
||||
"CreateEndpoint": "Create endpoint",
|
||||
"UpdateEndpoint": "Update endpoint",
|
||||
"CreateEndpointRule": "Create endpoint rule",
|
||||
@@ -768,7 +783,7 @@
|
||||
"OperateLog": "Operation Logs",
|
||||
"PasswordChangeLog": "Password Update Logs",
|
||||
"Perms": "Permissions",
|
||||
"PersonalInformationImprovement": "PersonalInformationImprovement",
|
||||
"PersonalInformationImprovement": "Personal information improvement",
|
||||
"PlatformCreate": "Platform create",
|
||||
"PlatformDetail": "Platform detail",
|
||||
"PlatformList": "Platforms",
|
||||
@@ -912,15 +927,20 @@
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"OAuth2LogoTip": "Tip: Authentication Service Provider (recommended image size: 64px*64px)",
|
||||
"EndpointListHelpMessage": "The service endpoint is the address (port) for the user to access the service. When the user connects to the asset, the service endpoint will be selected according to the endpoint rules and asset tags, and the connection will be established as the access entry to realize the distributed connection of assets.",
|
||||
"EndpointRuleListHelpMessage": "For the service endpoint selection strategy, two types are currently supported: <br>1. Specify the endpoint according to the endpoint rule (current page); <br>2. Select the endpoint through the asset tag. The tag name is fixed to endpoint, and the value is the name of the `endpoint`. <br>Two methods preferentially use label matching, because the IP segment may conflict, and the label method exists as a supplement to the rules.",
|
||||
"EnableKoKoSSHHelpText": "Enabled, connect assets to display SSH Client pull-up method",
|
||||
"SettingInEndpointHelpText": "Configure the service address and port in System Settings / Terminal Settings / Service Endpoints",
|
||||
"Feature": "Feature",
|
||||
"SMSProvider": "SMS provider",
|
||||
"SMSProvider": "SMS provider / Protocol",
|
||||
"SMS": "SMS",
|
||||
"AlibabaCloud": "Alibaba cloud",
|
||||
"TencentCloud": "Tencent cloud",
|
||||
"HuaweiCloud": "Huawei cloud",
|
||||
"SignChannelNum": "Signature Channel Number",
|
||||
"AppEndpoint": "App access address",
|
||||
"CMPP2": "CMPP v2.0",
|
||||
"VerifySignTmpl": "Verification code template",
|
||||
"Radius": "Radius",
|
||||
"Enable": "Enable",
|
||||
@@ -974,9 +994,12 @@
|
||||
"authLdapServerUri": "LDAP server",
|
||||
"authLdapUserAttrMap": "User attr map",
|
||||
"authUserAttrMap": "User attr map",
|
||||
"authUserAttrMapHelpText": "Mapping relationship { idp_key: sp_key}",
|
||||
"authUserAttrMapHelpText": "The key on the left is the JumpServer user property, and the value on the right is the authenticated platform user property",
|
||||
"SAML2": "SAML2",
|
||||
"OAuth2": "OAuth2",
|
||||
"enableSAML2Auth": "Enable SAML2 Auth",
|
||||
"enableOAuth2Auth": "Enable OAuth2 Auth",
|
||||
"tokenHTTPMethod": "Token Obtaining Method",
|
||||
"SAML2Auth": "SAML2 Auth",
|
||||
"authSAML2Xml": "IDP metadata XML",
|
||||
"authSAML2MetadataUrl": "IDP metadata URL",
|
||||
@@ -1096,7 +1119,13 @@
|
||||
"weComTest": "Test",
|
||||
"FeiShu": "FeiShu",
|
||||
"feiShuTest": "Test",
|
||||
"setting": "Setting"
|
||||
"setting": "Setting",
|
||||
"SystemTools": "System Tools",
|
||||
"basicTools": "Basic Tools",
|
||||
"destinationIP": "Destination IP",
|
||||
"testPort": "Test Port",
|
||||
"testTools": "Test",
|
||||
"testHelpText": "Please enter the destination address for testing"
|
||||
},
|
||||
"tickets": {
|
||||
"PermissionName": "Permission name",
|
||||
@@ -1125,6 +1154,10 @@
|
||||
"reply": "Reply",
|
||||
"status": "Status",
|
||||
"title": "Title",
|
||||
"RelevantApp": "App",
|
||||
"RelevantAsset": "Asset",
|
||||
"RelevantCommand": "Command",
|
||||
"RelevantSystemUser": "System user",
|
||||
"type": "Type",
|
||||
"user": "User",
|
||||
"Status": "Status",
|
||||
@@ -1393,16 +1426,20 @@
|
||||
"IPNetworkSegment": "Ip Network Segment",
|
||||
"Aliyun": "Ali Cloud",
|
||||
"Qcloud": "Tencent Cloud",
|
||||
"QcloudLighthouse": "Tencent Cloud(Lighthouse)",
|
||||
"QingyunPrivatecloud": "Qingyun Private Cloud",
|
||||
"HuaweiPrivatecloud": "Huawei Private Cloud",
|
||||
"OpenStack": "OpenStack",
|
||||
"CTYunPrivate": "CTYun Private Cloud",
|
||||
"GCP": "Google Cloud Platform",
|
||||
"FC": "Fusion Compute",
|
||||
"LAN": "LAN",
|
||||
"AWS_China": "AWS(China)",
|
||||
"AWS_Int": "AWS(International)",
|
||||
"HuaweiCloud": "Huawei Cloud",
|
||||
"BaiduCloud": "Baidu Cloud",
|
||||
"JDCloud": "JD Cloud",
|
||||
"KingSoftCloud": "KingSoft Cloud",
|
||||
"Azure":"Azure(China)",
|
||||
"Azure_Int": "Azure(International)",
|
||||
"HostnameStrategy": "Used to produce the asset hostname. For example, 1. Instance name (instanceDemo);2. Instance name and Partial IP (instanceDemo-250.1)",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"sqlserver": "SQLServer",
|
||||
"redis": "Redis",
|
||||
"mongodb": "MongoDB",
|
||||
"clickhouse": "ClickHouse",
|
||||
"k8s": "Kubernetes"
|
||||
},
|
||||
"applicationsCategory": {
|
||||
@@ -109,6 +110,7 @@
|
||||
"HardwareInfo": "ハードウェア情報",
|
||||
"AssetDetail": "アセットの詳細",
|
||||
"AssetList": "アセットリスト",
|
||||
"AssetTree": "アセットツリー",
|
||||
"ReplaceNodeAssetsAdminUser": "ノード資産を置換する管理者",
|
||||
"AssetListHelpMessage": "左側は資産ツリーで、右クリックはツリーノードを新規作成、削除、変更することができ、授権資産もノード方式で組織され、右側はそのノードの下に属する資産である\n",
|
||||
"TestGatewayTestConnection": "テスト接続ゲートウェイ",
|
||||
@@ -248,7 +250,9 @@
|
||||
"SystemUserName": "システムユーザー名",
|
||||
"LoginIP": "ログインIP",
|
||||
"LoginCity": "ログイン都市",
|
||||
"LoginDate": "ログイン日"
|
||||
"LoginDate": "ログイン日",
|
||||
"BeforeChange": "変更前",
|
||||
"AfterChange": "変更後"
|
||||
},
|
||||
"auth": {
|
||||
"LoginRequiredMsg": "アカウントが終了しました。ログインし直してください",
|
||||
@@ -258,6 +262,7 @@
|
||||
"ReLoginErr": "ログイン時間が 5 分を超えました。もう一度ログインしてください"
|
||||
},
|
||||
"common": {
|
||||
"NoContent": "まだ内容がない",
|
||||
"NeedAddAppsOrSystemUserErrMsg": "アプリケーションまたはシステムユーザーを追加してください",
|
||||
"VerificationCodeSent": "検証コードが送信されました",
|
||||
"SendVerificationCode": "認証コードの送信",
|
||||
@@ -271,6 +276,12 @@
|
||||
"IPLoginLimit": "IPログイン制限",
|
||||
"Setting": "設定",
|
||||
"Certificate": "証明書",
|
||||
"CACertificate": "CA 証明書",
|
||||
"ClientCertificate": "クライアント証明書",
|
||||
"CertificateKey": "証明書秘密鍵ファイル",
|
||||
"AllowInvalidCert": "証明書チェックを無視する",
|
||||
"UseSSL": "使う SSL/TLS",
|
||||
"SecretKey": "鍵",
|
||||
"Scope": "カテゴリ",
|
||||
"Builtin": "内蔵",
|
||||
"DateCreated": "作成日",
|
||||
@@ -416,6 +427,7 @@
|
||||
"disableSelected": "選択した無効",
|
||||
"disableSuccessMsg": "成功を無効にする",
|
||||
"fieldRequiredError": "このフィールドは必須項目です",
|
||||
"requiredHasUserNameMapped": "usernameフィールドのマッピングを含める必要があります, {'uid':'username'}など",
|
||||
"getErrorMsg": "の取得に失敗しました",
|
||||
"MFAErrorMsg": "MFAエラーです。チェックしてください",
|
||||
"Total": "合計",
|
||||
@@ -434,6 +446,7 @@
|
||||
"downloadImportTemplateMsg": "作成テンプレートのダウンロード",
|
||||
"downloadUpdateTemplateMsg": "更新テンプレートのダウンロード",
|
||||
"onlyCSVFilesTips": "Csvファイルのインポートのみサポート",
|
||||
"ImportFail": "インポートに失敗しました",
|
||||
"updateSuccessMsg": "更新のインポートに成功しました。合計:{count}",
|
||||
"uploadCsvLth10MHelpText": "Csv/xlsxのみアップロードでき、10m以下です",
|
||||
"dragUploadFileInfo": "ここにファイルをドラッグするか、ここをクリックしてアップロードしてください",
|
||||
@@ -491,6 +504,7 @@
|
||||
"InvalidJson": "JSONの合法的ではありません",
|
||||
"time_period": "時間帯",
|
||||
"FormatError": "フォーマットエラー",
|
||||
"NotSpecialEmoji": "特殊な表情記号の入力は許可されていません",
|
||||
"WeekCronSelect": {
|
||||
"Monday": "月曜日",
|
||||
"Tuesday": "火曜日",
|
||||
@@ -679,6 +693,7 @@
|
||||
},
|
||||
"route": {
|
||||
"": "",
|
||||
"AssignedTicketList": "割り当て済みワークオーダー",
|
||||
"CreateEndpoint": "エンドポイントを作成する",
|
||||
"UpdateEndpoint": "エンドポイントを更新",
|
||||
"CreateEndpointRule": "エンドポイントルールを作成する",
|
||||
@@ -933,6 +948,7 @@
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"OAuth2LogoTip": "ヒント: 認証サービス プロバイダー (推奨画像サイズ: 64px*64px)",
|
||||
"EndpointListHelpMessage": "サービスエンドポイントは、ユーザーがサービスにアクセスするためのアドレス(ポート)です。ユーザーがアセットに接続すると、エンドポイントルールとアセットタグに従ってサービスエンドポイントが選択され、接続がアクセスエントリとして確立されます。資産の分散接続を実現します。",
|
||||
"EndpointRuleListHelpMessage": "サービスエンドポイント選択戦略では、現在2つのタイプがサポートされています:<br> 1. エンドポイントルールに従ってエンドポイントを指定します(現在のページ);<br>2. アセットタグを介してエンドポイントを選択します。タグ名はに固定されていますエンドポイントであり、値はエンドポイントの名前です。 <br> IPセグメントが競合する可能性があるため、2つの方法が優先的にラベル照合を使用し、ラベル方法はルールの補足として存在します。",
|
||||
"EnableKoKoSSHHelpText": "有効にすると、アセットを接続してSSHクライアントのプルアップ方式を表示します",
|
||||
@@ -940,6 +956,10 @@
|
||||
"Feature": "機能",
|
||||
"AlibabaCloud": "Alibaba cloud",
|
||||
"TencentCloud": "テンセント雲",
|
||||
"HuaweiCloud": "ファーウェイ雲",
|
||||
"SignChannelNum": "サインパス番号",
|
||||
"AppEndpoint": "アクセスアドレスを適用する",
|
||||
"CMPP2": "CMPP v2.0",
|
||||
"Radius": "Radius",
|
||||
"VerifySignTmpl": "認証コードメールテンプレート",
|
||||
"Enable": "有効化",
|
||||
@@ -997,9 +1017,11 @@
|
||||
"authLdapServerUri": "LDAPアドレス",
|
||||
"authLdapUserAttrMap": "ユーザー属性マッピング",
|
||||
"authUserAttrMap": "ユーザー属性マッピング",
|
||||
"authUserAttrMapHelpText": "マッピング関係 {idp:sp}",
|
||||
"SAML2": "SAML2",
|
||||
"authUserAttrMapHelpText": "左側のキーがJumpServerユーザ属性、右側の値が認証プラットフォームユーザ属性",
|
||||
"OAuth2": "OAuth2",
|
||||
"enableSAML2Auth": "SAML2認証をオンにする",
|
||||
"enableOAuth2Auth": "OAuth2認証をオンにする",
|
||||
"tokenHTTPMethod": "Token 取得方法",
|
||||
"SAML2Auth": "SAML2認定",
|
||||
"authSAML2Xml": "IDPメタデータXML",
|
||||
"authSAML2MetadataUrl": "IDPメタデータURL",
|
||||
@@ -1126,7 +1148,13 @@
|
||||
"SMS": "SMS設定",
|
||||
"feiShuTest": "テスト",
|
||||
"setting": "設定",
|
||||
"SMSProvider": "メールサービス業者"
|
||||
"SystemTools": "システムツール",
|
||||
"basicTools": "基本的なツール",
|
||||
"destinationIP": "宛先アドレス",
|
||||
"testPort": "テストポート",
|
||||
"testTools": "テスト",
|
||||
"testHelpText": "テストの宛先アドレスを入力してください",
|
||||
"SMSProvider": "メールサービス業者 / プロトコル"
|
||||
},
|
||||
"tickets": {
|
||||
"OneAssigneeType": "一次受付者タイプ",
|
||||
@@ -1158,6 +1186,10 @@
|
||||
"reply": "返信",
|
||||
"status": "ステータス",
|
||||
"title": "タイトル",
|
||||
"RelevantApp": "するアプリケーション",
|
||||
"RelevantAsset": "する資産",
|
||||
"RelevantCommand": "するコマンド",
|
||||
"RelevantSystemUser": "するシステムユーザー",
|
||||
"action": "アクション",
|
||||
"type": "タイプ",
|
||||
"user": "ユーザー",
|
||||
@@ -1227,7 +1259,7 @@
|
||||
"DatePasswordLastUpdated": "パスワード最終更新日",
|
||||
"DatePasswordUpdated": "パスワード更新日",
|
||||
"DescribeOfGuide": "詳細については、をクリックしてください。",
|
||||
"Email": "メール",
|
||||
"Email": "ポスト",
|
||||
"Phone": "携帯番号",
|
||||
"WeCom": "企業wechat",
|
||||
"DingTalk": "ホッチキス",
|
||||
@@ -1437,16 +1469,20 @@
|
||||
"IPNetworkSegment": "IPネットワークセグメント",
|
||||
"Aliyun": "Alibaba cloud",
|
||||
"Qcloud": "テンセント雲",
|
||||
"QcloudLighthouse": "テンセント雲(軽量アプリケーションサーバー)",
|
||||
"QingyunPrivatecloud": "青雲プライベートクラウド",
|
||||
"HuaweiPrivatecloud": "ファーウェイプライベートクラウド",
|
||||
"OpenStack": "OpenStack",
|
||||
"CTYunPrivate": "天翼プライベート・クラウド",
|
||||
"GCP": "Googleクラウド",
|
||||
"FC": "Fusion Compute",
|
||||
"LAN": "ローカルエリアネットワーク",
|
||||
"AWS_China": "AWS(中国)",
|
||||
"AWS_Int": "AWS (国際)",
|
||||
"HuaweiCloud": "ファーウェイ雲",
|
||||
"BaiduCloud": "百度雲",
|
||||
"JDCloud": "京東雲",
|
||||
"KingSoftCloud": "金山雲",
|
||||
"Azure": "Azure(中国)",
|
||||
"Azure_Int": "Azure (国際)",
|
||||
"HostnameStrategy": "資産を生成するためにホスト名。例: 1. インスタンス名 (instanceDemo) 2.インスタンス名と一部IP (下位2桁) (instanceDemo-250.1)",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"sqlserver": "SQLServer",
|
||||
"redis": "Redis",
|
||||
"mongodb": "MongoDB",
|
||||
"clickhouse": "ClickHouse",
|
||||
"k8s": "Kubernetes"
|
||||
},
|
||||
"applicationsCategory": {
|
||||
@@ -110,6 +111,7 @@
|
||||
"AssetDetail": "资产详情",
|
||||
"AssetList": "资产列表",
|
||||
"ReplaceNodeAssetsAdminUser":"替换节点资产的管理员",
|
||||
"AssetTree": "资产树",
|
||||
"AssetListHelpMessage": "左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,右侧是属于该节点下的资产\n",
|
||||
"TestGatewayTestConnection":"测试连接网关",
|
||||
"TestGatewayHelpMessage": "如果使用了nat端口映射,请设置为ssh真实监听的端口",
|
||||
@@ -248,7 +250,9 @@
|
||||
"SystemUserName": "系统用户名",
|
||||
"LoginIP": "登录IP",
|
||||
"LoginCity": "登录城市",
|
||||
"LoginDate": "登录日期"
|
||||
"LoginDate": "登录日期",
|
||||
"BeforeChange": "变更前",
|
||||
"AfterChange": "变更后"
|
||||
},
|
||||
"auth": {
|
||||
"LoginRequiredMsg": "账号已退出,请重新登录",
|
||||
@@ -258,6 +262,7 @@
|
||||
"ReLoginErr": "登录时长已超过 5 分钟,请重新登录"
|
||||
},
|
||||
"common": {
|
||||
"NoContent": "暂无内容",
|
||||
"NeedAddAppsOrSystemUserErrMsg": "需要添加应用或系统用户",
|
||||
"VerificationCodeSent": "验证码已发送",
|
||||
"SendVerificationCode": "发送验证码",
|
||||
@@ -271,6 +276,12 @@
|
||||
"IPLoginLimit": "IP 登录限制",
|
||||
"Setting": "设置",
|
||||
"Certificate": "证书",
|
||||
"CACertificate": "CA 证书",
|
||||
"ClientCertificate": "客户端证书",
|
||||
"CertificateKey": "证书秘钥文件",
|
||||
"AllowInvalidCert": "忽略证书检查",
|
||||
"UseSSL": "使用 SSL/TLS",
|
||||
"SecretKey": "密钥",
|
||||
"Scope": "类别",
|
||||
"Builtin": "内置",
|
||||
"DateCreated": "创建日期",
|
||||
@@ -416,6 +427,7 @@
|
||||
"disableSelected": "禁用所选",
|
||||
"disableSuccessMsg": "禁用成功",
|
||||
"fieldRequiredError": "这个字段是必填项",
|
||||
"requiredHasUserNameMapped": "必须包含 username 字段的映射,如 { 'uid': 'username' }",
|
||||
"getErrorMsg": "获取失败",
|
||||
"MFAErrorMsg": "MFA错误,请检查",
|
||||
"Total": "总共",
|
||||
@@ -434,6 +446,7 @@
|
||||
"downloadImportTemplateMsg": "下载创建模板",
|
||||
"downloadUpdateTemplateMsg": "下载更新模板",
|
||||
"onlyCSVFilesTips": "仅支持csv文件导入",
|
||||
"ImportFail": "导入失败",
|
||||
"updateSuccessMsg": "导入更新成功,总共:{count}",
|
||||
"uploadCsvLth10MHelpText": "只能上传 csv/xlsx, 且不超过 10M",
|
||||
"dragUploadFileInfo": "将文件拖到此处,或点击此处上传",
|
||||
@@ -491,6 +504,7 @@
|
||||
"InvalidJson": "不是合法 JSON",
|
||||
"time_period": "时段",
|
||||
"FormatError": "格式错误",
|
||||
"NotSpecialEmoji": "不允许输入特殊表情符号",
|
||||
"WeekCronSelect": {
|
||||
"Monday": "星期一",
|
||||
"Tuesday": "星期二",
|
||||
@@ -935,6 +949,7 @@
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"OAuth2LogoTip": "提示:认证服务提供商(建议图片大小为: 64px*64px)",
|
||||
"EndpointListHelpMessage": "服务端点是用户访问服务的地址(端口),当用户在连接资产时,会根据端点规则和资产标签选择服务端点,作为访问入口建立连接,实现分布式连接资产",
|
||||
"EndpointRuleListHelpMessage": "对于服务端点选择策略,目前支持两种:<br>1、根据端点规则指定端点(当前页面);<br>2、通过资产标签选择端点,标签名固定是 endpoint,值是端点的名称。<br>两种方式优先使用标签匹配,因为 IP 段可能冲突,标签方式是作为规则的补充存在的。",
|
||||
"EnableKoKoSSHHelpText": "开启时连接资产会显示 SSH Client 拉起方式",
|
||||
@@ -942,6 +957,10 @@
|
||||
"Feature": "功能",
|
||||
"AlibabaCloud": "阿里云",
|
||||
"TencentCloud": "腾讯云",
|
||||
"HuaweiCloud": "华为云",
|
||||
"SignChannelNum": "签名通道号",
|
||||
"AppEndpoint": "应用接入地址",
|
||||
"CMPP2": "CMPP v2.0",
|
||||
"Radius": "Radius",
|
||||
"VerifySignTmpl": "验证码短信模板",
|
||||
"Enable": "启用",
|
||||
@@ -999,9 +1018,10 @@
|
||||
"authLdapServerUri": "LDAP地址",
|
||||
"authLdapUserAttrMap": "用户属性映射",
|
||||
"authUserAttrMap": "用户属性映射",
|
||||
"authUserAttrMapHelpText": "映射关系 {idp:sp}",
|
||||
"SAML2": "SAML2",
|
||||
"enableSAML2Auth": "开启 SAML2 认证",
|
||||
"authUserAttrMapHelpText": "左侧的键为 JumpServer 用户属性,右侧的值为认证平台用户属性",
|
||||
"OAuth2": "OAuth2",
|
||||
"enableOAuth2Auth": "开启 OAuth2 认证",
|
||||
"tokenHTTPMethod": "Token 获取方法",
|
||||
"SAML2Auth": "SAML2 认证",
|
||||
"authSAML2Xml": "IDP metadata XML",
|
||||
"authSAML2MetadataUrl": "IDP metadata URL",
|
||||
@@ -1128,7 +1148,14 @@
|
||||
"SMS": "短信设置",
|
||||
"feiShuTest": "测试",
|
||||
"setting": "设置",
|
||||
"SMSProvider": "短信服务商"
|
||||
"SMSProvider": "短信服务商",
|
||||
"SystemTools": "系统工具",
|
||||
"basicTools": "基本工具",
|
||||
"destinationIP": "目的地址",
|
||||
"testPort": "端口",
|
||||
"testTools": "测试",
|
||||
"testHelpText": "请输入目的地址进行测试",
|
||||
"SMSProvider": "短信服务商 / 协议"
|
||||
},
|
||||
"tickets": {
|
||||
"OneAssigneeType": "一级受理人类型",
|
||||
@@ -1160,6 +1187,10 @@
|
||||
"reply": "回复",
|
||||
"status": "状态",
|
||||
"title": "标题",
|
||||
"RelevantApp": "应用",
|
||||
"RelevantAsset": "资产",
|
||||
"RelevantCommand": "命令",
|
||||
"RelevantSystemUser": "系统用户",
|
||||
"action": "动作",
|
||||
"type": "类型",
|
||||
"user": "用户",
|
||||
@@ -1229,7 +1260,7 @@
|
||||
"DatePasswordLastUpdated": "最后更新密码日期",
|
||||
"DatePasswordUpdated": "密码更新日期",
|
||||
"DescribeOfGuide": "欢迎使用JumpServer堡垒机系统,获取更多信息请点击",
|
||||
"Email": "邮件",
|
||||
"Email": "邮箱",
|
||||
"Phone": "手机号",
|
||||
"WeCom": "企业微信",
|
||||
"DingTalk": "钉钉",
|
||||
@@ -1439,16 +1470,20 @@
|
||||
"IPNetworkSegment": "IP网段",
|
||||
"Aliyun": "阿里云",
|
||||
"Qcloud": "腾讯云",
|
||||
"QcloudLighthouse": "腾讯云(轻量应用服务器)",
|
||||
"QingyunPrivatecloud": "青云私有云",
|
||||
"HuaweiPrivatecloud": "华为私有云",
|
||||
"CTYunPrivate": "天翼私有云",
|
||||
"OpenStack": "OpenStack",
|
||||
"GCP": "谷歌云",
|
||||
"FC": "Fusion Compute",
|
||||
"LAN": "局域网",
|
||||
"AWS_China": "AWS(中国)",
|
||||
"AWS_Int": "AWS(国际)",
|
||||
"HuaweiCloud": "华为云",
|
||||
"BaiduCloud": "百度云",
|
||||
"JDCloud": "京东云",
|
||||
"KingSoftCloud": "金山云",
|
||||
"Azure":"Azure(中国)",
|
||||
"Azure_Int": "Azure(国际)",
|
||||
"HostnameStrategy": "用于生成资产主机名。例如:1. 实例名称 (instanceDemo);2. 实例名称和部分IP(后两位) (instanceDemo-250.1)",
|
||||
|
||||
@@ -1 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1645509697274" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2361" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M867.046319 156.952658 156.952658 156.952658c-49.040926 0-88.317465 39.720653-88.317465 88.76158l-0.444115 532.570501c0 49.040926 39.720653 88.76158 88.76158 88.76158l710.09366 0c49.040926 0 88.76158-39.720653 88.76158-88.76158L955.807898 245.714238C955.808922 196.673311 916.087245 156.952658 867.046319 156.952658zM867.046319 334.476841l-355.047342 221.903949-355.047342-221.903949 0-88.76158 355.047342 221.903949 355.047342-221.903949L867.046319 334.476841z" p-id="2362"></path></svg>
|
||||
<svg t="1659513943772" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5163" width="200" height="200"><path d="M1024 146.285714v731.428572H0V146.285714h1024z m-149.942857 94.573715L512 602.88l-362.057143-362.057143L98.267429 292.571429 512 706.340571 925.769143 292.571429l-51.748572-51.712z" fill="#7c7e7f" p-id="5164"></path></svg>
|
||||
|
Before Width: | Height: | Size: 865 B After Width: | Height: | Size: 377 B |
@@ -233,6 +233,10 @@ export default {
|
||||
encryptedFields: {
|
||||
type: Array,
|
||||
default: () => ['password', 'token', 'private_key']
|
||||
},
|
||||
needGetObjectDetail: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -312,7 +316,7 @@ export default {
|
||||
},
|
||||
async getFormValue() {
|
||||
const cloneFrom = this.$route.query['clone_from']
|
||||
if (!this.isUpdateMethod() && !cloneFrom) {
|
||||
if ((!this.isUpdateMethod() && !cloneFrom) || !this.needGetObjectDetail) {
|
||||
return Object.assign(this.form, this.initial)
|
||||
}
|
||||
let object = this.object
|
||||
@@ -322,9 +326,9 @@ export default {
|
||||
const url = `${curUrl}${cloneFrom}/${query ? ('?' + query) : ''}`
|
||||
object = await this.getObjectDetail(url)
|
||||
if (object['name']) {
|
||||
object.name = this.$t('common.cloneFrom') + ' ' + object.name
|
||||
object.name = this.$t('common.cloneFrom') + object.name
|
||||
} else if (object['hostname']) {
|
||||
object.hostname = this.$t('common.cloneFrom') + ' ' + object.hostname
|
||||
object.hostname = this.$t('common.cloneFrom') + object.hostname
|
||||
}
|
||||
} else {
|
||||
object = await this.getObjectDetail(this.iUrl)
|
||||
|
||||
@@ -97,6 +97,7 @@ export default {
|
||||
getDefaultFormSetting() {
|
||||
const vm = this
|
||||
return {
|
||||
needGetObjectDetail: false,
|
||||
submitMethod: () => 'patch',
|
||||
cleanFormValue: (value) => {
|
||||
const filterValue = {}
|
||||
|
||||
@@ -177,7 +177,7 @@ export default {
|
||||
}
|
||||
|
||||
&>>> .el-input__icon {
|
||||
color: #606266;
|
||||
color: #606266!important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,8 +117,10 @@ export default {
|
||||
.navbar-right {
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
|
||||
height: 55px;
|
||||
line-height: 55px;
|
||||
.header-hover {
|
||||
line-height: 56px!important;
|
||||
&:hover {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.wrapper-content {
|
||||
padding: 20px 25px 40px;
|
||||
padding: 20px 25px 10px;
|
||||
}
|
||||
|
||||
.wrapper-content >>> .el-alert {
|
||||
|
||||
@@ -43,6 +43,10 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
height: calc(100vh - 55px - 41px);
|
||||
overflow: auto;
|
||||
}
|
||||
@media print {
|
||||
.disabled-when-print{
|
||||
display: none;
|
||||
|
||||
@@ -46,7 +46,6 @@ Vue.use(require('vue-moment'), {
|
||||
moment
|
||||
})
|
||||
|
||||
// logger
|
||||
import VueLogger from 'vuejs-logger'
|
||||
import loggerOptions from './utils/logger'
|
||||
Vue.use(VueLogger, loggerOptions)
|
||||
@@ -55,14 +54,14 @@ import ECharts from 'vue-echarts'
|
||||
Vue.component('echarts', ECharts)
|
||||
|
||||
import service from '@/utils/request'
|
||||
Vue.prototype.$axios = service
|
||||
|
||||
// lodash
|
||||
// import _ from 'lodash'
|
||||
window._ = require('lodash')
|
||||
// Vue.set(Vue.prototype, '_', _)
|
||||
|
||||
// if the table component cannot access `this.$axios`, it cannot send request
|
||||
Vue.prototype.$axios = service
|
||||
import { Message } from '@/utils/Message'
|
||||
Vue.prototype.$message = Message
|
||||
|
||||
// 注册全局事件总线
|
||||
Vue.prototype.$eventBus = new Vue()
|
||||
new Vue({
|
||||
|
||||
@@ -15,6 +15,16 @@ export default [
|
||||
permissions: []
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/ops/ansible/task/:id/log/',
|
||||
component: () => import('@/views/ops/CeleryTaskLog'),
|
||||
name: 'AnsibleTaskLog',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('route.CeleryTaskLog'),
|
||||
permissions: []
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/ops/task/task/:id/log/',
|
||||
component: () => import('@/views/ops/CeleryTaskLog'),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import empty from '@/layout/empty'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
const activateMenu = '/console/assets/assets'
|
||||
|
||||
export default [
|
||||
{
|
||||
path: 'cloud',
|
||||
@@ -20,7 +22,7 @@ export default [
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.Cloud.CloudSync'),
|
||||
activeMenu: '/console/assets/assets'
|
||||
activeMenu: activateMenu
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -71,6 +73,7 @@ export default [
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.Cloud.AccountDetail'),
|
||||
activeMenu: activateMenu,
|
||||
permissions: ['xpack.view_account']
|
||||
}
|
||||
}
|
||||
@@ -121,7 +124,8 @@ export default [
|
||||
name: 'SyncInstanceTaskDetail',
|
||||
hidden: true,
|
||||
meta: {
|
||||
title: i18n.t('xpack.Cloud.SyncInstanceTaskDetail')
|
||||
title: i18n.t('xpack.Cloud.SyncInstanceTaskDetail'),
|
||||
activeMenu: activateMenu
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -295,6 +295,16 @@ export default {
|
||||
permissions: ['settings.change_other']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/settings/tools',
|
||||
name: 'Tools',
|
||||
component: () => import('@/views/settings/Tools'),
|
||||
meta: {
|
||||
title: i18n.t('setting.SystemTools'),
|
||||
icon: 'wrench',
|
||||
permissions: ['settings.view_setting']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/settings/license',
|
||||
name: 'License',
|
||||
|
||||
@@ -22,7 +22,7 @@ export default {
|
||||
name: 'MyTicketList',
|
||||
component: () => import('@/views/tickets/MyTicketList'),
|
||||
meta: {
|
||||
title: i18n.t('route.MyTickets'),
|
||||
title: i18n.t('tickets.MyTickets'),
|
||||
icon: 'file-text-o',
|
||||
showOrganization: false,
|
||||
permissions: []
|
||||
|
||||
@@ -72,6 +72,9 @@ const mutations = {
|
||||
},
|
||||
ADD_WORKBENCH_ORGS(state, org) {
|
||||
state.workbenchOrgs.push(org)
|
||||
},
|
||||
SET_IS_FIRST_LOGIN(state, flag) {
|
||||
state.profile.is_first_login = flag
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +143,9 @@ const actions = {
|
||||
const usingOrgs = mapper[viewName] || state.consoleOrgs
|
||||
Vue.$log.debug('Set using orgs: ', viewName, usingOrgs)
|
||||
commit('SET_USING_ORGS', usingOrgs)
|
||||
},
|
||||
ifFirstLogin({ commit }, flag) {
|
||||
commit('SET_IS_FIRST_LOGIN', flag)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,6 +182,9 @@ input[type=file] {
|
||||
|
||||
.el-col.el-col-sm-24 .ibox {
|
||||
margin-bottom: 10px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
|
||||
20
src/utils/Message.js
Normal file
20
src/utils/Message.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// 重置message,防止重复点击重复弹出message弹框
|
||||
import { Message as elMessage } from 'element-ui'
|
||||
|
||||
let messageDom = null
|
||||
const Message = (options) => {
|
||||
// 判断弹窗是否已存在, 若存在则关闭
|
||||
if (messageDom) messageDom.close()
|
||||
messageDom = elMessage(options)
|
||||
}
|
||||
|
||||
const typeArray = ['success', 'error', 'warning', 'info']
|
||||
typeArray.forEach(type => {
|
||||
Message[type] = options => {
|
||||
if (typeof options === 'string') options = { message: options }
|
||||
options.type = type
|
||||
return Message(options)
|
||||
}
|
||||
})
|
||||
|
||||
export { Message }
|
||||
@@ -302,3 +302,8 @@ export function groupedDropdownToCascader(group) {
|
||||
|
||||
export { BASE_URL }
|
||||
|
||||
export function openWindow(url, name = '', iWidth = 900, iHeight = 600) {
|
||||
var iTop = (window.screen.height - 30 - iHeight) / 2
|
||||
var iLeft = (window.screen.width - 10 - iWidth) / 2
|
||||
window.open(url, name, 'height=' + iHeight + ',width=' + iWidth + ',top=' + iTop + ',left=' + iLeft)
|
||||
}
|
||||
|
||||
@@ -3,17 +3,11 @@ import CryptoJS from 'crypto-js'
|
||||
import VueCookie from 'vue-cookie'
|
||||
|
||||
export function fillKey(key) {
|
||||
let keySize = 128
|
||||
// 如果超过 key 16 位, 最大取 32 位,需要更改填充
|
||||
if (key.length > 16) {
|
||||
key = key.slice(0, 32)
|
||||
keySize = keySize * 2
|
||||
const KeyLength = 16
|
||||
if (key.length > KeyLength) {
|
||||
key = key.slice(0, KeyLength)
|
||||
}
|
||||
const filledKeyLength = keySize / 8
|
||||
if (key.length >= filledKeyLength) {
|
||||
return key.slice(0, filledKeyLength)
|
||||
}
|
||||
const filledKey = Buffer.alloc(keySize / 8)
|
||||
const filledKey = Buffer.alloc(KeyLength)
|
||||
const keys = Buffer.from(key)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
filledKey[i] = keys[i]
|
||||
@@ -43,10 +37,13 @@ export function encryptPassword(password) {
|
||||
if (!password) {
|
||||
return ''
|
||||
}
|
||||
let rsaPublicKeyText = getCookie('jms_public_key')
|
||||
if (!rsaPublicKeyText) {
|
||||
return password
|
||||
}
|
||||
const aesKey = (Math.random() + 1).toString(36).substring(2)
|
||||
// public key 是 base64 存储的
|
||||
const rsaPublicKeyText = getCookie('jms_public_key')
|
||||
.replaceAll('"', '')
|
||||
rsaPublicKeyText = rsaPublicKeyText.replaceAll('"', '')
|
||||
const rsaPublicKey = atob(rsaPublicKeyText)
|
||||
const keyCipher = rsaEncrypt(aesKey, rsaPublicKey)
|
||||
const passwordCipher = aesEncrypt(password, aesKey)
|
||||
@@ -54,4 +51,5 @@ export function encryptPassword(password) {
|
||||
}
|
||||
|
||||
window.aesEncrypt = aesEncrypt
|
||||
window.fillKey = fillKey
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import store from '@/store'
|
||||
import { constantRoutes } from '@/router'
|
||||
import { openWindow } from './common'
|
||||
|
||||
export function openTaskPage(taskId) {
|
||||
window.open(`/#/ops/celery/task/${taskId}/log/`, '', 'width=900,height=600')
|
||||
export function openTaskPage(taskId, taskType) {
|
||||
taskType = taskType || 'celery'
|
||||
openWindow(`/#/ops/${taskType}/task/${taskId}/log/?type=${taskType}`)
|
||||
}
|
||||
|
||||
export function checkPermission(permsRequired, permsAll) {
|
||||
|
||||
2
src/utils/jquery-vendor.js
vendored
2
src/utils/jquery-vendor.js
vendored
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery'
|
||||
import $ from 'jquery/dist/jquery.min.js'
|
||||
window.$ = $
|
||||
window.jQuery = $
|
||||
export default $
|
||||
|
||||
@@ -3,7 +3,8 @@ import i18n from '@/i18n/i18n'
|
||||
import { getTokenFromCookie } from '@/utils/auth'
|
||||
import { getErrorResponseMsg } from '@/utils/common'
|
||||
import { refreshSessionIdAge } from '@/api/users'
|
||||
import { Message, MessageBox } from 'element-ui'
|
||||
import { MessageBox } from 'element-ui'
|
||||
import { Message } from '@/utils/Message'
|
||||
import store from '@/store'
|
||||
import axiosRetry from 'axios-retry'
|
||||
import router from '@/router'
|
||||
|
||||
@@ -3,7 +3,7 @@ import store from '@/store'
|
||||
import router, { resetRouter } from '@/router'
|
||||
import Vue from 'vue'
|
||||
import VueCookie from 'vue-cookie'
|
||||
import { Message } from 'element-ui'
|
||||
import { Message } from '@/utils/Message'
|
||||
import orgUtil from '@/utils/org'
|
||||
import orgs from '@/api/orgs'
|
||||
import { getPropView, isViewHasOrgs } from '@/utils/jms'
|
||||
|
||||
@@ -39,6 +39,9 @@ export function changeElementColor(themeColors) {
|
||||
.el-link.el-link--${key}:after {
|
||||
border-color: ${value}!important;
|
||||
}
|
||||
.el-tag--dark.el-tag--${key} {
|
||||
background-color: ${value} !important;
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,6 @@ export default {
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasMoreActions: false,
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'AccountBackupPlanCreate'
|
||||
|
||||
@@ -123,7 +123,6 @@ export default {
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasMoreActions: false,
|
||||
searchConfig: {
|
||||
getUrlQuery: false
|
||||
},
|
||||
|
||||
@@ -74,6 +74,9 @@ export default {
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasCreate: false,
|
||||
searchConfig: {
|
||||
getUrlQuery: false
|
||||
},
|
||||
hasMoreActions: false
|
||||
},
|
||||
assetRelationConfig: {
|
||||
|
||||
@@ -107,7 +107,6 @@ export default {
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasMoreActions: false,
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'AssetChangeAuthPlanCreate'
|
||||
|
||||
@@ -84,8 +84,7 @@ export default {
|
||||
createRoute: 'AssetAclCreate',
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
hasMoreActions: false
|
||||
hasImport: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { getDatabaseTypeFieldsMap } from '@/views/applications/DatabaseApp/const'
|
||||
import { UploadKey } from '@/components'
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
@@ -36,6 +39,32 @@ export default {
|
||||
fieldsMeta: {
|
||||
host: {
|
||||
type: 'input'
|
||||
},
|
||||
port: {
|
||||
rules: [Required]
|
||||
},
|
||||
use_ssl: {
|
||||
label: this.$t('common.UseSSL'),
|
||||
component: 'el-switch'
|
||||
},
|
||||
allow_invalid_cert: {
|
||||
label: this.$t('common.AllowInvalidCert'),
|
||||
hidden: (form) => { return !form.use_ssl }
|
||||
},
|
||||
ca_cert: {
|
||||
label: this.$t('common.CACertificate'),
|
||||
hidden: (form) => { return !form.use_ssl },
|
||||
component: UploadKey
|
||||
},
|
||||
client_cert: {
|
||||
label: this.$t('common.ClientCertificate'),
|
||||
hidden: (form) => { return !form.use_ssl },
|
||||
component: UploadKey
|
||||
},
|
||||
cert_key: {
|
||||
label: this.$t('common.CertificateKey'),
|
||||
hidden: (form) => { return !form.use_ssl },
|
||||
component: UploadKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { ORACLE } from '../const'
|
||||
import { MONGODB, REDIS } from '../const'
|
||||
|
||||
export function getDatabaseTypeFieldsMap(type) {
|
||||
const baseParams = ['host', 'port', 'database']
|
||||
const tlsParams = ['use_ssl', 'ca_cert']
|
||||
switch (type) {
|
||||
case ORACLE:
|
||||
return ['host', 'port', 'database', 'version']
|
||||
case REDIS:
|
||||
return baseParams.concat(tlsParams.concat(['client_cert', 'cert_key']))
|
||||
case MONGODB:
|
||||
return baseParams.concat(tlsParams.concat(['cert_key', 'allow_invalid_cert']))
|
||||
default:
|
||||
return ['host', 'port', 'database']
|
||||
return baseParams
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,64 +52,82 @@ export const DATABASE_CATEGORY = 'db'
|
||||
export const SQLSERVER = 'sqlserver'
|
||||
export const REDIS = 'redis'
|
||||
export const MONGODB = 'mongodb'
|
||||
export const CLICKHOUSE = 'clickhouse'
|
||||
|
||||
const MYSQL_ITEM = {
|
||||
name: MYSQL,
|
||||
title: i18n.t(`applications.applicationsType.${MYSQL}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: true,
|
||||
group: i18n.t('applications.RDBProtocol')
|
||||
}
|
||||
|
||||
const MARIADB_ITEM = {
|
||||
name: MARIADB,
|
||||
title: i18n.t(`applications.applicationsType.${MARIADB}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: true
|
||||
}
|
||||
|
||||
const ORACLE_ITEM = {
|
||||
name: ORACLE,
|
||||
title: i18n.t(`applications.applicationsType.${ORACLE}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: hasLicence
|
||||
}
|
||||
|
||||
const POSTGRESQL_ITEM = {
|
||||
name: POSTGRESQL,
|
||||
title: i18n.t(`applications.applicationsType.${POSTGRESQL}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: hasLicence
|
||||
}
|
||||
|
||||
const SQLSERVER_ITEM = {
|
||||
name: SQLSERVER,
|
||||
title: i18n.t(`applications.applicationsType.${SQLSERVER}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: hasLicence
|
||||
}
|
||||
|
||||
const CLICKHOUSE_ITEM = {
|
||||
name: CLICKHOUSE,
|
||||
title: i18n.t(`applications.applicationsType.${CLICKHOUSE}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: hasLicence
|
||||
}
|
||||
|
||||
const MONGODB_ITEM = {
|
||||
name: MONGODB,
|
||||
title: i18n.t(`applications.applicationsType.${MONGODB}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
group: i18n.t('applications.NoSQLProtocol')
|
||||
}
|
||||
|
||||
const REDIS_ITEM = {
|
||||
name: REDIS,
|
||||
title: i18n.t(`applications.applicationsType.${REDIS}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: true
|
||||
}
|
||||
|
||||
export const DATABASE = [
|
||||
{
|
||||
name: MYSQL,
|
||||
title: i18n.t(`applications.applicationsType.${MYSQL}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: true,
|
||||
group: i18n.t('applications.RDBProtocol')
|
||||
},
|
||||
{
|
||||
name: MARIADB,
|
||||
title: i18n.t(`applications.applicationsType.${MARIADB}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: true
|
||||
},
|
||||
{
|
||||
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: SQLSERVER,
|
||||
title: i18n.t(`applications.applicationsType.${SQLSERVER}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: hasLicence
|
||||
}
|
||||
MYSQL_ITEM, MARIADB_ITEM, ORACLE_ITEM, POSTGRESQL_ITEM, SQLSERVER_ITEM, CLICKHOUSE_ITEM
|
||||
]
|
||||
|
||||
export const KV_DATABASE = [
|
||||
{
|
||||
name: REDIS,
|
||||
title: i18n.t(`applications.applicationsType.${REDIS}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY,
|
||||
has: true,
|
||||
group: i18n.t('applications.NoSQLProtocol')
|
||||
},
|
||||
{
|
||||
name: MONGODB,
|
||||
title: i18n.t(`applications.applicationsType.${MONGODB}`),
|
||||
type: 'primary',
|
||||
category: DATABASE_CATEGORY
|
||||
}
|
||||
MONGODB_ITEM, REDIS_ITEM
|
||||
]
|
||||
|
||||
export const AppPlanDatabase = DATABASE
|
||||
export const AppPlanDatabase = [MYSQL_ITEM, MARIADB_ITEM, ORACLE_ITEM, POSTGRESQL_ITEM, SQLSERVER_ITEM, MONGODB_ITEM]
|
||||
|
||||
export const KUBERNETES = 'k8s'
|
||||
export const CLOUD_CATEGORY = 'cloud'
|
||||
|
||||
@@ -1,18 +1,40 @@
|
||||
<template>
|
||||
<IBox :fa="icon" :type="type" :title="title" v-bind="$attrs">
|
||||
<table style="width: 100%;table-layout:fixed;" class="CardTable">
|
||||
<tr v-for="obj of iObjects" :key="obj.value" class="item">
|
||||
<td style="overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">
|
||||
<el-tooltip style="margin: 4px;" effect="dark" :content="obj.label" placement="left">
|
||||
<el-link class="detail" @click="goDetail(obj)">{{ obj.label }}</el-link>
|
||||
</el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<el-button size="mini" type="primary" style="float: right" @click="buttonClickCallback(obj)">
|
||||
{{ buttonTitle }}
|
||||
</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
<IBox
|
||||
:fa="icon"
|
||||
:type="type"
|
||||
:title="title"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<table class="card-table">
|
||||
<div v-if="iObjects.length > 0" v-cloak>
|
||||
<tr v-for="obj of iObjects" :key="obj.value" class="item">
|
||||
<td>
|
||||
<el-tooltip
|
||||
style="margin: 4px;"
|
||||
effect="dark"
|
||||
:content="obj.label"
|
||||
placement="left"
|
||||
>
|
||||
<el-link class="detail" @click="goDetail(obj)">
|
||||
{{ obj.label }}
|
||||
</el-link>
|
||||
</el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
style="float: right"
|
||||
@click="buttonClickCallback(obj)"
|
||||
>
|
||||
{{ buttonTitle }}
|
||||
</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
<div v-else v-cloak style="text-align: center;">
|
||||
{{ $t('common.NoData') }}
|
||||
</div>
|
||||
</table>
|
||||
</IBox>
|
||||
</template>
|
||||
@@ -71,9 +93,9 @@ export default {
|
||||
methods: {
|
||||
async loadObjects() {
|
||||
const data = await this.$axios.get(this.url)
|
||||
data.forEach((v) => {
|
||||
for (const v of data) {
|
||||
v['label'] = v['name']
|
||||
})
|
||||
}
|
||||
this.objects = data
|
||||
},
|
||||
goDetail(obj) {
|
||||
@@ -84,18 +106,26 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-table {
|
||||
width: 100%;
|
||||
table-layout:fixed;
|
||||
}
|
||||
[v-cloak]{
|
||||
display: none!important;
|
||||
}
|
||||
b, strong {
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
tr td {
|
||||
line-height: 1.42857;
|
||||
padding: 8px;
|
||||
vertical-align: top;
|
||||
display: inline;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
tr.item {
|
||||
border-bottom: 1px solid #e7eaec;
|
||||
padding: 8px;
|
||||
|
||||
@@ -48,6 +48,8 @@ export default {
|
||||
showUpdate: true,
|
||||
showDelete: true,
|
||||
hasRightMenu: true,
|
||||
showSearch: true,
|
||||
customTreeHeader: true,
|
||||
url: '/api/v1/assets/assets/',
|
||||
nodeUrl: '/api/v1/assets/nodes/',
|
||||
// ?assets=0不显示资产. =1显示资产
|
||||
|
||||
@@ -88,7 +88,10 @@ export default {
|
||||
},
|
||||
headerActions: {
|
||||
hasLeftActions: false,
|
||||
hasRightActions: false
|
||||
hasRightActions: false,
|
||||
searchConfig: {
|
||||
getUrlQuery: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { Required } from '@/components/DataForm/rules'
|
||||
import { Required, specialEmojiCheck } from '@/components/DataForm/rules'
|
||||
import { ACCOUNT_PROVIDER_ATTRS_MAP, aliyun } from '../const'
|
||||
import { UploadKey } from '@/components'
|
||||
import { encryptPassword } from '@/utils/crypto'
|
||||
@@ -17,10 +17,24 @@ export default {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
const accountProvider = this.$route.query.provider || aliyun
|
||||
const accountProviderAttrs = ACCOUNT_PROVIDER_ATTRS_MAP[accountProvider]
|
||||
function setFieldAttrs() {
|
||||
const fieldsObject = {}
|
||||
const updateNotRequiredFields = ['access_key_secret', 'client_secret', 'password', 'sc_password', 'oc_password', 'cert_file', 'key_file']
|
||||
for (const item of accountProviderAttrs?.attrs) {
|
||||
fieldsObject[item] = {
|
||||
rules: updateNotRequiredFields.includes(item) && vm.$route.params.id ? [] : [Required]
|
||||
}
|
||||
}
|
||||
return fieldsObject
|
||||
}
|
||||
return {
|
||||
initial: {
|
||||
attrs: {
|
||||
ip_group: []
|
||||
},
|
||||
provider: this.$route.query.provider,
|
||||
port: 443
|
||||
},
|
||||
@@ -31,10 +45,15 @@ export default {
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
name: {
|
||||
rules: [Required, specialEmojiCheck]
|
||||
},
|
||||
attrs: {
|
||||
encryptedFields: ['access_key_secret'],
|
||||
fields: accountProviderAttrs.attrs,
|
||||
fieldsMeta: {
|
||||
// 必须放在最上面,下面特殊制定的字段才会覆盖默认
|
||||
...setFieldAttrs(),
|
||||
service_account_key: {
|
||||
label: this.$t('xpack.Cloud.ServerAccountKey'),
|
||||
component: UploadKey,
|
||||
@@ -42,6 +61,20 @@ export default {
|
||||
toFormat: 'object'
|
||||
}
|
||||
},
|
||||
cert_file: {
|
||||
label: this.$t('common.Certificate'),
|
||||
component: UploadKey,
|
||||
el: {
|
||||
toFormat: 'object'
|
||||
}
|
||||
},
|
||||
key_file: {
|
||||
label: this.$t('common.SecretKey'),
|
||||
component: UploadKey,
|
||||
el: {
|
||||
toFormat: 'object'
|
||||
}
|
||||
},
|
||||
password: {
|
||||
rules: this.$route.params.id ? [] : [Required]
|
||||
}
|
||||
@@ -72,16 +105,34 @@ export default {
|
||||
const attrs = values.attrs
|
||||
for (const item of encryptedFields) {
|
||||
const value = attrs[item]
|
||||
if (value) {
|
||||
attrs[item] = encryptPassword(value)
|
||||
if (!value) {
|
||||
continue
|
||||
}
|
||||
attrs[item] = encryptPassword(value)
|
||||
}
|
||||
const toListFields = ['ip_group']
|
||||
for (const item of toListFields) {
|
||||
let value = attrs[item]
|
||||
if (!value) {
|
||||
continue
|
||||
}
|
||||
value = value?.split(',') || []
|
||||
value = value.filter((value, index) => { if (value) return true })
|
||||
attrs[item] = value
|
||||
}
|
||||
return values
|
||||
},
|
||||
afterGetFormValue(formValue) {
|
||||
if (!formValue.attrs) {
|
||||
return formValue
|
||||
}
|
||||
if (Array.isArray(formValue.attrs.ip_group)) {
|
||||
formValue.attrs.ip_group = formValue.attrs.ip_group.toString()
|
||||
}
|
||||
return formValue
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<script type="text/jsx">
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import { ACCOUNT_PROVIDER_ATTRS_MAP, aliyun, aws_china, aws_international, huaweicloud, qcloud, azure, azure_international, vmware, nutanix, qingcloud_private, huaweicloud_private, openstack, gcp, baiducloud, jdcloud, fc } from '../const'
|
||||
import { ACCOUNT_PROVIDER_ATTRS_MAP, aliyun, aws_china, aws_international, huaweicloud, qcloud, qcloud_lighthouse, azure, azure_international, vmware, nutanix, qingcloud_private, huaweicloud_private, ctyun_private, openstack, gcp, baiducloud, jdcloud, kingsoftcloud, fc, lan } from '../const'
|
||||
|
||||
export default {
|
||||
name: 'AccountList',
|
||||
@@ -82,6 +82,10 @@ export default {
|
||||
type: 'primary',
|
||||
can: true
|
||||
},
|
||||
{
|
||||
name: qcloud_lighthouse,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[qcloud_lighthouse].title
|
||||
},
|
||||
{
|
||||
name: huaweicloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[huaweicloud].title
|
||||
@@ -94,6 +98,10 @@ export default {
|
||||
name: jdcloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[jdcloud].title
|
||||
},
|
||||
{
|
||||
name: kingsoftcloud,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[kingsoftcloud].title
|
||||
},
|
||||
{
|
||||
name: aws_china,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[aws_china].title
|
||||
@@ -127,6 +135,10 @@ export default {
|
||||
name: huaweicloud_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[huaweicloud_private].title
|
||||
},
|
||||
{
|
||||
name: ctyun_private,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[ctyun_private].title
|
||||
},
|
||||
{
|
||||
name: openstack,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[openstack].title
|
||||
@@ -138,6 +150,10 @@ export default {
|
||||
{
|
||||
name: fc,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[fc].title
|
||||
},
|
||||
{
|
||||
name: lan,
|
||||
title: ACCOUNT_PROVIDER_ATTRS_MAP[lan].title
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export default {
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name']],
|
||||
[this.$t('xpack.Cloud.CloudSource'), ['account', 'regions']],
|
||||
[this.$t('xpack.Cloud.SaveSetting'), ['hostname_strategy', 'node', 'unix_admin_user', 'windows_admin_user', 'protocols', 'ip_network_segment_group', 'is_always_update']],
|
||||
[this.$t('xpack.Cloud.SaveSetting'), ['hostname_strategy', 'node', 'unix_admin_user', 'windows_admin_user', 'protocols', 'ip_network_segment_group', 'sync_ip_type', 'is_always_update']],
|
||||
[this.$t('xpack.Timer'), ['is_periodic', 'crontab', 'interval']],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
@@ -92,6 +92,7 @@ export default {
|
||||
component: Select2,
|
||||
el: {
|
||||
multiple: true,
|
||||
allowCreate: true,
|
||||
value: [],
|
||||
ajax: {
|
||||
url: '/api/v1/xpack/cloud/regions/',
|
||||
|
||||
@@ -38,6 +38,7 @@ export default {
|
||||
},
|
||||
tableConfig: {
|
||||
url: `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.id}/instances/`,
|
||||
hasSelection: false,
|
||||
columns: [
|
||||
'instance_id',
|
||||
{
|
||||
|
||||
@@ -58,10 +58,7 @@ export default {
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
permissions: 'xpack.view_syncinstancedetail',
|
||||
route: 'SyncInstanceTaskDetail',
|
||||
routeQuery: {
|
||||
activeTab: 'detail'
|
||||
}
|
||||
route: 'SyncInstanceTaskDetail'
|
||||
}
|
||||
},
|
||||
history_count: {
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const gcp = 'gcp'
|
||||
export const aliyun = 'aliyun'
|
||||
export const baiducloud = 'baiducloud'
|
||||
export const jdcloud = 'jdcloud'
|
||||
export const kingsoftcloud = 'kingsoftcloud'
|
||||
export const aws_international = 'aws_international'
|
||||
export const aws_china = 'aws_china'
|
||||
export const huaweicloud = 'huaweicloud'
|
||||
export const qcloud = 'qcloud'
|
||||
export const qcloud_lighthouse = 'qcloud_lighthouse'
|
||||
export const azure = 'azure'
|
||||
export const azure_international = 'azure_international'
|
||||
export const vmware = 'vmware'
|
||||
export const nutanix = 'nutanix'
|
||||
|
||||
export const qingcloud_private = 'qingcloud_private'
|
||||
export const huaweicloud_private = 'huaweicloud_private'
|
||||
export const ctyun_private = 'ctyun_private'
|
||||
export const openstack = 'openstack'
|
||||
export const gcp = 'gcp'
|
||||
export const nutanix = 'nutanix'
|
||||
export const vmware = 'vmware'
|
||||
export const fc = 'fc'
|
||||
export const baiducloud = 'baiducloud'
|
||||
export const jdcloud = 'jdcloud'
|
||||
export const lan = 'lan'
|
||||
|
||||
export const ACCOUNT_PROVIDER_ATTRS_MAP = {
|
||||
[aliyun]: {
|
||||
@@ -48,11 +53,21 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
|
||||
title: i18n.t('xpack.Cloud.JDCloud'),
|
||||
attrs: ['access_key_id', 'access_key_secret']
|
||||
},
|
||||
[kingsoftcloud]: {
|
||||
name: kingsoftcloud,
|
||||
title: i18n.t('xpack.Cloud.KingSoftCloud'),
|
||||
attrs: ['access_key_id', 'access_key_secret']
|
||||
},
|
||||
[qcloud]: {
|
||||
name: qcloud,
|
||||
title: i18n.t('xpack.Cloud.Qcloud'),
|
||||
attrs: ['access_key_id', 'access_key_secret']
|
||||
},
|
||||
[qcloud_lighthouse]: {
|
||||
name: qcloud_lighthouse,
|
||||
title: i18n.t('xpack.Cloud.QcloudLighthouse'),
|
||||
attrs: ['access_key_id', 'access_key_secret']
|
||||
},
|
||||
[azure]: {
|
||||
name: azure,
|
||||
title: i18n.t('xpack.Cloud.Azure'),
|
||||
@@ -63,6 +78,11 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
|
||||
title: i18n.t('xpack.Cloud.Azure_Int'),
|
||||
attrs: ['client_id', 'client_secret', 'tenant_id', 'subscription_id']
|
||||
},
|
||||
[gcp]: {
|
||||
name: gcp,
|
||||
title: i18n.t('xpack.Cloud.GCP'),
|
||||
attrs: ['service_account_key']
|
||||
},
|
||||
[vmware]: {
|
||||
name: vmware,
|
||||
title: 'VMware',
|
||||
@@ -88,14 +108,19 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
|
||||
title: i18n.t('xpack.Cloud.OpenStack'),
|
||||
attrs: ['auth_url', 'user_domain_name', 'username', 'password']
|
||||
},
|
||||
[gcp]: {
|
||||
name: gcp,
|
||||
title: i18n.t('xpack.Cloud.GCP'),
|
||||
attrs: ['service_account_key']
|
||||
},
|
||||
[fc]: {
|
||||
name: fc,
|
||||
title: i18n.t('xpack.Cloud.FC'),
|
||||
attrs: ['api_endpoint', 'username', 'password']
|
||||
},
|
||||
[ctyun_private]: {
|
||||
name: ctyun_private,
|
||||
title: i18n.t('xpack.Cloud.CTYunPrivate'),
|
||||
attrs: ['access_key_id', 'access_key_secret', 'api_endpoint', 'cert_file', 'key_file']
|
||||
},
|
||||
[lan]: {
|
||||
name: lan,
|
||||
title: i18n.t('xpack.Cloud.LAN'),
|
||||
attrs: ['ip_group', 'test_port', 'test_timeout', 'platform', 'hostname_prefix']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export default {
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name']],
|
||||
[this.$t('common.Correlation'), ['users', 'user_groups', 'assets', 'applications', 'system_users']],
|
||||
[this.$t('common.Correlation'), ['users', 'user_groups', 'nodes', 'assets', 'applications', 'system_users']],
|
||||
[this.$t('common.Other'), ['is_active', 'comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
@@ -36,6 +36,17 @@ export default {
|
||||
url: '/api/v1/users/groups/'
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
el: {
|
||||
value: [],
|
||||
ajax: {
|
||||
url: '/api/v1/assets/nodes/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.full_value, value: item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
assets: {
|
||||
type: 'assetSelect',
|
||||
component: AssetSelect,
|
||||
@@ -63,7 +74,7 @@ export default {
|
||||
el: {
|
||||
value: [],
|
||||
ajax: {
|
||||
url: `/api/v1/assets/system-users/?protocol__in=ssh,telnet,mysql,postgresql,mariadb,oracle,sqlserver,k8s`,
|
||||
url: `/api/v1/assets/system-users/?protocol__in=ssh,telnet,mysql,postgresql,mariadb,oracle,sqlserver,k8s,redis,mongodb,clickhouse`,
|
||||
transformOption: (item) => {
|
||||
if (this.$route.query.type === 'k8s') {
|
||||
return { label: item.name, value: item.id }
|
||||
|
||||
@@ -75,7 +75,7 @@ export default {
|
||||
icon: 'fa-info-circle',
|
||||
title: this.$t('assets.SystemUser'),
|
||||
objectsAjax: {
|
||||
url: `/api/v1/assets/system-users/?protocol__in=ssh,telnet,mysql,postgresql,mariadb,oracle,sqlserver,k8s`,
|
||||
url: `/api/v1/assets/system-users/?protocol__in=ssh,telnet,mysql,postgresql,mariadb,oracle,sqlserver,k8s,redis,mongodb,clickhouse`,
|
||||
transformOption: (item) => defaultTransformOption(item, 'username')
|
||||
},
|
||||
hasObjectsId: this.object.system_users,
|
||||
|
||||
@@ -80,11 +80,7 @@ export default {
|
||||
hasImport: false,
|
||||
hasRefresh: true,
|
||||
hasSearch: true,
|
||||
hasMoreActions: false,
|
||||
createRoute: 'CommandFilterCreate',
|
||||
canCreate: () => {
|
||||
return this.$hasPerm('assets.add_commandfilter')
|
||||
}
|
||||
createRoute: 'CommandFilterCreate'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -42,7 +42,6 @@ export default {
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasMoreActions: false,
|
||||
createRoute: 'DomainCreate'
|
||||
},
|
||||
notice: this.$t('assets.DomainHelpMessage')
|
||||
|
||||
@@ -27,7 +27,6 @@ export default {
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasMoreActions: false,
|
||||
createRoute: 'LabelCreate'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ export default {
|
||||
case 'sqlserver':
|
||||
case 'redis':
|
||||
case 'mongodb':
|
||||
case 'clickhouse':
|
||||
return Database
|
||||
case 'k8s':
|
||||
return K8S
|
||||
|
||||
@@ -93,7 +93,6 @@ export default {
|
||||
methods: {
|
||||
onGetObjectDone(obj) {
|
||||
this.fieldsMeta.private_key.el.fingerprint = obj.ssh_key_fingerprint
|
||||
console.log(obj.fingerprint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
vm.relationDialog.tableConfig.url = setUrlParam(vm.relationDialog.tableConfig.url, 'commandexecution', row.id)
|
||||
vm.relationDialog.show = true
|
||||
}
|
||||
return <el-link onClick={onClick}>{ cellValue.length }</el-link>
|
||||
return <el-link class='text-link' onClick={onClick}>{ cellValue.length }</el-link>
|
||||
}
|
||||
},
|
||||
command: {
|
||||
@@ -67,7 +67,7 @@ export default {
|
||||
formatter: (row) => {
|
||||
const label = this.$t('audits.View')
|
||||
const route = { to: { name: 'CeleryTaskLog', params: { id: row.id }}}
|
||||
return <router-link {...{ attrs: route }} target='_blank'>{ label }</router-link>
|
||||
return <router-link class='text-link' {...{ attrs: route }} target='_blank'>{ label }</router-link>
|
||||
}
|
||||
},
|
||||
date_start: {
|
||||
@@ -100,7 +100,7 @@ export default {
|
||||
options: [
|
||||
{
|
||||
label: this.$t('audits.Hosts'),
|
||||
value: 'asset__hostname'
|
||||
value: 'hostname_ip'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -138,5 +138,4 @@ export default {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,23 +1,48 @@
|
||||
<template>
|
||||
<GenericListPage :table-config="tableConfig" :header-actions="headerActions" />
|
||||
<div>
|
||||
<GenericListPage
|
||||
v-loading="loading"
|
||||
:table-config="tableConfig"
|
||||
:header-actions="headerActions"
|
||||
/>
|
||||
<el-dialog
|
||||
:title="this.$t('route.OperateLog')"
|
||||
:visible.sync="logDetailVisible"
|
||||
width="70%"
|
||||
>
|
||||
<TwoTabFormatter :row="rowObj" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GenericListPage from '@/layout/components/GenericListPage'
|
||||
import { getDaysAgo, getDaysFuture } from '@/utils/common'
|
||||
import TwoTabFormatter from '@/components/TableFormatters/TwoTabFormatter'
|
||||
import { ActionsFormatter } from '@/components/TableFormatters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericListPage
|
||||
GenericListPage,
|
||||
TwoTabFormatter
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
const now = new Date()
|
||||
const dateFrom = getDaysAgo(7, now).toISOString()
|
||||
const dateTo = getDaysFuture(1, now).toISOString()
|
||||
return {
|
||||
rowObj: {
|
||||
left: '',
|
||||
right: '',
|
||||
leftTitle: vm.$t('audits.BeforeChange'),
|
||||
rightTitle: vm.$t('audits.AfterChange')
|
||||
},
|
||||
logDetailVisible: false,
|
||||
loading: false,
|
||||
tableConfig: {
|
||||
url: '/api/v1/audits/operate-logs/',
|
||||
columns: ['user', 'action_display', 'resource_type_display', 'resource', 'remote_addr', 'datetime'],
|
||||
columns: ['user', 'action_display', 'resource_type_display', 'resource', 'remote_addr', 'datetime', 'actions'],
|
||||
columnsMeta: {
|
||||
user: {
|
||||
showOverflowTooltip: true
|
||||
@@ -36,7 +61,38 @@ export default {
|
||||
width: '140px'
|
||||
},
|
||||
action_display: {
|
||||
width: '90px'
|
||||
width: '70px'
|
||||
},
|
||||
actions: {
|
||||
width: '70px',
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: false,
|
||||
canUpdate: false,
|
||||
hasDelete: false,
|
||||
canDelete: false,
|
||||
hasClone: false,
|
||||
canClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
name: 'View',
|
||||
title: this.$t('common.View'),
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
vm.loading = true
|
||||
vm.$axios.get(
|
||||
`/api/v1/audits/operate-logs/${row.id}/?type=action_detail`,
|
||||
).then(res => {
|
||||
vm.rowObj.left = res.before
|
||||
vm.rowObj.right = res.after
|
||||
vm.logDetailVisible = true
|
||||
}).finally(() => {
|
||||
vm.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
extraQuery: {
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
<Page>
|
||||
<el-collapse-transition>
|
||||
<div style="display: flex;justify-items: center; flex-wrap: nowrap;justify-content:space-between;">
|
||||
<div v-show="iShowTree" :style="iShowTree?('width:250px;'):('width:0;')" class="transition-box">
|
||||
<div
|
||||
v-show="iShowTree"
|
||||
:style="{width: iShowTree?'20%': 0}"
|
||||
class="transition-box tree-box"
|
||||
>
|
||||
<AutoDataZTree
|
||||
ref="AutoDataZTree"
|
||||
:key="DataZTree"
|
||||
@@ -10,7 +14,7 @@
|
||||
class="auto-data-ztree"
|
||||
/>
|
||||
</div>
|
||||
<div :style="iShowTree?('display: flex;width: calc(100% - 250px);'):('display: flex;width:100%;')">
|
||||
<div :style="iShowTree?('display: flex;width: 80%;'):('display: flex;width:100%;')">
|
||||
<div class="mini">
|
||||
<div style="display:block" class="mini-button" @click="iShowTree=!iShowTree">
|
||||
<i v-show="iShowTree" class="fa fa-angle-left fa-x" /><i v-show="!iShowTree" class="fa fa-angle-right fa-x" />
|
||||
@@ -70,6 +74,8 @@ export default {
|
||||
treeUrl: '',
|
||||
showRefresh: true,
|
||||
showMenu: false,
|
||||
showSearch: true,
|
||||
customTreeHeader: true,
|
||||
check: {
|
||||
enable: true
|
||||
},
|
||||
@@ -276,4 +282,8 @@ export default {
|
||||
height: 100px;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.tree-box {
|
||||
margin-right: 2px;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import ListTable from '@/components/ListTable'
|
||||
import { ActionsFormatter } from '@/components/TableFormatters'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'AdhocExecutionHistory',
|
||||
@@ -86,6 +87,14 @@ export default {
|
||||
callback: function({ row, tableData }) {
|
||||
return this.$router.push({ name: 'HistoryExecutionDetail', params: { id: row.id }})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'log',
|
||||
title: this.$t('ops.output'),
|
||||
type: 'info',
|
||||
callback: function({ row }) {
|
||||
openTaskPage(row.id, 'ansible')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import DetailCard from '@/components/DetailCard'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import RunInfoCard from '../../RunInfoCard'
|
||||
import { toLastFailureDisplay, toLastSucessDisplay } from '../business'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'HistoryExecutionDetail',
|
||||
@@ -72,17 +71,6 @@ export default {
|
||||
{
|
||||
key: this.$t('ops.isSuccess'),
|
||||
value: this.object.is_success
|
||||
},
|
||||
{
|
||||
key: this.$t('ops.output'),
|
||||
value: this.object.id,
|
||||
formatter: function(row, value) {
|
||||
const onClick = function() {
|
||||
openTaskPage(value, 'ansible')
|
||||
}
|
||||
const title = this.$t('common.View')
|
||||
return <a onClick={onClick} >{ title }</a>
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export default {
|
||||
openTaskPage(value, 'ansible')
|
||||
}
|
||||
const title = this.$t('common.View')
|
||||
return <a onClick={onClick} >{ title }</a>
|
||||
return <a class='text-link' onClick={onClick} >{ title }</a>
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import ListTable from '@/components/ListTable'
|
||||
import { DetailFormatter } from '@/components/TableFormatters'
|
||||
import { toSafeLocalDateStr } from '@/utils/common'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
|
||||
export default {
|
||||
name: 'TaskHistory',
|
||||
@@ -95,6 +96,14 @@ export default {
|
||||
callback: function({ row, tableData }) {
|
||||
return this.$router.push({ name: 'HistoryExecutionDetail', params: { id: row.id }})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'log',
|
||||
title: this.$t('ops.output'),
|
||||
type: 'info',
|
||||
callback: function({ row }) {
|
||||
openTaskPage(row.id, 'ansible')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,19 +12,23 @@
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import { getDayFuture } from '@/utils/common'
|
||||
import PermissionFormActionField from '../components/PermissionFormActionField'
|
||||
import { remoteApplication } from '../const.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericCreateUpdatePage
|
||||
},
|
||||
data() {
|
||||
const queryCategory = this.$route.query.category
|
||||
const queryType = this.$route.query.type
|
||||
const urlSearchFields = `category=${queryCategory}&type=${queryType}`
|
||||
return {
|
||||
initial: {
|
||||
is_active: true,
|
||||
date_start: new Date().toISOString(),
|
||||
date_expired: getDayFuture(36500, new Date()).toISOString(),
|
||||
type: this.$route.query.type,
|
||||
category: this.$route.query.category
|
||||
type: queryType,
|
||||
category: queryCategory
|
||||
},
|
||||
fields: [
|
||||
[this.$t('common.Basic'), ['name']],
|
||||
@@ -33,7 +37,7 @@ export default {
|
||||
[this.$t('common.action'), ['actions']],
|
||||
[this.$t('common.Other'), ['is_active', 'date_start', 'date_expired', 'comment']]
|
||||
],
|
||||
url: `/api/v1/perms/application-permissions/?category=${this.$route.query.category}&type=${this.$route.query.type}`,
|
||||
url: `/api/v1/perms/application-permissions/?${urlSearchFields}`,
|
||||
createSuccessNextRoute: { name: 'ApplicationPermissionDetail' },
|
||||
fieldsMeta: {
|
||||
users: {
|
||||
@@ -66,7 +70,7 @@ export default {
|
||||
el: {
|
||||
value: [],
|
||||
ajax: {
|
||||
url: `/api/v1/applications/applications/?category=${this.$route.query.category}&type=${this.$route.query.type}`,
|
||||
url: `/api/v1/applications/applications/?${urlSearchFields}`,
|
||||
transformOption: (item) => {
|
||||
return { label: item.name + ' (' + item.type_display + ')', value: item.id }
|
||||
}
|
||||
@@ -79,7 +83,6 @@ export default {
|
||||
ajax: {
|
||||
url: (function() {
|
||||
let url = '/api/v1/assets/system-users/'
|
||||
const queryType = this.$route.query.type
|
||||
if (this.$route.query.category === 'remote_app') {
|
||||
url += `?protocol=rdp`
|
||||
} else if (queryType) {
|
||||
@@ -88,7 +91,7 @@ export default {
|
||||
return url
|
||||
}.bind(this)()),
|
||||
transformOption: (item) => {
|
||||
if (this.$route.query.type === 'k8s') {
|
||||
if (queryType === 'k8s') {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
const username = item.username || '*'
|
||||
@@ -106,7 +109,7 @@ export default {
|
||||
actions: {
|
||||
label: this.$t('perms.Actions'),
|
||||
component: PermissionFormActionField,
|
||||
helpText: this.$t('common.actionsTips')
|
||||
helpText: remoteApplication.includes(queryType) ? this.$t('common.actionsTips') : ''
|
||||
},
|
||||
is_active: {
|
||||
type: 'checkbox'
|
||||
|
||||
@@ -22,6 +22,10 @@ export default {
|
||||
choices: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
actions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -74,6 +78,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
choicesIDs() {
|
||||
if (this.actions.length !== 0) {
|
||||
return this.actions
|
||||
}
|
||||
if (this.choices.length === 0) {
|
||||
return [
|
||||
'all', 'connect', 'upload_file', 'download_file', 'updownload',
|
||||
@@ -83,9 +90,6 @@ export default {
|
||||
return this.choices.map((v) => v.value)
|
||||
},
|
||||
iData() {
|
||||
console.log('this.choicesIDs', this.choicesIDs)
|
||||
console.log('this.choices', this.choices)
|
||||
console.log('this.value', this.value)
|
||||
this.$log.debug('choices: ', this.choicesIDs)
|
||||
const fullTreeNodes = _.cloneDeep(this.fullChoicesTreeNodes)
|
||||
const treeNodes = this.trimChoicesTreeNodes(fullTreeNodes)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const remoteApplication = ['chrome', 'mysql_workbench', 'vmware_client', 'custom']
|
||||
|
||||
@@ -78,6 +78,7 @@ export default {
|
||||
},
|
||||
onPerformSuccess() {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
this.$store.dispatch('users/ifFirstLogin', false)
|
||||
setTimeout(() => this.$router.push({ name: 'ProfileInfo' }), 100)
|
||||
},
|
||||
submitMethod() {
|
||||
|
||||
@@ -160,7 +160,7 @@ export default {
|
||||
attrs: {
|
||||
disabled: true,
|
||||
name: 'site_msg',
|
||||
model: this.object.receive_backends.indexOf('site_msg') !== -1
|
||||
model: this.object?.receive_backends.indexOf('site_msg') !== -1
|
||||
},
|
||||
callbacks: {
|
||||
change: this.updateUserReceiveBackends
|
||||
@@ -171,7 +171,7 @@ export default {
|
||||
type: 'switcher',
|
||||
attrs: {
|
||||
name: 'email',
|
||||
model: this.object.receive_backends.indexOf('email') !== -1
|
||||
model: this.object?.receive_backends.indexOf('email') !== -1
|
||||
},
|
||||
callbacks: {
|
||||
change: this.updateUserReceiveBackends
|
||||
@@ -182,7 +182,7 @@ export default {
|
||||
type: 'switcher',
|
||||
attrs: {
|
||||
name: 'wecom',
|
||||
model: this.object.receive_backends.indexOf('wecom') !== -1
|
||||
model: this.object?.receive_backends.indexOf('wecom') !== -1
|
||||
},
|
||||
has: this.$store.getters.publicSettings.AUTH_WECOM,
|
||||
callbacks: {
|
||||
@@ -194,7 +194,7 @@ export default {
|
||||
type: 'switcher',
|
||||
attrs: {
|
||||
name: 'dingtalk',
|
||||
model: this.object.receive_backends.indexOf('dingtalk') !== -1
|
||||
model: this.object?.receive_backends.indexOf('dingtalk') !== -1
|
||||
},
|
||||
has: this.$store.getters.publicSettings.AUTH_DINGTALK,
|
||||
callbacks: {
|
||||
@@ -206,7 +206,7 @@ export default {
|
||||
type: 'switcher',
|
||||
attrs: {
|
||||
name: 'feishu',
|
||||
model: this.object.receive_backends.indexOf('feishu') !== -1
|
||||
model: this.object?.receive_backends.indexOf('feishu') !== -1
|
||||
},
|
||||
has: this.$store.getters.publicSettings.AUTH_FEISHU,
|
||||
callbacks: {
|
||||
@@ -306,6 +306,7 @@ export default {
|
||||
{ 'receive_backends': this.getReceiveBackendList() }
|
||||
).then(res => {
|
||||
this.$message.success(this.$t('common.updateSuccessMsg'))
|
||||
this.$store.dispatch('users/getProfile', true)
|
||||
}).catch(err => {
|
||||
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + err))
|
||||
})
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
<template>
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm
|
||||
:fields="fields"
|
||||
:fields-meta="fieldsMeta"
|
||||
:initial="object"
|
||||
:url="url"
|
||||
:submit-method="submitMethod"
|
||||
v-bind="$data"
|
||||
/>
|
||||
</IBox>
|
||||
</template>
|
||||
@@ -29,6 +26,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
url: `/api/v1/users/profile/`,
|
||||
hasDetailInMsg: false,
|
||||
fields: [
|
||||
[this.$t('users.Account'), ['username', 'name', 'email']],
|
||||
[this.$t('common.Other'), ['phone', 'wechat']]
|
||||
@@ -43,13 +41,11 @@ export default {
|
||||
email: {
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
submitMethod() {
|
||||
return 'patch'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submitMethod() {
|
||||
return 'patch'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<script>
|
||||
import BaseAuth from './Base'
|
||||
import { JsonRequired } from '@/components/DataForm/rules'
|
||||
import { JsonRequiredUserNameMapped } from '@/components/DataForm/rules'
|
||||
import { JsonEditor } from '@/components/FormFields'
|
||||
|
||||
export default {
|
||||
@@ -25,16 +25,14 @@ export default {
|
||||
'AUTH_CAS', 'CAS_SERVER_URL', 'CAS_ROOT_PROXIED_AS', 'CAS_VERSION'
|
||||
]],
|
||||
[this.$t('common.Other'), [
|
||||
'CAS_LOGOUT_COMPLETELY', 'CAS_USERNAME_ATTRIBUTE',
|
||||
'CAS_APPLY_ATTRIBUTES_TO_USER', 'CAS_RENAME_ATTRIBUTES',
|
||||
'CAS_CREATE_USER'
|
||||
'CAS_LOGOUT_COMPLETELY', 'CAS_RENAME_ATTRIBUTES', 'CAS_CREATE_USER'
|
||||
]]
|
||||
],
|
||||
fieldsMeta: {
|
||||
CAS_RENAME_ATTRIBUTES: {
|
||||
component: JsonEditor,
|
||||
label: this.$t('setting.authUserAttrMap'),
|
||||
rules: [JsonRequired]
|
||||
rules: [JsonRequiredUserNameMapped]
|
||||
}
|
||||
},
|
||||
submitMethod: () => 'patch',
|
||||
@@ -43,9 +41,18 @@ export default {
|
||||
return obj
|
||||
},
|
||||
cleanFormValue(data) {
|
||||
if (data['CAS_RENAME_ATTRIBUTES']) {
|
||||
data['CAS_RENAME_ATTRIBUTES'] = JSON.parse(data['CAS_RENAME_ATTRIBUTES'])
|
||||
let userNameAttribute = ''
|
||||
const renameAttributes = JSON.parse(data['CAS_RENAME_ATTRIBUTES'])
|
||||
if (renameAttributes) {
|
||||
data['CAS_RENAME_ATTRIBUTES'] = renameAttributes
|
||||
}
|
||||
for (const key in renameAttributes) {
|
||||
if (renameAttributes[key] === 'username') {
|
||||
userNameAttribute = key
|
||||
}
|
||||
}
|
||||
data['CAS_USERNAME_ATTRIBUTE'] = userNameAttribute
|
||||
data['CAS_APPLY_ATTRIBUTES_TO_USER'] = true
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
106
src/views/settings/Auth/OAuth2.vue
Normal file
106
src/views/settings/Auth/OAuth2.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<BaseAuth
|
||||
:config="settings"
|
||||
:title="$t('setting.OAuth2')"
|
||||
enable-field="AUTH_OAUTH2"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseAuth from './Base'
|
||||
import { JsonEditor } from '@/components/FormFields'
|
||||
import { JsonRequired } from '@/components/DataForm/rules'
|
||||
import { UploadField } from '@/components'
|
||||
import request from '@/utils/request'
|
||||
|
||||
export default {
|
||||
name: 'OAuth2',
|
||||
components: {
|
||||
BaseAuth
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
settings: {
|
||||
url: '/api/v1/settings/setting/?category=oauth2',
|
||||
fields: [
|
||||
[this.$t('common.Basic'), [
|
||||
'AUTH_OAUTH2',
|
||||
'AUTH_OAUTH2_PROVIDER',
|
||||
'AUTH_OAUTH2_LOGO_PATH',
|
||||
'AUTH_OAUTH2_CLIENT_ID', 'AUTH_OAUTH2_CLIENT_SECRET',
|
||||
'AUTH_OAUTH2_ACCESS_TOKEN_METHOD'
|
||||
]],
|
||||
[this.$t('common.Params'), [
|
||||
'AUTH_OAUTH2_SCOPE',
|
||||
'AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT',
|
||||
'AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT',
|
||||
'AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT',
|
||||
'AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT'
|
||||
]],
|
||||
[this.$t('common.Other'), [
|
||||
'AUTH_OAUTH2_LOGOUT_COMPLETELY',
|
||||
'AUTH_OAUTH2_ALWAYS_UPDATE_USER',
|
||||
'AUTH_OAUTH2_USER_ATTR_MAP'
|
||||
]]
|
||||
],
|
||||
fieldsMeta: {
|
||||
AUTH_OAUTH2_LOGO_PATH: {
|
||||
component: UploadField,
|
||||
el: {
|
||||
width: '5%',
|
||||
height: '5%',
|
||||
tip: this.$t('setting.OAuth2LogoTip')
|
||||
},
|
||||
on: {
|
||||
fileChange: ([value], updateForm) => {
|
||||
vm.updateOAuth2Logo(value)
|
||||
}
|
||||
}
|
||||
},
|
||||
AUTH_OAUTH2_USER_ATTR_MAP: {
|
||||
component: JsonEditor,
|
||||
label: this.$t('setting.authUserAttrMap'),
|
||||
rules: [JsonRequired],
|
||||
helpText: this.$t('setting.authUserAttrMapHelpText')
|
||||
},
|
||||
AUTH_OAUTH2_ACCESS_TOKEN_METHOD: {
|
||||
label: this.$t('setting.tokenHTTPMethod')
|
||||
}
|
||||
},
|
||||
submitMethod: () => 'patch',
|
||||
afterGetFormValue(obj) {
|
||||
obj.AUTH_OAUTH2_USER_ATTR_MAP = JSON.stringify(obj.AUTH_OAUTH2_USER_ATTR_MAP)
|
||||
return obj
|
||||
},
|
||||
cleanFormValue(data) {
|
||||
delete data['AUTH_OAUTH2_LOGO_PATH']
|
||||
if (data['AUTH_OAUTH2_USER_ATTR_MAP']) {
|
||||
data['AUTH_OAUTH2_USER_ATTR_MAP'] = JSON.parse(data['AUTH_OAUTH2_USER_ATTR_MAP'])
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateOAuth2Logo(file) {
|
||||
const formData = new FormData()
|
||||
formData.append('AUTH_OAUTH2_LOGO_PATH', file)
|
||||
return request({
|
||||
url: '/api/v1/settings/setting/?category=oauth2',
|
||||
method: 'patch',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: formData
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<BaseAuth
|
||||
:value="value"
|
||||
:config="settings"
|
||||
:title="$t('setting.SAML2')"
|
||||
enable-field="AUTH_SAML2"
|
||||
@@ -19,12 +18,6 @@ export default {
|
||||
components: {
|
||||
BaseAuth
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
settings: {
|
||||
@@ -45,9 +38,6 @@ export default {
|
||||
]]
|
||||
],
|
||||
fieldsMeta: {
|
||||
AUTH_SAML2: {
|
||||
label: this.$t('setting.enableSAML2Auth')
|
||||
},
|
||||
SAML2_IDP_METADATA_URL: {
|
||||
component: 'el-input',
|
||||
label: this.$t('setting.authSAML2MetadataUrl'),
|
||||
|
||||
@@ -19,6 +19,7 @@ import FeiShu from './FeiShu'
|
||||
import WeCom from './WeCom'
|
||||
import SSO from './SSO'
|
||||
import SAML2 from './SAML2'
|
||||
import OAuth2 from './OAuth2'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -33,7 +34,8 @@ export default {
|
||||
FeiShu,
|
||||
Radius,
|
||||
SSO,
|
||||
SAML2
|
||||
SAML2,
|
||||
OAuth2
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -60,6 +62,10 @@ export default {
|
||||
title: this.$t('setting.SAML2'),
|
||||
name: 'SAML2'
|
||||
},
|
||||
{
|
||||
title: this.$t('setting.OAuth2'),
|
||||
name: 'OAuth2'
|
||||
},
|
||||
{
|
||||
title: this.$t('setting.WeCom'),
|
||||
name: 'WeCom'
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
import ListTable from '@/components/ListTable'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import { importLdapUser, refreshLdapUserCache, startLdapUserCache } from '@/api/settings'
|
||||
import { getErrorResponseMsg } from '@/utils/common'
|
||||
|
||||
export default {
|
||||
name: 'ImportDialog',
|
||||
@@ -103,7 +104,12 @@ export default {
|
||||
importLdapUser(data).then(res => {
|
||||
this.$message.success(res.msg)
|
||||
// eslint-disable-next-line no-return-assign
|
||||
}).finally(() => this.dialogLdapUserImportLoginStatus = false)
|
||||
}).catch(error => {
|
||||
const errorMessage = getErrorResponseMsg(error) || this.$t('common.imExport.ImportFail')
|
||||
this.$message.error(errorMessage)
|
||||
}).finally(() => {
|
||||
this.dialogLdapUserImportLoginStatus = false
|
||||
})
|
||||
}
|
||||
},
|
||||
importAllUserClick() {
|
||||
|
||||
@@ -60,6 +60,7 @@ export default {
|
||||
actions: {
|
||||
prop: 'id',
|
||||
formatterArgs: {
|
||||
canUpdate: this.$hasPerm('orgs.change_organization'),
|
||||
canDelete: function({ row }) {
|
||||
return !row.is_default && vm.$hasPerm('orgs.delete_organization')
|
||||
},
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<Page>
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm v-bind="$data" />
|
||||
<GenericCreateUpdateForm
|
||||
v-bind="$data"
|
||||
@submitSuccess="onSubmitSuccess"
|
||||
/>
|
||||
</IBox>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -43,7 +46,9 @@ export default {
|
||||
[
|
||||
this.$t('setting.Perm'),
|
||||
[
|
||||
'PERM_SINGLE_ASSET_TO_UNGROUP_NODE'
|
||||
'PERM_SINGLE_ASSET_TO_UNGROUP_NODE',
|
||||
'TICKET_AUTHORIZE_DEFAULT_TIME',
|
||||
'TICKET_AUTHORIZE_DEFAULT_TIME_UNIT'
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -81,6 +86,9 @@ export default {
|
||||
methods: {
|
||||
hasValidLicense() {
|
||||
return this.$store.getters.hasValidLicense
|
||||
},
|
||||
onSubmitSuccess(res) {
|
||||
this.$store.state.settings.publicSettings.TICKET_AUTHORIZE_DEFAULT_TIME = res.TICKET_AUTHORIZE_DEFAULT_TIME
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
v-on="$listeners"
|
||||
@confirm="onConfirm()"
|
||||
>
|
||||
<GenericCreateUpdateForm v-bind="iConfig" @submitSuccess="submitSuccess" />
|
||||
<GenericCreateUpdateForm ref="form" v-bind="iConfig" @submitSuccess="submitSuccess" />
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
@@ -52,6 +52,16 @@ export default {
|
||||
submitSuccess(res) {
|
||||
this.$emit('input', !!res[this.enableField])
|
||||
this.visible = false
|
||||
},
|
||||
testPerformError(error) {
|
||||
const data = error.response.data
|
||||
for (const key of Object.keys(data)) {
|
||||
let value = data[key]
|
||||
if (value instanceof Array) {
|
||||
value = value.join(';')
|
||||
}
|
||||
this.$refs.form.$refs.form.setFieldError(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
72
src/views/settings/SMS/CMPP2.vue
Normal file
72
src/views/settings/SMS/CMPP2.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<BaseSMS ref="baseSms" :title="$t('setting.CMPP2')" :config="$data" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseSMS from './Base'
|
||||
import { UpdateToken } from '@/components/FormFields'
|
||||
|
||||
export default {
|
||||
name: 'CMPP2',
|
||||
components: {
|
||||
BaseSMS
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
url: `/api/v1/settings/setting/?category=cmpp2`,
|
||||
hasDetailInMsg: false,
|
||||
visible: false,
|
||||
moreButtons: [
|
||||
{
|
||||
title: this.$t('common.Test'),
|
||||
loading: false,
|
||||
callback: function(value, form, btn) {
|
||||
btn.loading = true
|
||||
vm.$axios.post(
|
||||
`/api/v1/settings/sms/cmpp2/testing/`,
|
||||
value
|
||||
).then(res => {
|
||||
vm.$message.success(res['msg'])
|
||||
}).catch((error) => {
|
||||
vm.$log.error('err occur')
|
||||
vm.$refs.baseSms.testPerformError(error)
|
||||
}).finally(() => { btn.loading = false })
|
||||
}
|
||||
}
|
||||
],
|
||||
fields: [
|
||||
[
|
||||
this.$t('common.BasicInfo'),
|
||||
[
|
||||
'CMPP2_HOST', 'CMPP2_PORT', 'CMPP2_SP_ID', 'CMPP2_SP_SECRET', 'CMPP2_SRC_ID', 'CMPP2_SERVICE_ID',
|
||||
'CMPP2_VERIFY_SIGN_NAME', 'CMPP2_VERIFY_TEMPLATE_CODE'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('common.Other'),
|
||||
[
|
||||
'SMS_TEST_PHONE'
|
||||
]
|
||||
]
|
||||
],
|
||||
fieldsMeta: {
|
||||
CMPP2_SP_SECRET: {
|
||||
component: UpdateToken
|
||||
}
|
||||
},
|
||||
submitMethod() {
|
||||
return 'patch'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<BaseSMS :title="$t('setting.AlibabaCloud')" :config="$data" />
|
||||
<BaseSMS ref="baseSms" :title="$t('setting.AlibabaCloud')" :config="$data" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -24,12 +24,13 @@ export default {
|
||||
callback: function(value, form, btn) {
|
||||
btn.loading = true
|
||||
vm.$axios.post(
|
||||
`/api/v1/settings/alibaba/testing/`,
|
||||
`/api/v1/settings/sms/alibaba/testing/`,
|
||||
value
|
||||
).then(res => {
|
||||
vm.$message.success(res['msg'])
|
||||
}).catch(() => {
|
||||
}).catch((error) => {
|
||||
vm.$log.error('err occur')
|
||||
vm.$refs.baseSms.testPerformError(error)
|
||||
}).finally(() => { btn.loading = false })
|
||||
}
|
||||
}
|
||||
|
||||
88
src/views/settings/SMS/SMSHuawei.vue
Normal file
88
src/views/settings/SMS/SMSHuawei.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<BaseSMS ref="baseSms" :title="$t('setting.HuaweiCloud')" :config="$data" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseSMS from './Base'
|
||||
import { UpdateToken } from '@/components/FormFields'
|
||||
|
||||
export default {
|
||||
name: 'SMSHuawei',
|
||||
components: {
|
||||
BaseSMS
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
return {
|
||||
url: `/api/v1/settings/setting/?category=huawei`,
|
||||
hasDetailInMsg: false,
|
||||
visible: false,
|
||||
moreButtons: [
|
||||
{
|
||||
title: this.$t('common.Test'),
|
||||
loading: false,
|
||||
callback: function(value, form, btn) {
|
||||
btn.loading = true
|
||||
vm.$axios.post(
|
||||
`/api/v1/settings/sms/huawei/testing/`,
|
||||
value
|
||||
).then(res => {
|
||||
vm.$message.success(res['msg'])
|
||||
}).catch((error) => {
|
||||
vm.$log.error('err occur')
|
||||
vm.$refs.baseSms.testPerformError(error)
|
||||
}).finally(() => { btn.loading = false })
|
||||
}
|
||||
}
|
||||
],
|
||||
fields: [
|
||||
[
|
||||
this.$t('common.BasicInfo'),
|
||||
[
|
||||
'HUAWEI_APP_KEY', 'HUAWEI_APP_SECRET', 'HUAWEI_SMS_ENDPOINT'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('setting.VerifySignTmpl'),
|
||||
[
|
||||
'HUAWEI_SIGN_CHANNEL_NUM', 'HUAWEI_VERIFY_SIGN_NAME', 'HUAWEI_VERIFY_TEMPLATE_CODE'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('common.Other'),
|
||||
[
|
||||
'SMS_TEST_PHONE'
|
||||
]
|
||||
]
|
||||
],
|
||||
fieldsMeta: {
|
||||
HUAWEI_VERIFY_SIGN_TMPL: {
|
||||
fields: ['SIGN_NAME', 'TEMPLATE_CODE'],
|
||||
fieldsMeta: {
|
||||
}
|
||||
},
|
||||
HUAWEI_APP_SECRET: {
|
||||
component: UpdateToken
|
||||
},
|
||||
HUAWEI_SIGN_CHANNEL_NUM: {
|
||||
label: this.$t('setting.SignChannelNum')
|
||||
},
|
||||
HUAWEI_SMS_ENDPOINT: {
|
||||
label: this.$t('setting.AppEndpoint')
|
||||
}
|
||||
},
|
||||
submitMethod() {
|
||||
return 'put'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<BaseSMS :title="$t('setting.TencentCloud')" :config="$data" />
|
||||
<BaseSMS ref="baseSms" :title="$t('setting.TencentCloud')" :config="$data" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -24,12 +24,13 @@ export default {
|
||||
callback: function(value, form, btn) {
|
||||
btn.loading = true
|
||||
vm.$axios.post(
|
||||
`/api/v1/settings/tencent/testing/`,
|
||||
`/api/v1/settings/sms/tencent/testing/`,
|
||||
value
|
||||
).then(res => {
|
||||
vm.$message.success(res['msg'])
|
||||
}).catch(() => {
|
||||
}).catch((error) => {
|
||||
vm.$log.error('err occur')
|
||||
vm.$refs.baseSms.testPerformError(error)
|
||||
}).finally(() => { btn.loading = false })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
||||
import SMSAlibaba from './SMSAlibaba'
|
||||
import SMSTencent from './SMSTencent'
|
||||
import SMSHuawei from './SMSHuawei'
|
||||
import CMPP2 from './CMPP2'
|
||||
|
||||
export default {
|
||||
name: 'Auth',
|
||||
@@ -23,7 +25,7 @@ export default {
|
||||
],
|
||||
[
|
||||
this.$t('setting.SMSProvider'), [
|
||||
'ALIYUN', 'QCLOUD'
|
||||
'ALIYUN', 'QCLOUD', 'HUAWEICLOUD', 'CMPP2'
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -41,6 +43,20 @@ export default {
|
||||
hidden: (form) => {
|
||||
return form['SMS_BACKEND'] !== 'tencent'
|
||||
}
|
||||
},
|
||||
HUAWEICLOUD: {
|
||||
label: this.$t('setting.HuaweiCloud'),
|
||||
component: SMSHuawei,
|
||||
hidden: (form) => {
|
||||
return form['SMS_BACKEND'] !== 'huawei'
|
||||
}
|
||||
},
|
||||
CMPP2: {
|
||||
label: this.$t('setting.CMPP2'),
|
||||
component: CMPP2,
|
||||
hidden: (form) => {
|
||||
return form['SMS_BACKEND'] !== 'cmpp2'
|
||||
}
|
||||
}
|
||||
},
|
||||
submitMethod() {
|
||||
|
||||
@@ -37,6 +37,12 @@ export default {
|
||||
[
|
||||
'TERMINAL_MAGNUS_ENABLED'
|
||||
]
|
||||
],
|
||||
[
|
||||
`Web ${comp}(Luna)`,
|
||||
[
|
||||
'TERMINAL_GRAPHICAL_RESOLUTION'
|
||||
]
|
||||
]
|
||||
],
|
||||
fieldsMeta: {
|
||||
|
||||
@@ -23,13 +23,15 @@ export default {
|
||||
this.$t('applications.port'),
|
||||
[
|
||||
'http_port', 'https_port', 'ssh_port', 'rdp_port',
|
||||
'mysql_port', 'mariadb_port', 'postgresql_port', 'redis_port',
|
||||
'oracle_11g_port', 'oracle_12c_port'
|
||||
'magnus_listen_port_range'
|
||||
]
|
||||
],
|
||||
[this.$t('common.Other'), ['comment']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
magnus_listen_port_range: {
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
hasDetailInMsg: false
|
||||
}
|
||||
|
||||
@@ -20,17 +20,15 @@ export default {
|
||||
url: '/api/v1/terminal/endpoints/',
|
||||
columns: [
|
||||
'name', 'host',
|
||||
'http_port', 'https_port', 'ssh_port',
|
||||
'rdp_port', 'mysql_port', 'mariadb_port',
|
||||
'postgresql_port', 'redis_port',
|
||||
'oracle_11g_port', 'oracle_12c_port',
|
||||
'http_port', 'https_port', 'ssh_port', 'rdp_port',
|
||||
'magnus_listen_port_range',
|
||||
'date_created', 'comment', 'actions'
|
||||
],
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'host', 'actions',
|
||||
'http_port', 'https_port', 'ssh_port', 'rdp_port'
|
||||
'http_port', 'https_port', 'ssh_port', 'rdp_port', 'magnus_listen_port_range'
|
||||
]
|
||||
},
|
||||
columnsMeta: {
|
||||
@@ -39,9 +37,10 @@ export default {
|
||||
},
|
||||
actions: {
|
||||
formatterArgs: {
|
||||
canUpdate: this.$hasPerm('terminal.change_endpoint'),
|
||||
updateRoute: 'EndpointUpdate',
|
||||
cloneRoute: 'EndpointCreate',
|
||||
canDelete: ({ row }) => row.id !== '00000000-0000-0000-0000-000000000001'
|
||||
canDelete: ({ row }) => row.id !== '00000000-0000-0000-0000-000000000001' && this.$hasPerm('terminal.delete_endpoint')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ export default {
|
||||
},
|
||||
actions: {
|
||||
formatterArgs: {
|
||||
canUpdate: this.$hasPerm('terminal.change_endpointrule'),
|
||||
updateRoute: 'EndpointRuleUpdate',
|
||||
cloneRoute: 'EndpointRuleCreate'
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ export default {
|
||||
initial: {
|
||||
type: storageType,
|
||||
endpoint_suffix: 'core.chinacloudapi.cn',
|
||||
protocol: 'http'
|
||||
protocol: 'http',
|
||||
is_default: true
|
||||
},
|
||||
getUrl() {
|
||||
const params = this.$route.params
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user