Compare commits
24 Commits
v4.1
...
pr@dev@per
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a69398ea3f | ||
|
|
9b7c4ed353 | ||
|
|
41f841532f | ||
|
|
0b9f47dd84 | ||
|
|
d32a376e8c | ||
|
|
0f8a8845df | ||
|
|
8bb2c66b99 | ||
|
|
b3d0be2f60 | ||
|
|
5a94ddd976 | ||
|
|
7b9627a80b | ||
|
|
5fcfecc060 | ||
|
|
f4d7a2283c | ||
|
|
40bb8410d3 | ||
|
|
45344ac620 | ||
|
|
5b894c9667 | ||
|
|
d89bd15b6d | ||
|
|
5e640dd45c | ||
|
|
09aa750794 | ||
|
|
744b215800 | ||
|
|
f4e11da053 | ||
|
|
2e6b5706d5 | ||
|
|
440a5b27ef | ||
|
|
932e16844e | ||
|
|
aff2e439dd |
13
.github/workflows/build-base-image.yml
vendored
@@ -15,8 +15,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Lock Pull Request
|
||||
run: |
|
||||
curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-d '{"state":"pending", "description":"Action running, merge disabled", "context":"Lock PR"}' \
|
||||
"https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }}"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
@@ -59,3 +64,9 @@ jobs:
|
||||
git push
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Unlock Pull Request
|
||||
run: |
|
||||
curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-d '{"state":"success", "description":"Action running, merge disabled", "context":"Lock PR"}' \
|
||||
"https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }}"
|
||||
|
||||
2
.github/workflows/release-drafter.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
tag: ${{ steps.get_version.outputs.TAG }}
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16.20'
|
||||
node-version: '20.15'
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
- name: Build web
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"autoprefixer": "^9.5.1",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-eslint": "10.0.2",
|
||||
"babel-jest": "23.6.0",
|
||||
"chalk": "2.4.2",
|
||||
"compression-webpack-plugin": "^6.1.1",
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 961 B After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 916 B After Width: | Height: | Size: 2.4 KiB |
BIN
src/assets/img/icons/h3c.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 940 B |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -37,6 +37,10 @@ export default {
|
||||
type: String,
|
||||
default: '/api/v1/assets/assets/'
|
||||
},
|
||||
defaultPageSize: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
baseNodeUrl: {
|
||||
type: String,
|
||||
default: '/api/v1/assets/nodes/'
|
||||
@@ -70,6 +74,7 @@ export default {
|
||||
value: iValue,
|
||||
multiple: true,
|
||||
clearable: true,
|
||||
defaultPageSize: this.defaultPageSize,
|
||||
ajax: {
|
||||
url: this.baseUrl,
|
||||
transformOption: (item) => {
|
||||
|
||||
@@ -197,6 +197,24 @@ export class FormFieldGenerator {
|
||||
return field
|
||||
}
|
||||
|
||||
setChoicesTips(field, fieldMeta, fieldRemoteMeta) {
|
||||
// 设置 checkbox 的 tips
|
||||
if (['checkbox-group', 'radio-group'].indexOf(field.type) !== -1) {
|
||||
field.options.map(option => {
|
||||
if (!option.tip && field.tips) {
|
||||
option.tip = field.tips[option.value]
|
||||
}
|
||||
if (!option.tip) {
|
||||
const match = option.label.match(/^(.+?)\s*\((.*?)\)$/)
|
||||
if (match) {
|
||||
option.label = match[1]
|
||||
option.tip = match[2]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
afterGenerateField(field) {
|
||||
field.label = toSentenceCase(field.label)
|
||||
|
||||
@@ -204,15 +222,7 @@ export class FormFieldGenerator {
|
||||
field.el.placeholder = field.placeholder
|
||||
}
|
||||
|
||||
// 设置 checkbox 的 tips
|
||||
if (field.tips && ['checkbox-group', 'radio-group'].indexOf(field.type) !== -1) {
|
||||
field.options.map(option => {
|
||||
if (!option.tip && field.tips[option.value]) {
|
||||
option.tip = field.tips[option.value]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.setChoicesTips(field)
|
||||
return field
|
||||
}
|
||||
|
||||
|
||||
@@ -125,16 +125,19 @@ export default {
|
||||
allowCreate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultPageSize: {
|
||||
type: Number,
|
||||
default: 10
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const vm = this
|
||||
const defaultPageSize = 10
|
||||
const defaultParams = {
|
||||
search: '',
|
||||
page: 1,
|
||||
hasMore: true,
|
||||
pageSize: defaultPageSize
|
||||
pageSize: vm.defaultPageSize
|
||||
}
|
||||
// 设置axios全局报错提示不显示
|
||||
const validateStatus = (status) => {
|
||||
@@ -194,7 +197,6 @@ export default {
|
||||
}
|
||||
},
|
||||
iAjax() {
|
||||
const defaultPageSize = 10
|
||||
const defaultMakeParams = (params) => {
|
||||
const page = params.page || 1
|
||||
const offset = (page - 1) * params.pageSize
|
||||
@@ -237,7 +239,7 @@ export default {
|
||||
}
|
||||
const defaultAjax = {
|
||||
url: '',
|
||||
pageSize: defaultPageSize,
|
||||
pageSize: this.defaultPageSize,
|
||||
makeParams: defaultMakeParams,
|
||||
transformOption: defaultTransformOption,
|
||||
processResults: defaultProcessResults,
|
||||
|
||||
@@ -44,8 +44,9 @@
|
||||
import DataTable from '@/components/Table/DataTable/index.vue'
|
||||
import { getUpdateObjURL } from '@/utils/common'
|
||||
import { sleep } from '@/utils/time'
|
||||
import { EditableInputFormatter, StatusFormatter } from '@/components/Table/TableFormatters'
|
||||
import { EditableInputFormatter } from '@/components/Table/TableFormatters'
|
||||
import { encryptPassword } from '@/utils/crypto'
|
||||
import getStatusColumnMeta from '@/components/Table/ListTable/TableAction/const'
|
||||
|
||||
export default {
|
||||
name: 'ImportTable',
|
||||
@@ -223,38 +224,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
generateTableColumns(tableTitles, tableData) {
|
||||
const vm = this
|
||||
const columns = [{
|
||||
prop: '@status',
|
||||
label: vm.$t('Status'),
|
||||
width: '80px',
|
||||
align: 'center',
|
||||
formatter: StatusFormatter,
|
||||
formatterArgs: {
|
||||
faChoices: {
|
||||
ok: 'fa-check text-primary',
|
||||
error: 'fa-times text-danger',
|
||||
pending: 'fa-clock-o'
|
||||
},
|
||||
getChoicesKey(val) {
|
||||
if (val === 'ok' || val === 'pending') {
|
||||
return val
|
||||
}
|
||||
return 'error'
|
||||
},
|
||||
getTip(val) {
|
||||
if (val === 'ok') {
|
||||
return vm.$t('Success')
|
||||
} else if (val === 'pending') {
|
||||
return vm.$t('Pending')
|
||||
} else if (val && val.name === 'error') {
|
||||
return val.error
|
||||
}
|
||||
return ''
|
||||
},
|
||||
hasTips: true
|
||||
}
|
||||
}]
|
||||
const columns = [{ ...getStatusColumnMeta.bind(this)().status }]
|
||||
for (const item of tableTitles) {
|
||||
const dataItemLens = tableData.map(d => {
|
||||
if (!d) {
|
||||
|
||||
40
src/components/Table/ListTable/TableAction/const.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import { StatusFormatter } from '@/components/Table/TableFormatters'
|
||||
import i18n from '@/i18n/i18n'
|
||||
|
||||
export const getStatusColumnMeta = (prop = '@status') => {
|
||||
return {
|
||||
status: {
|
||||
prop: prop,
|
||||
label: i18n.t('Status'),
|
||||
width: '80px',
|
||||
align: 'center',
|
||||
formatter: StatusFormatter,
|
||||
formatterArgs: {
|
||||
faChoices: {
|
||||
ok: 'fa-check text-primary',
|
||||
error: 'fa-times text-danger',
|
||||
pending: 'fa-clock-o'
|
||||
},
|
||||
getChoicesKey: (val) => {
|
||||
if (val === 'ok' || val === 'pending') {
|
||||
return val
|
||||
}
|
||||
return 'error'
|
||||
},
|
||||
getTip: (val) => {
|
||||
if (val === 'ok') {
|
||||
return i18n.t('Success')
|
||||
} else if (val === 'pending') {
|
||||
return i18n.t('Pending')
|
||||
} else if ((val && val.name === 'error') || val.error !== undefined) {
|
||||
return val.error
|
||||
}
|
||||
return ''
|
||||
},
|
||||
hasTips: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default getStatusColumnMeta
|
||||
@@ -189,22 +189,23 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.urlUpdated[this.tableUrl] = location.href
|
||||
this.$set(this.urlUpdated, this.tableUrl, location.href)
|
||||
},
|
||||
deactivated() {
|
||||
this.isDeactivated = true
|
||||
},
|
||||
activated() {
|
||||
this.isDeactivated = false
|
||||
const preURL = this.urlUpdated[this.tableUrl]
|
||||
if (!preURL || preURL === location.href) {
|
||||
return
|
||||
}
|
||||
this.urlUpdated[this.tableUrl] = location.href
|
||||
this.$log.debug('Reload the table get latest data: pre ', preURL, ' current: ', location.href)
|
||||
setTimeout(() => {
|
||||
this.$nextTick(() => {
|
||||
this.isDeactivated = false
|
||||
const cleanUrl = this.tableUrl.split('?')[0]
|
||||
const preURL = this.urlUpdated[cleanUrl]
|
||||
|
||||
if (!preURL || preURL === location.href) return
|
||||
|
||||
this.$set(this.urlUpdated, this.tableUrl, location.href)
|
||||
this.$log.debug('Reload the table get latest data: pre ', preURL, ' current: ', location.href)
|
||||
this.reloadTable()
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
handleActionInitialDone() {
|
||||
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
showRenameBtn: false,
|
||||
drag: {
|
||||
isCopy: false,
|
||||
isMove: true
|
||||
isMove: !this.$store.getters.currentOrgIsRoot
|
||||
}
|
||||
},
|
||||
callback: {
|
||||
|
||||
@@ -35,13 +35,29 @@ export default {
|
||||
]),
|
||||
announcement() {
|
||||
const ann = this.publicSettings.ANNOUNCEMENT
|
||||
return { id: ann['ID'], subject: ann['SUBJECT'], content: ann['CONTENT'], link: ann['LINK'] }
|
||||
return {
|
||||
id: ann['ID'],
|
||||
subject: ann['SUBJECT'],
|
||||
content: ann['CONTENT'],
|
||||
link: ann['LINK'],
|
||||
date_start: ann['DATE_START'],
|
||||
date_end: ann['DATE_END']
|
||||
}
|
||||
},
|
||||
enabled() {
|
||||
return this.publicSettings.ANNOUNCEMENT_ENABLED && (this.announcement.content || this.announcement.subject)
|
||||
return this.publicSettings.ANNOUNCEMENT_ENABLED && (this.announcement.content || this.announcement.subject) && this.isDateValid
|
||||
},
|
||||
title() {
|
||||
return this.$t('Announcement') + ': ' + this.announcement.subject
|
||||
},
|
||||
isDateValid() {
|
||||
if (this.announcement.date_start === undefined || this.announcement.date_end === undefined) {
|
||||
return true
|
||||
}
|
||||
const now = new Date()
|
||||
const start = new Date(this.announcement.date_start)
|
||||
const end = new Date(this.announcement.date_end)
|
||||
return now >= start && now <= end
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -33,11 +33,13 @@ export default {
|
||||
query[k] = v
|
||||
}
|
||||
|
||||
let key
|
||||
if (this.$route.name.toLowerCase().includes('list')) {
|
||||
return _.trimEnd(this.$route.path, '/') + '?' + new URLSearchParams(query).toString()
|
||||
key = _.trimEnd(this.$route.path, '/') + '?' + new URLSearchParams(query).toString()
|
||||
} else {
|
||||
return new Date().getTime()
|
||||
key = new Date().getTime()
|
||||
}
|
||||
return key
|
||||
},
|
||||
chatAiEnabled() {
|
||||
return this.publicSettings?.CHAT_AI_ENABLED
|
||||
|
||||
@@ -66,11 +66,15 @@ export default {
|
||||
break
|
||||
case 'logout':
|
||||
this.logout()
|
||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||
break
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
async logout() {
|
||||
const currentOrg = this.$store.getters.currentOrg
|
||||
if (currentOrg.autoEnter) {
|
||||
await this.$store.dispatch('users/setCurrentOrg', this.$store.getters.preOrg)
|
||||
}
|
||||
window.location.href = `${process.env.VUE_APP_LOGOUT_PATH}?next=${this.$route.fullPath}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<span class="msg-detail-time">{{ formatDate(currentMsg.date_created) }}</span>
|
||||
</div>
|
||||
<div class="msg-detail-txt">
|
||||
<span v-sanitize="currentMsg.content.message" />
|
||||
<MarkDown :value="currentMsg.content.message" />
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
@@ -80,10 +80,14 @@
|
||||
<script>
|
||||
import { toSafeLocalDateStr } from '@/utils/time'
|
||||
import Dialog from '@/components/Dialog'
|
||||
import MarkDown from '@/components/Widgets/MarkDown'
|
||||
|
||||
export default {
|
||||
name: 'SiteMessages',
|
||||
components: { Dialog },
|
||||
components: {
|
||||
Dialog,
|
||||
MarkDown
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<el-alert v-if="helpMessage" type="success">
|
||||
<span v-sanitize="helpMessage" class="announcement-main" />
|
||||
</el-alert>
|
||||
<transition v-if="!loading" appear mode="out-in" name="fade-transform">
|
||||
<transition appear mode="out-in" name="fade-transform">
|
||||
<slot>
|
||||
<keep-alive>
|
||||
<component :is="computeActiveComponent" />
|
||||
@@ -83,16 +83,18 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
toSentenceCase: toSentenceCase
|
||||
loading: false,
|
||||
toSentenceCase: toSentenceCase,
|
||||
activeTab: this.activeMenu
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iActiveMenu: {
|
||||
get() {
|
||||
return this.activeMenu
|
||||
return this.activeTab
|
||||
},
|
||||
set(item) {
|
||||
this.activeTab = item
|
||||
this.$emit('update:activeMenu', item)
|
||||
}
|
||||
},
|
||||
@@ -119,16 +121,13 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
const activeTab = to.query?.tab
|
||||
if (activeTab && this.iActiveMenu !== activeTab) {
|
||||
this.iActiveMenu = activeTab
|
||||
}
|
||||
// 好像没必要
|
||||
// const activeTab = to.query?.tab
|
||||
// if (activeTab && this.iActiveMenu !== activeTab) {
|
||||
// this.iActiveMenu = activeTab
|
||||
// }
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
this.iActiveMenu = this.getPropActiveTab()
|
||||
this.loading = false
|
||||
},
|
||||
created() {
|
||||
this.iActiveMenu = this.getPropActiveTab()
|
||||
this.loading = false
|
||||
@@ -136,15 +135,8 @@ export default {
|
||||
methods: {
|
||||
handleTabClick(tab) {
|
||||
this.$emit('tab-click', tab)
|
||||
this.$emit('update:activeMenu', tab.name)
|
||||
|
||||
this.iActiveMenu = tab.name
|
||||
this.$cookie.set(this.$route.path, tab.name, 1)
|
||||
|
||||
if (this.$router.currentRoute.query[this.$route.path]) {
|
||||
this.$router.push({
|
||||
query: { ...this.$route.query, [this.$route.path]: '' }
|
||||
})
|
||||
}
|
||||
},
|
||||
getPropActiveTab() {
|
||||
let activeTab = ''
|
||||
|
||||
@@ -24,6 +24,9 @@ async function checkLogin({ to, from, next }) {
|
||||
} catch (e) {
|
||||
Vue.$log.error(e)
|
||||
const status = e.response.status
|
||||
if (store.getters.currentOrg.autoEnter) {
|
||||
await store.dispatch('users/setCurrentOrg', store.getters.preOrg)
|
||||
}
|
||||
if (status === 401 || status === 403) {
|
||||
setTimeout(() => {
|
||||
window.location = process.env.VUE_APP_LOGIN_PATH
|
||||
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
}
|
||||
},
|
||||
reviewers: {
|
||||
hidden: (item) => !['review', 'warning'].includes(item.action),
|
||||
hidden: (item) => !['review', 'warning', 'notify_and_warn'].includes(item.action),
|
||||
rules: [rules.RequiredChange],
|
||||
el: {
|
||||
value: [],
|
||||
|
||||
@@ -25,17 +25,17 @@ export default {
|
||||
const platform = this.$route.query.type
|
||||
const baseFields = [[this.$t('Basic'), ['db_name']]]
|
||||
let tlsFields = ['use_ssl', 'ca_cert']
|
||||
switch (platform) {
|
||||
case 'redis':
|
||||
tlsFields = tlsFields.concat(['client_cert', 'client_key'])
|
||||
break
|
||||
case 'mysql':
|
||||
tlsFields = tlsFields.concat(['client_cert', 'client_key', 'allow_invalid_cert'])
|
||||
break
|
||||
case 'mongodb':
|
||||
tlsFields = tlsFields.concat(['client_key', 'allow_invalid_cert'])
|
||||
break
|
||||
const platformFieldsMap = {
|
||||
redis: ['client_cert', 'client_key'],
|
||||
postgresql: ['client_cert', 'client_key', 'allow_invalid_cert'],
|
||||
mysql: ['client_cert', 'client_key', 'allow_invalid_cert'],
|
||||
mongodb: ['client_key', 'allow_invalid_cert']
|
||||
}
|
||||
|
||||
if (platformFieldsMap[platform]) {
|
||||
tlsFields = tlsFields.concat(platformFieldsMap[platform])
|
||||
}
|
||||
|
||||
if (tlsFields.length > 2) {
|
||||
const secureField = [
|
||||
this.$t('Secure'), tlsFields, 2
|
||||
|
||||
@@ -22,17 +22,17 @@
|
||||
:key="platform.id"
|
||||
:span="6"
|
||||
>
|
||||
<el-card
|
||||
:style="{ borderLeftColor: randomBorderColor(index) }"
|
||||
class="platform-item"
|
||||
shadow="hover"
|
||||
@click.native="createAsset(platform)"
|
||||
>
|
||||
<img :src="loadImage(platform)" alt="icon" class="asset-icon">
|
||||
<el-tooltip :content="platform.name">
|
||||
<el-tooltip :content="platform.name" :open-delay="1000">
|
||||
<el-card
|
||||
:style="{ borderLeftColor: randomBorderColor(index) }"
|
||||
class="platform-item"
|
||||
shadow="hover"
|
||||
@click.native="createAsset(platform)"
|
||||
>
|
||||
<img :src="loadImage(platform)" alt="icon" class="asset-icon">
|
||||
<span class="platform-name">{{ platform.name }}</span>
|
||||
</el-tooltip>
|
||||
</el-card>
|
||||
</el-card>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
@@ -236,8 +236,8 @@ export default {
|
||||
}
|
||||
|
||||
.asset-icon {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
vertical-align: -0.2em;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
@@ -89,14 +89,15 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleTabClick(tab) {
|
||||
const query = _.cloneDeep(this.$route.query)
|
||||
const newQuery = {
|
||||
...query,
|
||||
tab: tab.name
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$router.replace({ query: newQuery })
|
||||
})
|
||||
// 这样不行,会闪
|
||||
// const query = _.cloneDeep(this.$route.query)
|
||||
// const newQuery = {
|
||||
// ...query,
|
||||
// tab: tab.name
|
||||
// }
|
||||
// this.$nextTick(() => {
|
||||
// this.$router.replace({ query: newQuery })
|
||||
// })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,10 @@ export default {
|
||||
BaseAssetCreateUpdate
|
||||
},
|
||||
data() {
|
||||
const platformType = this.$route.query.platform_type
|
||||
return {
|
||||
url: '/api/v1/assets/gateways/',
|
||||
updateInitial: async(initial) => {
|
||||
const url = `/api/v1/assets/platforms/?name=Gateway`
|
||||
const url = `/api/v1/assets/platforms/?name__startswith=Gateway`
|
||||
const platform = await this.$axios.get(url)
|
||||
initial.platform = parseInt(platform[0].id)
|
||||
initial.domain = this.$route.query.domain
|
||||
@@ -26,11 +25,11 @@ export default {
|
||||
disabled: true
|
||||
},
|
||||
platform: {
|
||||
disabled: true,
|
||||
helpText: this.$t('GatewayPlatformHelpText'),
|
||||
el: {
|
||||
multiple: false,
|
||||
ajax: {
|
||||
url: `/api/v1/assets/platforms/?type=${platformType}`,
|
||||
url: `/api/v1/assets/platforms/?name__startswith=Gateway`,
|
||||
transformOption: (item) => {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export default {
|
||||
width: '60%',
|
||||
tableConfig: {
|
||||
hasSelection: false,
|
||||
url: `/api/v1/ops/adhocs/`,
|
||||
url: `/api/v1/ops/adhocs/?only_mine=true`,
|
||||
columns: ['name', 'module', 'args', 'comment', 'actions'],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
|
||||
@@ -83,7 +83,7 @@ export default {
|
||||
multiple: false,
|
||||
value: [],
|
||||
ajax: {
|
||||
url: '/api/v1/ops/playbooks/',
|
||||
url: `/api/v1/ops/playbooks/?only_mine=true`,
|
||||
transformOption: (item) => {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
|
||||
@@ -11,12 +11,13 @@ export default {
|
||||
GenericListTable
|
||||
},
|
||||
data() {
|
||||
const currentUserID = this.$store.state.users.profile.id
|
||||
return {
|
||||
tableConfig: {
|
||||
url: '/api/v1/ops/adhocs/',
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'module', 'args', 'comment', 'date_created', 'actions']
|
||||
default: ['name', 'module', 'args', 'comment', 'scope', 'date_created', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
@@ -29,10 +30,14 @@ export default {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
canUpdate: this.$hasPerm('ops.change_adhoc'),
|
||||
canUpdate: ({ row }) => {
|
||||
return this.$hasPerm('ops.change_adhoc') && row.creator === currentUserID
|
||||
},
|
||||
updateRoute: 'AdhocUpdate',
|
||||
hasDelete: true,
|
||||
canDelete: this.$hasPerm('ops.delete_adhoc'),
|
||||
canDelete: ({ row }) => {
|
||||
return this.$hasPerm('ops.delete_adhoc') && row.creator === currentUserID
|
||||
},
|
||||
hasClone: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export default {
|
||||
return {
|
||||
url: '/api/v1/ops/adhocs/',
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name', 'module', 'args', 'comment']]
|
||||
[this.$t('Basic'), ['name', 'module', 'args', 'comment', 'scope']]
|
||||
],
|
||||
initial: {
|
||||
module: 'shell',
|
||||
|
||||
@@ -16,6 +16,7 @@ export default {
|
||||
GenericListTable
|
||||
},
|
||||
data() {
|
||||
const currentUserID = this.$store.state.users.profile.id
|
||||
return {
|
||||
createDialogVisible: false,
|
||||
uploadDialogVisible: false,
|
||||
@@ -23,7 +24,7 @@ export default {
|
||||
url: '/api/v1/ops/playbooks/',
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: ['name', 'comment', 'date_created', 'actions']
|
||||
default: ['name', 'comment', 'scope', 'date_created', 'actions']
|
||||
},
|
||||
columnsMeta: {
|
||||
name: {
|
||||
@@ -36,11 +37,15 @@ export default {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
canUpdate: this.$hasPerm('ops.change_playbook'),
|
||||
canUpdate: ({ row }) => {
|
||||
return this.$hasPerm('ops.change_playbook') && row.creator === currentUserID
|
||||
},
|
||||
updateRoute: 'PlaybookUpdate',
|
||||
hasDelete: true,
|
||||
canDelete: this.$hasPerm('ops.delete_playbook'),
|
||||
hasClone: false
|
||||
canDelete: ({ row }) => {
|
||||
return this.$hasPerm('ops.delete_playbook') && row.creator === currentUserID
|
||||
},
|
||||
hasClone: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
return {
|
||||
url: '/api/v1/ops/playbooks/',
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name', 'comment']]
|
||||
[this.$t('Basic'), ['name', 'comment', 'scope']]
|
||||
],
|
||||
createSuccessNextRoute: {
|
||||
name: 'Template'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<NewNodeDialog v-if="createDialogVisible" :visible.sync="createDialogVisible" @confirm="doCreate" />
|
||||
<TreeTable ref="TreeTable" :tree-setting="treeSetting">
|
||||
<template slot="rMenu">
|
||||
<template v-if="!disableEdit" slot="rMenu">
|
||||
<li id="m_create_file" class="rmenu" tabindex="-1" @click="onCreate('file')">
|
||||
{{ $tc('NewFile') }}
|
||||
</li>
|
||||
@@ -62,7 +62,9 @@ export default {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const disableEdit = this.object.creator !== this.$store.state.users.profile.id
|
||||
return {
|
||||
disableEdit: disableEdit,
|
||||
newNode: {},
|
||||
createDialogVisible: false,
|
||||
createType: 'directory',
|
||||
@@ -70,7 +72,8 @@ export default {
|
||||
closing: false,
|
||||
DataZTree: 0,
|
||||
cmOptions: {
|
||||
mode: 'yaml'
|
||||
mode: 'yaml',
|
||||
readOnly: disableEdit
|
||||
},
|
||||
toolbar: {
|
||||
left: {
|
||||
@@ -82,6 +85,7 @@ export default {
|
||||
el: {
|
||||
type: 'primary'
|
||||
},
|
||||
isVisible: disableEdit,
|
||||
callback: () => {
|
||||
this.onSave()
|
||||
}
|
||||
@@ -94,6 +98,7 @@ export default {
|
||||
el: {
|
||||
type: 'primary'
|
||||
},
|
||||
isVisible: disableEdit,
|
||||
callback: () => {
|
||||
this.onReset()
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ export default {
|
||||
}],
|
||||
el: {
|
||||
value: [],
|
||||
defaultPageSize: 300,
|
||||
baseUrl: '/api/v1/assets/assets/?fields_size=mini',
|
||||
treeSetting: {
|
||||
showSearch: false,
|
||||
showRefresh: false
|
||||
|
||||
103
src/views/settings/Auth/Ldap/HaSyncSettingDialog.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
:title="$tc('SyncSetting')"
|
||||
top="10%"
|
||||
v-bind="$attrs"
|
||||
width="50%"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<GenericCreateUpdateForm
|
||||
:has-detail-in-msg="false"
|
||||
v-bind="settings"
|
||||
@submitSuccess="onSuccess"
|
||||
/>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GenericCreateUpdateForm } from '@/layout/components'
|
||||
import { CronTab, Dialog } from '@/components'
|
||||
import Select2 from '@/components/Form/FormFields/Select2.vue'
|
||||
import { Required } from '@/components/Form/DataForm/rules'
|
||||
|
||||
export default {
|
||||
name: 'SyncSettingDialog',
|
||||
components: {
|
||||
GenericCreateUpdateForm,
|
||||
Dialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
settings: {
|
||||
visible: false,
|
||||
url: '/api/v1/settings/setting/?category=ldap_ha',
|
||||
fields: [
|
||||
'AUTH_LDAP_HA_SYNC_ORG_IDS', 'AUTH_LDAP_HA_SYNC_IS_PERIODIC', 'AUTH_LDAP_HA_SYNC_CRONTAB',
|
||||
'AUTH_LDAP_HA_SYNC_INTERVAL', 'AUTH_LDAP_HA_SYNC_RECEIVERS'
|
||||
],
|
||||
fieldsMeta: {
|
||||
AUTH_LDAP_HA_SYNC_ORG_IDS: {
|
||||
component: Select2,
|
||||
rules: [Required],
|
||||
el: {
|
||||
popperClass: 'sync-setting-org',
|
||||
multiple: true,
|
||||
ajax: {
|
||||
url: '/api/v1/orgs/orgs/',
|
||||
transformOption: (item) => {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
}
|
||||
},
|
||||
hidden: (formValue) => {
|
||||
return !this.$hasLicense()
|
||||
}
|
||||
},
|
||||
AUTH_LDAP_HA_SYNC_IS_PERIODIC: {
|
||||
type: 'switch'
|
||||
},
|
||||
AUTH_LDAP_HA_SYNC_CRONTAB: {
|
||||
component: CronTab,
|
||||
helpTip: this.$t('CrontabOfCreateUpdatePage')
|
||||
},
|
||||
AUTH_LDAP_HA_SYNC_INTERVAL: {
|
||||
helpText: this.$t('IntervalOfCreateUpdatePage')
|
||||
},
|
||||
AUTH_LDAP_HA_SYNC_RECEIVERS: {
|
||||
component: Select2,
|
||||
el: {
|
||||
value: [],
|
||||
multiple: true,
|
||||
ajax: {
|
||||
url: '/api/v1/users/users/?fields_size=mini&oid=ROOT',
|
||||
transformOption: (item) => {
|
||||
return { label: item.name + '(' + item.username + ')', value: item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
submitMethod: () => 'patch',
|
||||
cleanFormValue(value) {
|
||||
if (value['AUTH_LDAP_HA_SYNC_INTERVAL'] === '') {
|
||||
value['AUTH_LDAP_HA_SYNC_INTERVAL'] = null
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSuccess() {
|
||||
this.$emit('update:visible', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -49,6 +49,7 @@ import { DEFAULT_ORG_ID, SYSTEM_ORG_ID } from '@/utils/org'
|
||||
import ListTable from '@/components/Table/ListTable/index.vue'
|
||||
import Dialog from '@/components/Dialog/index.vue'
|
||||
import Select2 from '@/components/Form/FormFields/Select2.vue'
|
||||
import getStatusColumnMeta from '@/components/Table/ListTable/TableAction/const'
|
||||
|
||||
export default {
|
||||
name: 'ImportDialog',
|
||||
@@ -57,6 +58,12 @@ export default {
|
||||
Dialog,
|
||||
Select2
|
||||
},
|
||||
props: {
|
||||
category: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogLdapUserImportLoginStatus: false,
|
||||
@@ -75,9 +82,10 @@ export default {
|
||||
}
|
||||
},
|
||||
tableConfig: {
|
||||
url: '/api/v1/settings/ldap/users/',
|
||||
columns: ['username', 'name', 'email', 'groups', 'existing'],
|
||||
url: `/api/v1/settings/ldap/users/?category=${this.category}`,
|
||||
columns: ['status', 'username', 'name', 'email', 'groups', 'existing'],
|
||||
columnsMeta: {
|
||||
...getStatusColumnMeta.bind(this)('status'),
|
||||
username: {
|
||||
label: this.$t('Username'),
|
||||
width: '180px'
|
||||
@@ -189,7 +197,7 @@ export default {
|
||||
enableWS() {
|
||||
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||
const port = document.location.port ? ':' + document.location.port : ''
|
||||
const url = '/ws/ldap/'
|
||||
const url = `/ws/ldap/?category=${this.category}`
|
||||
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||
this.ws = new WebSocket(wsURL)
|
||||
},
|
||||
|
||||
151
src/views/settings/Auth/Ldap/Ldap.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm v-bind="$data" />
|
||||
<ImportDialog v-if="dialogLdapUserImport" :category="category" :visible.sync="dialogLdapUserImport" />
|
||||
<TestLoginDialog :visible.sync="dialogTest" :category="category" />
|
||||
<SyncSettingDialog :visible.sync="dialogSyncSetting" />
|
||||
</IBox>
|
||||
</template>
|
||||
<script>
|
||||
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm/index.vue'
|
||||
import ImportDialog from './ImportDialog.vue'
|
||||
import TestLoginDialog from './TestLoginDialog.vue'
|
||||
import SyncSettingDialog from './SyncSettingDialog.vue'
|
||||
import { IBox } from '@/components'
|
||||
import rules, { JsonRequired } from '@/components/Form/DataForm/rules'
|
||||
import { JsonEditor, UpdateToken } from '@/components/Form/FormFields'
|
||||
|
||||
export default {
|
||||
name: 'Ldap',
|
||||
components: {
|
||||
GenericCreateUpdateForm,
|
||||
IBox,
|
||||
ImportDialog,
|
||||
TestLoginDialog,
|
||||
SyncSettingDialog
|
||||
},
|
||||
data() {
|
||||
const category = 'ldap'
|
||||
return {
|
||||
category: category,
|
||||
url: `/api/v1/settings/setting/?category=${category}`,
|
||||
dialogTest: false,
|
||||
dialogLdapUserImport: false,
|
||||
dialogSyncSetting: false,
|
||||
encryptedFields: ['AUTH_LDAP_BIND_PASSWORD'],
|
||||
fields: [
|
||||
[
|
||||
this.$t('Basic'),
|
||||
[
|
||||
'AUTH_LDAP', 'AUTH_LDAP_SERVER_URI',
|
||||
'AUTH_LDAP_BIND_DN', 'AUTH_LDAP_BIND_PASSWORD'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('Search'),
|
||||
[
|
||||
'AUTH_LDAP_SEARCH_OU', 'AUTH_LDAP_SEARCH_FILTER',
|
||||
'AUTH_LDAP_USER_ATTR_MAP'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('Other'),
|
||||
[
|
||||
'AUTH_LDAP_CONNECT_TIMEOUT', 'AUTH_LDAP_SEARCH_PAGED_SIZE',
|
||||
'AUTH_LDAP_CACHE_TIMEOUT'
|
||||
]
|
||||
]
|
||||
],
|
||||
fieldsMeta: {
|
||||
AUTH_LDAP_BIND_DN: {
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
AUTH_LDAP_BIND_PASSWORD: {
|
||||
component: UpdateToken
|
||||
},
|
||||
AUTH_LDAP_SEARCH_OU: {
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
AUTH_LDAP_USER_ATTR_MAP: {
|
||||
component: JsonEditor,
|
||||
rules: [JsonRequired]
|
||||
}
|
||||
},
|
||||
hasDetailInMsg: false,
|
||||
moreButtons: [
|
||||
{
|
||||
title: this.$t('LdapConnectTest'),
|
||||
loading: false,
|
||||
callback: function(value, form, btn) {
|
||||
if (value['AUTH_LDAP_BIND_PASSWORD'] === undefined) {
|
||||
value['AUTH_LDAP_BIND_PASSWORD'] = ''
|
||||
}
|
||||
btn.loading = true
|
||||
this.enableWS()
|
||||
this.ws.onopen = (e) => {
|
||||
this.ws.send(JSON.stringify({ msg_type: 'testing_config', ...value }))
|
||||
}
|
||||
this.ws.onmessage = (e) => {
|
||||
const data = JSON.parse(e.data)
|
||||
if (data.ok) {
|
||||
this.$message.success(data.msg)
|
||||
} else {
|
||||
this.$message.error(data.msg)
|
||||
}
|
||||
btn.loading = false
|
||||
}
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('LdapLoginTest'),
|
||||
callback: function(value, form) {
|
||||
this.dialogTest = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('LdapBulkImport'),
|
||||
callback: function(value, form) {
|
||||
this.dialogLdapUserImport = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('SyncSetting'),
|
||||
callback: function(value, form) {
|
||||
this.dialogSyncSetting = true
|
||||
}.bind(this)
|
||||
}
|
||||
],
|
||||
submitMethod: () => 'patch',
|
||||
afterGetFormValue(obj) {
|
||||
return obj
|
||||
},
|
||||
cleanFormValue(data) {
|
||||
if (data['AUTH_LDAP_BIND_PASSWORD'] === '') {
|
||||
delete data['AUTH_LDAP_BIND_PASSWORD']
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
enableWS() {
|
||||
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||
const port = document.location.port ? ':' + document.location.port : ''
|
||||
const url = '/ws/ldap/'
|
||||
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||
this.ws = new WebSocket(wsURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.listTable ::v-deep .table-action-right-side {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
151
src/views/settings/Auth/Ldap/LdapHA.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm v-bind="$data" />
|
||||
<ImportDialog v-if="dialogLdapUserImport" :category="category" :visible.sync="dialogLdapUserImport" />
|
||||
<TestLoginDialog :visible.sync="dialogTest" :category="category" />
|
||||
<SyncSettingDialog :visible.sync="dialogSyncSetting" />
|
||||
</IBox>
|
||||
</template>
|
||||
<script>
|
||||
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm/index.vue'
|
||||
import ImportDialog from './ImportDialog.vue'
|
||||
import TestLoginDialog from './TestLoginDialog.vue'
|
||||
import SyncSettingDialog from './HaSyncSettingDialog.vue'
|
||||
import { IBox } from '@/components'
|
||||
import rules, { JsonRequired } from '@/components/Form/DataForm/rules'
|
||||
import { JsonEditor, UpdateToken } from '@/components/Form/FormFields'
|
||||
|
||||
export default {
|
||||
name: 'LdapHA',
|
||||
components: {
|
||||
GenericCreateUpdateForm,
|
||||
IBox,
|
||||
ImportDialog,
|
||||
TestLoginDialog,
|
||||
SyncSettingDialog
|
||||
},
|
||||
data() {
|
||||
const category = 'ldap_ha'
|
||||
return {
|
||||
category: category,
|
||||
url: `/api/v1/settings/setting/?category=${category}`,
|
||||
dialogTest: false,
|
||||
dialogLdapUserImport: false,
|
||||
dialogSyncSetting: false,
|
||||
encryptedFields: ['AUTH_LDAP_HA_BIND_PASSWORD'],
|
||||
fields: [
|
||||
[
|
||||
this.$t('Basic'),
|
||||
[
|
||||
'AUTH_LDAP_HA', 'AUTH_LDAP_HA_SERVER_URI',
|
||||
'AUTH_LDAP_HA_BIND_DN', 'AUTH_LDAP_HA_BIND_PASSWORD'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('Search'),
|
||||
[
|
||||
'AUTH_LDAP_HA_SEARCH_OU', 'AUTH_LDAP_HA_SEARCH_FILTER',
|
||||
'AUTH_LDAP_HA_USER_ATTR_MAP'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('Other'),
|
||||
[
|
||||
'AUTH_LDAP_HA_CONNECT_TIMEOUT', 'AUTH_LDAP_HA_SEARCH_PAGED_SIZE',
|
||||
'AUTH_LDAP_HA_CACHE_TIMEOUT'
|
||||
]
|
||||
]
|
||||
],
|
||||
fieldsMeta: {
|
||||
AUTH_LDAP_HA_BIND_DN: {
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
AUTH_LDAP_HA_BIND_PASSWORD: {
|
||||
component: UpdateToken
|
||||
},
|
||||
AUTH_LDAP_HA_SEARCH_OU: {
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
AUTH_LDAP_HA_USER_ATTR_MAP: {
|
||||
component: JsonEditor,
|
||||
rules: [JsonRequired]
|
||||
}
|
||||
},
|
||||
hasDetailInMsg: false,
|
||||
moreButtons: [
|
||||
{
|
||||
title: this.$t('LdapConnectTest'),
|
||||
loading: false,
|
||||
callback: function(value, form, btn) {
|
||||
if (value['AUTH_LDAP_HA_BIND_PASSWORD'] === undefined) {
|
||||
value['AUTH_LDAP_HA_BIND_PASSWORD'] = ''
|
||||
}
|
||||
btn.loading = true
|
||||
this.enableWS()
|
||||
this.ws.onopen = (e) => {
|
||||
this.ws.send(JSON.stringify({ msg_type: 'testing_config', ...value }))
|
||||
}
|
||||
this.ws.onmessage = (e) => {
|
||||
const data = JSON.parse(e.data)
|
||||
if (data.ok) {
|
||||
this.$message.success(data.msg)
|
||||
} else {
|
||||
this.$message.error(data.msg)
|
||||
}
|
||||
btn.loading = false
|
||||
}
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('LdapLoginTest'),
|
||||
callback: function(value, form) {
|
||||
this.dialogTest = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('LdapBulkImport'),
|
||||
callback: function(value, form) {
|
||||
this.dialogLdapUserImport = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('SyncSetting'),
|
||||
callback: function(value, form) {
|
||||
this.dialogSyncSetting = true
|
||||
}.bind(this)
|
||||
}
|
||||
],
|
||||
submitMethod: () => 'patch',
|
||||
afterGetFormValue(obj) {
|
||||
return obj
|
||||
},
|
||||
cleanFormValue(data) {
|
||||
if (data['AUTH_LDAP_HA_BIND_PASSWORD'] === '') {
|
||||
delete data['AUTH_LDAP_HA_BIND_PASSWORD']
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
enableWS() {
|
||||
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||
const port = document.location.port ? ':' + document.location.port : ''
|
||||
const url = `/ws/ldap/?category=${this.category}`
|
||||
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||
this.ws = new WebSocket(wsURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.listTable ::v-deep .table-action-right-side {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -40,6 +40,12 @@ export default {
|
||||
components: {
|
||||
Dialog
|
||||
},
|
||||
props: {
|
||||
category: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
testLdapLoginStatus: false,
|
||||
@@ -69,7 +75,7 @@ export default {
|
||||
enableWS() {
|
||||
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||
const port = document.location.port ? ':' + document.location.port : ''
|
||||
const url = '/ws/ldap/'
|
||||
const url = `/ws/ldap/?category=${this.category}`
|
||||
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||
this.ws = new WebSocket(wsURL)
|
||||
}
|
||||
|
||||
@@ -1,152 +1 @@
|
||||
<template>
|
||||
<IBox>
|
||||
<GenericCreateUpdateForm v-bind="$data" />
|
||||
<ImportDialog v-if="dialogLdapUserImport" :visible.sync="dialogLdapUserImport" />
|
||||
<TestLoginDialog :visible.sync="dialogTest" />
|
||||
<SyncSettingDialog :visible.sync="dialogSyncSetting" />
|
||||
</IBox>
|
||||
</template>
|
||||
<script>
|
||||
import GenericCreateUpdateForm from '@/layout/components/GenericCreateUpdateForm/index.vue'
|
||||
import ImportDialog from './ImportDialog.vue'
|
||||
import TestLoginDialog from './TestLoginDialog.vue'
|
||||
import SyncSettingDialog from './SyncSettingDialog.vue'
|
||||
import { IBox } from '@/components'
|
||||
import rules, { JsonRequired } from '@/components/Form/DataForm/rules'
|
||||
import { JsonEditor, UpdateToken } from '@/components/Form/FormFields'
|
||||
|
||||
export default {
|
||||
name: 'Ldap',
|
||||
components: {
|
||||
GenericCreateUpdateForm,
|
||||
IBox,
|
||||
ImportDialog,
|
||||
TestLoginDialog,
|
||||
SyncSettingDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogTest: false,
|
||||
dialogLdapUserImport: false,
|
||||
dialogSyncSetting: false,
|
||||
encryptedFields: ['AUTH_LDAP_BIND_PASSWORD'],
|
||||
fields: [
|
||||
[
|
||||
this.$t('Basic'),
|
||||
[
|
||||
'AUTH_LDAP', 'AUTH_LDAP_SERVER_URI',
|
||||
'AUTH_LDAP_BIND_DN', 'AUTH_LDAP_BIND_PASSWORD'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('Search'),
|
||||
[
|
||||
'AUTH_LDAP_SEARCH_OU', 'AUTH_LDAP_SEARCH_FILTER',
|
||||
'AUTH_LDAP_USER_ATTR_MAP'
|
||||
]
|
||||
],
|
||||
[
|
||||
this.$t('Other'),
|
||||
[
|
||||
'AUTH_LDAP_CONNECT_TIMEOUT', 'AUTH_LDAP_SEARCH_PAGED_SIZE',
|
||||
'AUTH_LDAP_CACHE_TIMEOUT'
|
||||
]
|
||||
]
|
||||
],
|
||||
fieldsMeta: {
|
||||
AUTH_LDAP_BIND_DN: {
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
AUTH_LDAP_BIND_PASSWORD: {
|
||||
component: UpdateToken
|
||||
},
|
||||
AUTH_LDAP_SEARCH_OU: {
|
||||
rules: [
|
||||
rules.Required
|
||||
]
|
||||
},
|
||||
AUTH_LDAP_USER_ATTR_MAP: {
|
||||
component: JsonEditor,
|
||||
rules: [JsonRequired]
|
||||
}
|
||||
},
|
||||
url: '/api/v1/settings/setting/?category=ldap',
|
||||
hasDetailInMsg: false,
|
||||
moreButtons: [
|
||||
{
|
||||
title: this.$t('LdapConnectTest'),
|
||||
loading: false,
|
||||
callback: function(value, form, btn) {
|
||||
if (value['AUTH_LDAP_BIND_PASSWORD'] === undefined) {
|
||||
value['AUTH_LDAP_BIND_PASSWORD'] = ''
|
||||
}
|
||||
btn.loading = true
|
||||
this.enableWS()
|
||||
this.ws.onopen = (e) => {
|
||||
this.ws.send(JSON.stringify({ msg_type: 'testing_config', ...value }))
|
||||
}
|
||||
this.ws.onmessage = (e) => {
|
||||
const data = JSON.parse(e.data)
|
||||
if (data.ok) {
|
||||
this.$message.success(data.msg)
|
||||
} else {
|
||||
this.$message.error(data.msg)
|
||||
}
|
||||
btn.loading = false
|
||||
}
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('LdapLoginTest'),
|
||||
callback: function(value, form) {
|
||||
this.dialogTest = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('LdapBulkImport'),
|
||||
callback: function(value, form) {
|
||||
this.dialogLdapUserImport = true
|
||||
}.bind(this)
|
||||
},
|
||||
{
|
||||
title: this.$t('SyncSetting'),
|
||||
callback: function(value, form) {
|
||||
this.dialogSyncSetting = true
|
||||
}.bind(this)
|
||||
}
|
||||
],
|
||||
submitMethod: () => 'patch',
|
||||
afterGetFormValue(obj) {
|
||||
return obj
|
||||
},
|
||||
cleanFormValue(data) {
|
||||
if (data['AUTH_LDAP_BIND_PASSWORD'] === '') {
|
||||
delete data['AUTH_LDAP_BIND_PASSWORD']
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// this.loading = false
|
||||
},
|
||||
methods: {
|
||||
enableWS() {
|
||||
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||
const port = document.location.port ? ':' + document.location.port : ''
|
||||
const url = '/ws/ldap/'
|
||||
const wsURL = scheme + '://' + document.location.hostname + port + url
|
||||
this.ws = new WebSocket(wsURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.listTable ::v-deep .table-action-right-side {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
<script>
|
||||
import TabPage from '@/layout/components/TabPage'
|
||||
import LDAP from './Ldap'
|
||||
import LdapHA from './Ldap/LdapHA.vue'
|
||||
import LDAP from './Ldap/Ldap.vue'
|
||||
import Base from './Base'
|
||||
import Basic from './Basic'
|
||||
import CAS from './CAS'
|
||||
@@ -28,6 +29,7 @@ export default {
|
||||
components: {
|
||||
TabPage,
|
||||
LDAP,
|
||||
LdapHA,
|
||||
Base,
|
||||
Basic,
|
||||
CAS,
|
||||
@@ -45,6 +47,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
let extraBackends = []
|
||||
let ldapHABackends = []
|
||||
if (this.$store.getters.hasValidLicense) {
|
||||
extraBackends = [
|
||||
{
|
||||
@@ -93,6 +96,13 @@ export default {
|
||||
key: 'AUTH_RADIUS'
|
||||
}
|
||||
]
|
||||
ldapHABackends = [
|
||||
{
|
||||
title: this.$t('LDAP HA'),
|
||||
name: 'LdapHA',
|
||||
key: 'AUTH_LDAP_HA'
|
||||
}
|
||||
]
|
||||
}
|
||||
return {
|
||||
loading: true,
|
||||
@@ -107,6 +117,7 @@ export default {
|
||||
name: 'LDAP',
|
||||
key: 'AUTH_LDAP'
|
||||
},
|
||||
...ldapHABackends,
|
||||
{
|
||||
title: this.$t('CAS'),
|
||||
name: 'CAS',
|
||||
|
||||
@@ -33,7 +33,7 @@ export default {
|
||||
fieldsMeta: {
|
||||
ANNOUNCEMENT: {
|
||||
fields: [
|
||||
'SUBJECT', 'CONTENT', 'LINK'
|
||||
'SUBJECT', 'CONTENT', 'DATE_START', 'DATE_END', 'LINK'
|
||||
],
|
||||
fieldsMeta: {
|
||||
CONTENT: {
|
||||
|
||||
@@ -32,7 +32,8 @@ export default {
|
||||
this.$t('Database'),
|
||||
[
|
||||
'JOB_EXECUTION_KEEP_DAYS',
|
||||
'CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS'
|
||||
'CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS',
|
||||
'ACCOUNT_CHANGE_SECRET_RECORD_KEEP_DAYS'
|
||||
]
|
||||
]
|
||||
],
|
||||
|
||||
@@ -30,11 +30,11 @@ export default {
|
||||
detailCardItems() {
|
||||
return [
|
||||
{
|
||||
key: this.$t('Name'),
|
||||
key: this.$t('TaskPath'),
|
||||
value: this.object.name
|
||||
},
|
||||
{
|
||||
key: this.$t('Comment'),
|
||||
key: this.$t('Name'),
|
||||
value: this.object.meta.comment
|
||||
},
|
||||
{
|
||||
@@ -44,6 +44,10 @@ export default {
|
||||
{
|
||||
key: this.$t('LastPublishedTime'),
|
||||
value: this.object.last_published_time
|
||||
},
|
||||
{
|
||||
key: this.$t('Description'),
|
||||
value: this.object.meta.description
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ export default {
|
||||
role: { name: '', comment: '', users: [] },
|
||||
config: {
|
||||
titlePrefix: scope === 'org' ? vm.$t('OrgRole') : vm.$t('SystemRole'),
|
||||
getObjectName: (obj) => { return obj.display_name },
|
||||
url: `/api/v1/rbac/${scope}-roles`,
|
||||
activeMenu: 'RoleInfo',
|
||||
actions: {
|
||||
|
||||