diff --git a/.github/workflows/build-base-image.yml b/.github/workflows/build-base-image.yml
index 28a2e35d3f..3f8dd31e6e 100644
--- a/.github/workflows/build-base-image.yml
+++ b/.github/workflows/build-base-image.yml
@@ -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 }}"
diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml
index 33addda888..2017994103 100644
--- a/.github/workflows/release-drafter.yml
+++ b/.github/workflows/release-drafter.yml
@@ -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
diff --git a/src/assets/img/icons/cisco.png b/src/assets/img/icons/cisco.png
index 9ce6827e99..9162ae4fff 100644
Binary files a/src/assets/img/icons/cisco.png and b/src/assets/img/icons/cisco.png differ
diff --git a/src/assets/img/icons/clickhouse.png b/src/assets/img/icons/clickhouse.png
index cf4a0a5b67..2f4604218b 100644
Binary files a/src/assets/img/icons/clickhouse.png and b/src/assets/img/icons/clickhouse.png differ
diff --git a/src/assets/img/icons/db2.png b/src/assets/img/icons/db2.png
index c7c8bcda75..9c916f0b69 100644
Binary files a/src/assets/img/icons/db2.png and b/src/assets/img/icons/db2.png differ
diff --git a/src/assets/img/icons/gateway.png b/src/assets/img/icons/gateway.png
index 2ca2415270..e492fc3296 100644
Binary files a/src/assets/img/icons/gateway.png and b/src/assets/img/icons/gateway.png differ
diff --git a/src/assets/img/icons/general.png b/src/assets/img/icons/general.png
index f6dc155684..3760e80753 100644
Binary files a/src/assets/img/icons/general.png and b/src/assets/img/icons/general.png differ
diff --git a/src/assets/img/icons/h3c.png b/src/assets/img/icons/h3c.png
new file mode 100644
index 0000000000..a76041f811
Binary files /dev/null and b/src/assets/img/icons/h3c.png differ
diff --git a/src/assets/img/icons/linux.png b/src/assets/img/icons/linux.png
index 0967cbe1ba..1e21304038 100644
Binary files a/src/assets/img/icons/linux.png and b/src/assets/img/icons/linux.png differ
diff --git a/src/assets/img/icons/mariadb.png b/src/assets/img/icons/mariadb.png
index cca0b5941a..93dcdd007c 100644
Binary files a/src/assets/img/icons/mariadb.png and b/src/assets/img/icons/mariadb.png differ
diff --git a/src/assets/img/icons/mysql.png b/src/assets/img/icons/mysql.png
index 982c7edfc6..b50287503d 100644
Binary files a/src/assets/img/icons/mysql.png and b/src/assets/img/icons/mysql.png differ
diff --git a/src/assets/img/icons/oracle.png b/src/assets/img/icons/oracle.png
index 003041b33f..27c45e55ff 100644
Binary files a/src/assets/img/icons/oracle.png and b/src/assets/img/icons/oracle.png differ
diff --git a/src/assets/img/icons/other.png b/src/assets/img/icons/other.png
index 312678ad2c..85a4db8fbb 100644
Binary files a/src/assets/img/icons/other.png and b/src/assets/img/icons/other.png differ
diff --git a/src/assets/img/icons/private.png b/src/assets/img/icons/private.png
index ecb0cc1e7b..25a61f3458 100644
Binary files a/src/assets/img/icons/private.png and b/src/assets/img/icons/private.png differ
diff --git a/src/assets/img/icons/unix.png b/src/assets/img/icons/unix.png
index 30fbcb4c1a..1846f26e81 100644
Binary files a/src/assets/img/icons/unix.png and b/src/assets/img/icons/unix.png differ
diff --git a/src/assets/img/icons/vmware.png b/src/assets/img/icons/vmware.png
index 61da1658cd..50f0dbfbc1 100644
Binary files a/src/assets/img/icons/vmware.png and b/src/assets/img/icons/vmware.png differ
diff --git a/src/assets/img/icons/windows.png b/src/assets/img/icons/windows.png
index d34b295eaf..9f48418566 100644
Binary files a/src/assets/img/icons/windows.png and b/src/assets/img/icons/windows.png differ
diff --git a/src/components/Apps/AssetSelect/index.vue b/src/components/Apps/AssetSelect/index.vue
index 9a418f5f99..aaad143916 100644
--- a/src/components/Apps/AssetSelect/index.vue
+++ b/src/components/Apps/AssetSelect/index.vue
@@ -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) => {
diff --git a/src/components/Form/AutoDataForm/utils.js b/src/components/Form/AutoDataForm/utils.js
index 5976e87515..55deadb1b0 100644
--- a/src/components/Form/AutoDataForm/utils.js
+++ b/src/components/Form/AutoDataForm/utils.js
@@ -197,22 +197,32 @@ export class FormFieldGenerator {
return field
}
- afterGenerateField(field) {
- field.label = toSentenceCase(field.label)
-
- if (field.placeholder) {
- field.el.placeholder = field.placeholder
- }
-
+ setChoicesTips(field, fieldMeta, fieldRemoteMeta) {
// 设置 checkbox 的 tips
- if (field.tips && ['checkbox-group', 'radio-group'].indexOf(field.type) !== -1) {
+ if (['checkbox-group', 'radio-group'].indexOf(field.type) !== -1) {
field.options.map(option => {
- if (!option.tip && field.tips[option.value]) {
+ 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)
+
+ if (field.placeholder) {
+ field.el.placeholder = field.placeholder
+ }
+ this.setChoicesTips(field)
return field
}
diff --git a/src/components/Form/FormFields/InputWithUnit.vue b/src/components/Form/FormFields/InputWithUnit.vue
index fc8c205cf9..bcd4568a2a 100644
--- a/src/components/Form/FormFields/InputWithUnit.vue
+++ b/src/components/Form/FormFields/InputWithUnit.vue
@@ -1,5 +1,5 @@
-
+
{{ iUnit }}
@@ -15,6 +15,7 @@ export default {
},
data() {
return {
+ defaultValue: 24,
displayMapper: {
'second': this.$t('Second'), // 'sec' is the default value of 'unit
'min': this.$t('Minute'), // 'min' is the default value of 'unit
@@ -29,6 +30,9 @@ export default {
computed: {
iUnit() {
return this.displayMapper[this.unit] || this.unit
+ },
+ iValue() {
+ return this.$attrs.value ? this.$attrs.value : this.defaultValue
}
}
}
diff --git a/src/components/Form/FormFields/Select2.vue b/src/components/Form/FormFields/Select2.vue
index a4487a6dcc..f65a2315ae 100644
--- a/src/components/Form/FormFields/Select2.vue
+++ b/src/components/Form/FormFields/Select2.vue
@@ -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,
diff --git a/src/components/Table/ListTable/TableAction/ImportTable.vue b/src/components/Table/ListTable/TableAction/ImportTable.vue
index 1133330e3c..ea1e06ca63 100644
--- a/src/components/Table/ListTable/TableAction/ImportTable.vue
+++ b/src/components/Table/ListTable/TableAction/ImportTable.vue
@@ -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) {
diff --git a/src/components/Table/ListTable/TableAction/const.js b/src/components/Table/ListTable/TableAction/const.js
new file mode 100644
index 0000000000..2b326c3e77
--- /dev/null
+++ b/src/components/Table/ListTable/TableAction/const.js
@@ -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
diff --git a/src/components/Table/ListTable/index.vue b/src/components/Table/ListTable/index.vue
index 26981f8d6a..4d963c4b59 100644
--- a/src/components/Table/ListTable/index.vue
+++ b/src/components/Table/ListTable/index.vue
@@ -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() {
diff --git a/src/components/Tree/DataZTree/index.vue b/src/components/Tree/DataZTree/index.vue
index b56d48c24a..acb94117cd 100644
--- a/src/components/Tree/DataZTree/index.vue
+++ b/src/components/Tree/DataZTree/index.vue
@@ -39,7 +39,7 @@ export default {
showRenameBtn: false,
drag: {
isCopy: false,
- isMove: true
+ isMove: !this.$store.getters.currentOrgIsRoot
}
},
callback: {
diff --git a/src/components/Widgets/Announcement/index.vue b/src/components/Widgets/Announcement/index.vue
index f5bce77041..3ac0e5a7fd 100644
--- a/src/components/Widgets/Announcement/index.vue
+++ b/src/components/Widgets/Announcement/index.vue
@@ -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: {
diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue
index 048b886413..2d8e95cf87 100644
--- a/src/layout/components/AppMain.vue
+++ b/src/layout/components/AppMain.vue
@@ -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
diff --git a/src/layout/components/NavHeader/AccountDropdown.vue b/src/layout/components/NavHeader/AccountDropdown.vue
index a69c810732..9f924d7143 100644
--- a/src/layout/components/NavHeader/AccountDropdown.vue
+++ b/src/layout/components/NavHeader/AccountDropdown.vue
@@ -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}`
}
}
}
diff --git a/src/layout/components/NavHeader/SiteMessages.vue b/src/layout/components/NavHeader/SiteMessages.vue
index 7c644fa5a5..626051ff7d 100644
--- a/src/layout/components/NavHeader/SiteMessages.vue
+++ b/src/layout/components/NavHeader/SiteMessages.vue
@@ -70,7 +70,7 @@
{{ formatDate(currentMsg.date_created) }}
-
+
@@ -80,10 +80,14 @@
+
+
diff --git a/src/views/settings/Auth/Ldap/ImportDialog.vue b/src/views/settings/Auth/Ldap/ImportDialog.vue
index be3ba4b2f2..c60c5a31fa 100644
--- a/src/views/settings/Auth/Ldap/ImportDialog.vue
+++ b/src/views/settings/Auth/Ldap/ImportDialog.vue
@@ -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)
},
diff --git a/src/views/settings/Auth/Ldap/Ldap.vue b/src/views/settings/Auth/Ldap/Ldap.vue
new file mode 100644
index 0000000000..8ac47a93f2
--- /dev/null
+++ b/src/views/settings/Auth/Ldap/Ldap.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/settings/Auth/Ldap/LdapHA.vue b/src/views/settings/Auth/Ldap/LdapHA.vue
new file mode 100644
index 0000000000..42aeac136e
--- /dev/null
+++ b/src/views/settings/Auth/Ldap/LdapHA.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/settings/Auth/Ldap/TestLoginDialog.vue b/src/views/settings/Auth/Ldap/TestLoginDialog.vue
index a81cdcb629..d0e587b0c0 100644
--- a/src/views/settings/Auth/Ldap/TestLoginDialog.vue
+++ b/src/views/settings/Auth/Ldap/TestLoginDialog.vue
@@ -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)
}
diff --git a/src/views/settings/Auth/Ldap/index.vue b/src/views/settings/Auth/Ldap/index.vue
index 76d0ac74a2..8b13789179 100644
--- a/src/views/settings/Auth/Ldap/index.vue
+++ b/src/views/settings/Auth/Ldap/index.vue
@@ -1,152 +1 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/views/settings/Auth/index.vue b/src/views/settings/Auth/index.vue
index 87f11b9822..8dde8d8c63 100644
--- a/src/views/settings/Auth/index.vue
+++ b/src/views/settings/Auth/index.vue
@@ -8,7 +8,8 @@