Compare commits

...

90 Commits

Author SHA1 Message Date
jiangweidong
150795ebac feat: Custom change password supports configuration of interactive items 2024-10-24 17:02:16 +08:00
feng
a636bb2037 perf: List preference translate 2024-10-18 14:32:45 +08:00
fit2bot
9b6f54c1ed perf: update placeholder (#4409)
* perf: update passkey create

* perf: update placeholder

---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-10-17 11:10:18 +08:00
feng
611341307b fix: ticket duplicate submission 2024-10-16 19:09:18 +08:00
ibuler
f8479c53ff perf: update passkey create 2024-10-16 18:51:56 +08:00
wangruidong
e25bf46659 perf: url is null set remote false 2024-10-16 18:36:12 +08:00
wangruidong
6d07307e56 perf: users use Select2 2024-10-16 16:54:34 +08:00
wangruidong
6fb7fe8fa1 fix: interval field check 2024-10-16 11:01:44 +08:00
zhaojisen
19cd497095 fixed: Restores remote application styles 2024-10-15 17:06:06 +08:00
wangruidong
d858489367 fix: modify getPreference url 2024-10-15 14:13:10 +08:00
feng
341a30ba06 perf: The platform gets activeMenu and adds a default value 2024-10-15 11:32:51 +08:00
ZhaoJiSen
8390fb7429 Merge pull request #4398 from jumpserver/pr@dev@help_tip
perf: Help text automatic line wrapping
2024-10-12 14:40:43 +08:00
feng
5389f1d011 perf: Help text automatic line wrapping 2024-10-12 14:39:15 +08:00
ZhaoJiSen
bde642570f Merge pull request #4397 from jumpserver/pr@dev@perf_reveiver
style: Set special receiver to optimize the scroll wheel display
2024-10-12 11:35:16 +08:00
zhaojisen
3e7b970fe7 style: Set special receiver to optimize the scroll wheel display 2024-10-12 11:32:05 +08:00
ZhaoJiSen
2421e822b4 Merge pull request #4396 from jumpserver/pr@dev@perf_number_style
style: Optimize the number field style
2024-10-11 18:19:35 +08:00
zhaojisen
b2e474e3f6 style: Optimize the number field style 2024-10-11 18:15:46 +08:00
ZhaoJiSen
541836390a Merge pull request #4394 from jumpserver/pr@dev@fix_tab_change
fixed: Fixed command filtering and command group tab switching issues
2024-10-10 18:55:00 +08:00
zhaojisen
bb92e3f22b fixed: Fixed command filtering and command group tab switching issues 2024-10-10 18:51:50 +08:00
ZhaoJiSen
c9ad797b40 Merge pull request #4393 from jumpserver/pr@dev@fix_quick_job_node
fixed: Fixed the shortcut command selection node issue
2024-10-10 18:39:34 +08:00
zhaojisen
2a08310efc fixed: Fixed the shortcut command selection node issue 2024-10-10 18:37:47 +08:00
zhaojisen
3b2803b9a1 fixed: Fixed the shortcut command selection node issue 2024-10-10 18:36:33 +08:00
wangruidong
1b8ac9112e fix: user lack permission to view the type tree 2024-10-10 17:11:13 +08:00
ZhaoJiSen
9cc3dd4de9 Merge pull request #4390 from jumpserver/pr@dev@fix_add_lineHeight
style: Add Line Height
2024-10-10 14:40:30 +08:00
zhaojisen
120ef70eb1 style: Add Line Height 2024-10-10 14:36:01 +08:00
ZhaoJiSen
541f4ebc62 Merge pull request #4389 from jumpserver/pr@dev@fix_remote_app
style: Adjusting the styles of the remote application
2024-10-10 14:17:52 +08:00
zhaojisen
a47636abc7 style: Adjusting the styles of the remote application 2024-10-10 14:11:53 +08:00
ZhaoJiSen
78f6f4b36a Merge pull request #4388 from jumpserver/pr@dev@fix_comments_style
fixed: Fixed the presentation format of asset notes
2024-10-10 11:23:16 +08:00
zhaojisen
5abc0b77cf fixed: Fixed the presentation format of asset notes 2024-10-10 11:18:49 +08:00
ZhaoJiSen
464638e782 Merge pull request #4387 from jumpserver/pr@dev@fix_input_border
style: Fixed the display of the account number input box
2024-10-10 10:46:23 +08:00
zhaojisen
37cfeb2077 style: Fixed the display of the account number input box 2024-10-10 10:43:09 +08:00
ZhaoJiSen
44e297f01a Merge pull request #4386 from jumpserver/pr@dev@fix_tooltip_position
fixed: Fixed issue with sidebar toolTip placement
2024-10-10 09:50:33 +08:00
zhaojisen
febc283e36 fixed: Fixed issue with sidebar toolTip placement 2024-10-10 09:47:05 +08:00
ZhaoJiSen
dae33f55e8 Merge pull request #4385 from jumpserver/pr@dev@fix_special_chara
fixed: Fixed + and - not being special characters
2024-10-09 18:38:23 +08:00
zhaojisen
c62cd27690 fixed: Fixed + and - not being special characters 2024-10-09 18:34:31 +08:00
ZhaoJiSen
83443f8187 Merge pull request #4383 from jumpserver/pr@dev@fix_tab_refresh
fixed: Fixed An issue where adding a tag to asset does not work
2024-10-09 17:26:08 +08:00
zhaojisen
cb46f393e0 fixed: Fixed An issue where adding a tag to asset does not work 2024-10-09 17:17:12 +08:00
wangruidong
ddf5ac2151 fix: Determine the asset connection opening method based on the settings 2024-10-09 16:44:25 +08:00
wangruidong
88173f852a perf: Default endpoint cannot be disabled 2024-10-09 16:43:16 +08:00
wangruidong
d5f16e90e2 perf: add created_by field 2024-10-09 16:15:36 +08:00
ZhaoJiSen
d1c0aca4ff Merge pull request #4380 from jumpserver/pr@dev@fix_font_size
fixed: Fixed an issue where the font size of the resource is too large due to the parent element's font size being overridden
2024-10-09 14:47:42 +08:00
zhaojisen
a42edf17ec fixed: Fixed an issue where the font size of the resource is too large due to the parent element's font size being overridden 2024-10-09 14:32:06 +08:00
feng
dac5dfcd1c perf: Optimize file audit download prompt 2024-09-29 16:12:41 +08:00
ZhaoJiSen
8e6ca146e1 Merge pull request #4353 from jumpserver/dependabot/npm_and_yarn/serve-static-1.16.0
chore(deps-dev): bump serve-static from 1.15.0 to 1.16.0
2024-09-29 10:16:07 +08:00
ZhaoJiSen
9c06d36eab Merge pull request #4351 from jumpserver/dependabot/npm_and_yarn/path-to-regexp-3.3.0
chore(deps): bump path-to-regexp from 2.4.0 to 3.3.0
2024-09-29 10:14:32 +08:00
wisonic
ab7bd574f8 fix: assets proportion tooltip cause page bounced at first time 2024-09-27 17:34:25 +08:00
wangruidong
6007dc8621 perf: Endpoint add is_active field 2024-09-27 16:00:40 +08:00
wangruidong
a0e7c48dc9 perf: adhoc can clone 2024-09-27 14:12:13 +08:00
wangruidong
71dea791bf fix: xterm output truncate 2024-09-26 17:17:23 +08:00
ZhaoJiSen
e1b9184f41 Merge pull request #4372 from jumpserver/pr@dev@fix_perm_list_tree
fixed: Fixed an issue with draggable Tree nodes under asset authorization
2024-09-26 17:02:08 +08:00
zhaojisen
730d47f02a fixed: Fixed an issue with draggable Tree nodes under asset authorization 2024-09-26 16:59:18 +08:00
feng
e82ec68935 perf: The maximum length of the randomly generated password is changed to 36 2024-09-25 10:54:05 +08:00
feng
e8e5975d7f fix: fix after successfully creating an asset, the asset list cannot be partially refreshed 2024-09-24 15:44:55 +08:00
ZhaoJiSen
3cb9dec978 Merge pull request #4366 from jumpserver/pr@dev@fix_style_fix
style: Restoring styles
2024-09-19 18:21:50 +08:00
zhaojisen
c025441075 style: Restoring styles 2024-09-19 18:20:41 +08:00
wangruidong
050b50fa74 fix: playbook template clone error 2024-09-19 15:07:31 +08:00
ibuler
e26befabc3 perf: filter gateway using new params 2024-09-19 11:24:51 +08:00
wangruidong
8545fc6136 fix: session is_locked show value 2024-09-19 11:20:54 +08:00
ZhaoJiSen
e343e0df9d Merge pull request #4363 from jumpserver/pr@dev@perf_remote_app
style: Adjust the remote application page layout
2024-09-18 21:16:47 +08:00
zhaojisen
74519e6f3c style: Adjust the remote application page layout 2024-09-18 21:15:49 +08:00
feng
1305f90372 perf: TimerExecution translate 2024-09-18 15:06:06 +08:00
feng
bbc3f53c0a perf: User details community version removes user acl 2024-09-18 11:11:53 +08:00
ZhaoJiSen
aa605adbc7 Merge pull request #4359 from jumpserver/pr@dev@fix_show_NaN
fixed: Fixed NaN issue when setting timing interval
2024-09-17 13:42:51 +08:00
zhaojisen
49ea7d0969 fixed: Fixed NaN issue when setting timing interval 2024-09-17 13:34:51 +08:00
feng
4221bdb2ab perf: Postgresql add ssl mode 2024-09-13 17:49:05 +08:00
dependabot[bot]
af4010e299 chore(deps-dev): bump serve-static from 1.15.0 to 1.16.0
Bumps [serve-static](https://github.com/expressjs/serve-static) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/expressjs/serve-static/releases)
- [Changelog](https://github.com/expressjs/serve-static/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/serve-static/compare/v1.15.0...1.16.0)

---
updated-dependencies:
- dependency-name: serve-static
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-12 02:22:33 +00:00
ibuler
9b7c4ed353 chore: lock the pull request when workflow start 2024-09-12 10:22:01 +08:00
dependabot[bot]
879df90503 chore(deps): bump path-to-regexp from 2.4.0 to 3.3.0
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) from 2.4.0 to 3.3.0.
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](https://github.com/pillarjs/path-to-regexp/compare/v2.4.0...v3.3.0)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-11 18:05:18 +00:00
wangruidong
41f841532f feat: LDAP HA 2024-09-11 18:26:49 +08:00
feng
0b9f47dd84 feat: Postgresql support ssl 2024-09-11 18:12:33 +08:00
wangruidong
d32a376e8c feat: Support playbook, adhoc share 2024-09-11 18:02:03 +08:00
ibuler
0f8a8845df perf: change gateway gateway tip 2024-09-11 15:26:57 +08:00
wangruidong
8bb2c66b99 perf: Add task description 2024-09-09 18:59:25 +08:00
ibuler
b3d0be2f60 perf: update platform icon 2024-09-06 11:15:07 +08:00
ibuler
5a94ddd976 perf: api can set radio and checkbox tip 2024-09-06 11:14:03 +08:00
ibuler
7b9627a80b perf: support change gateway platform 2024-09-06 11:10:46 +08:00
feng
5fcfecc060 perf: Acl action add notify and warn 2024-09-06 11:08:21 +08:00
wangruidong
f4d7a2283c feat: Add announcement start and end dates 2024-09-06 10:56:03 +08:00
feng
40bb8410d3 perf: System settings task cleanup add change secret and push record retention days 2024-09-02 10:18:13 +08:00
fit2bot
45344ac620 perf: ldap import user error msg (#4333)
* perf: ldap import user error msg

* fix: Duplication on new code

---------

Co-authored-by: wangruidong <940853815@qq.com>
2024-08-30 17:01:18 +08:00
feng
5b894c9667 perf: View the internal message and convert the content into markdown 2024-08-29 17:28:18 +08:00
ZhaoJiSen
d89bd15b6d Merge pull request #4337 from jumpserver/pr@dev@fix_table_reload
fixed: Fixed an issue where tables could not be refreshed
2024-08-29 13:06:40 +08:00
zhaojisen
5e640dd45c fixed: Fixed an issue where tables could not be refreshed 2024-08-29 11:50:23 +08:00
fit2bot
09aa750794 chore: update checkout action 2024-08-28 11:34:09 +08:00
吴小白
744b215800 feat: bump node from 16.20 to 20.15 2024-08-27 11:07:37 +08:00
ibuler
f4e11da053 perf: revert pre org if logout 2024-08-23 15:40:16 +08:00
ibuler
2e6b5706d5 perf: remove tab in query 2024-08-21 18:04:19 +08:00
feng
440a5b27ef perf: Asset authorization request asset optimization mini limit 300 2024-08-21 11:34:17 +08:00
feng
932e16844e perf: Role detail title translate 2024-08-20 11:29:02 +08:00
feng
aff2e439dd perf: Ztree cannot be dragged under global organization 2024-08-20 10:45:21 +08:00
103 changed files with 998 additions and 405 deletions

View File

@@ -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 }}"

View File

@@ -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

View File

@@ -66,7 +66,7 @@
"normalize.css": "7.0.0",
"npm": "^7.8.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"path-to-regexp": "3.3.0",
"v-sanitize": "^0.0.13",
"vue": "2.6.10",
"vue-codemirror": "4.0.6",
@@ -117,7 +117,7 @@
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2",
"serve-static": "^1.13.2",
"serve-static": "^1.16.0",
"strip-ansi": "^7.1.0",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.2",

View File

@@ -15,6 +15,7 @@ export function testEmailSetting(data) {
data: data
})
}
export function importLicense(formData) {
return request({
url: '/api/v1/xpack/license/import',
@@ -25,6 +26,7 @@ export function importLicense(formData) {
data: formData
})
}
export function testLdapSetting(data, refresh = true) {
let url = '/api/v1/settings/ldap/testing/config/'
if (refresh) {
@@ -96,9 +98,17 @@ export function getPublicSettings(isOpen) {
method: 'get'
})
}
export function getLogo() {
return request({
url: '/api/v1/xpack/interface/setting/',
method: 'get'
})
}
export function getPreference() {
return request({
url: '/api/v1/users/preference/?category=luna',
method: 'get'
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 961 B

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 B

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -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) => {

View File

@@ -32,6 +32,10 @@ export default {
type: String,
default: '/api/v1/assets/assets/'
},
typeUrl: {
type: String,
default: '/api/v1/assets/nodes/category/tree/'
},
nodeUrl: {
type: String,
default: '/api/v1/assets/nodes/'
@@ -105,9 +109,9 @@ export default {
showAssets: false,
showSearch: false,
customTreeHeaderName: this.$t('TypeTree'),
url: '/api/v1/assets/nodes/category/tree/',
url: this.typeUrl,
nodeUrl: this.treeSetting?.nodeUrl || this.nodeUrl,
treeUrl: `/api/v1/assets/nodes/category/tree/?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
treeUrl: `${this.typeUrl}?assets=${showAssets ? '1' : '0'}&count_resource=${this.treeSetting.countResource || 'asset'}`,
callback: {
onSelected: (event, treeNode) => this.getAssetsUrl(treeNode)
}

View File

@@ -78,7 +78,7 @@ export default {
formatterData = data
}
return (
<span>{formatterData}</span>
<span style={{ whiteSpace: 'pre-wrap', lineHeight: '1.2' }}>{formatterData}</span>
)
}
if (this.value instanceof Array) {

View File

@@ -123,6 +123,7 @@ export default {
&__body {
padding: 20px 30px;
font-size: 13px;
&:has(.el-table) {
background: #f3f3f4;

View File

@@ -7,7 +7,7 @@
v-bind="$attrs"
v-on="$listeners"
>
<span
<div
v-for="(group, i) in groups"
:key="'group-'+group.name"
:slot="'id:'+group.name"
@@ -18,7 +18,7 @@
:index="i"
:line="i !== 0 && !groupHidden(groups[i - 1], i - 1)"
/>
</span>
</div>
</DataForm>
</template>

View File

@@ -7,7 +7,6 @@ import BasicTree from '@/components/Form/FormFields/BasicTree.vue'
import JsonEditor from '@/components/Form/FormFields/JsonEditor.vue'
import { assignIfNot, toSentenceCase } from '@/utils/common'
import TagInput from '@/components/Form/FormFields/TagInput.vue'
import TransferSelect from '@/components/Form/FormFields/TransferSelect.vue'
import i18n from '@/i18n/i18n'
export class FormFieldGenerator {
@@ -135,9 +134,6 @@ export class FormFieldGenerator {
case 'comment':
field.el.type = 'textarea'
break
case 'users':
field.component = TransferSelect
field.el.label = field.label
}
return field
}
@@ -197,6 +193,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 +218,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
}

View File

@@ -17,7 +17,7 @@
placement="right"
popper-class="help-tips"
>
<div slot="content" v-sanitize="data.helpTip" /> <!-- Noncompliant -->
<div slot="content" v-sanitize="data.helpTip" class="help-tip-content" /> <!-- Noncompliant -->
<i class="fa fa-question-circle-o help-tip-icon" />
</el-tooltip>
</template>
@@ -322,4 +322,9 @@ export default {
cursor: pointer;
}
}
.help-tip-content {
white-space: pre-wrap;
word-wrap: break-word;
}
</style>

View File

@@ -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

View File

@@ -48,7 +48,8 @@ export default {
type: 'input-number',
el: {
min: 8,
max: 30
max: 36,
size: 'mini'
}
},
{

View File

@@ -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,

View File

@@ -42,7 +42,7 @@ export default {
patterns.push([/\d/, i18n.t('NUMBER_REQUIRED')])
}
if (passwordRule['SECURITY_PASSWORD_SPECIAL_CHAR']) {
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~@#¥……&*()——|{}【】‘;:”“'。,、?]")
const pattern = new RegExp("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~@#¥……&*()——|{}【】‘;:”“'。,、?_+-]")
patterns.push([pattern, i18n.t('SPECIAL_CHAR_REQUIRED')])
}
for (const [pattern, msg] of patterns) {

View File

@@ -3,7 +3,7 @@
v-if="showColumnSettingPopover"
:cancel-title="$tc('RestoreDefault')"
:destroy-on-close="true"
:title="$tc('TableSetting')"
:title="$tc('ListPreference')"
:visible.sync="showColumnSettingPopover"
top="10%"
width="50%"

View File

@@ -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) {

View File

@@ -106,7 +106,7 @@ export default {
{
name: 'actionSetting',
icon: 'system-setting',
tip: this.$t('TableSetting'),
tip: this.$t('ListPreference'),
has: this.hasColumnSetting,
callback: this.handleTableSettingClick.bind(this)
},

View 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

View File

@@ -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() {

View File

@@ -131,6 +131,16 @@ export default {
}
},
computed: {},
watch: {
cellValue: {
handler(newValue) {
if (newValue) {
this.initial = this.formatterArgs.getLabels(this.cellValue)
this.iLabels = [...this.initial]
}
}
}
},
mounted() {
this.initial = this.formatterArgs.getLabels(this.cellValue)
this.iLabels = [...this.initial]

View File

@@ -39,7 +39,7 @@ export default {
showRenameBtn: false,
drag: {
isCopy: false,
isMove: true
isMove: !this.$store.getters.currentOrgIsRoot
}
},
callback: {

View File

@@ -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: {

View File

@@ -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

View File

@@ -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}`
}
}
}

View File

@@ -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,

View File

@@ -176,7 +176,7 @@ export default {
// 未找到与之对应的
& ::v-deep .el-submenu__title {
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
padding: 0 8px;
line-height: $headerHeight;
height: $headerHeight;

View File

@@ -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 = ''

View File

@@ -38,7 +38,7 @@ body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: auto;
color: var(--color-text-primary);
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 1.428;
}
@@ -593,3 +593,9 @@ li.rmenu i.fa {
height: 6px; /* 设置水平滚动条的高度 */
}
}
.black-theme-popover {
width: 300px;
max-height: 700px;
overflow-y: scroll;
}

View File

@@ -23,6 +23,10 @@ $single-menu-height: 38px;
}
}
.el-menu--vertical {
background-color: #fff;
}
.el-menu {
border-right: none !important;
background-color: inherit !important;
@@ -139,6 +143,10 @@ $single-menu-height: 38px;
.nest-menu .level2-menu {
line-height: $single-menu-height;
}
.el-tooltip {
width: 55px !important;
}
}
}
}

View File

@@ -128,6 +128,8 @@ export function getErrorResponseMsg(error) {
}).filter(i => i).join('; ')
} else if (typeof data === 'string') {
return data
} else {
msg = error.toString()
}
return msg
}
@@ -310,4 +312,26 @@ export function toSentenceCase(string) {
}).join(' ')
return s[0].toUpperCase() + s.slice(1)
}
export { BASE_URL }
export function openNewWindow(url) {
let count
let top = 50
count = parseInt(window.sessionStorage.getItem('newWindowCount'), 10)
if (isNaN(count)) {
count = 0
}
let left = 100 + count * 100
top = 50 + count * 50
if (left + screen.width / 3 > screen.width) {
// 支持两排足以
top = screen.height / 3
count = 1
left = 100
}
let params = 'toolbar=yes,scrollbars=yes,resizable=yes'
params = params + `,top=${top},left=${left},width=${screen.width / 3},height=${screen.height / 3}`
window.sessionStorage.setItem('newWindowCount', `${count + 1}`)
window.open(url, '_blank', params)
}

View File

@@ -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

View File

@@ -19,7 +19,7 @@ export default {
initial: {
is_periodic: false,
password_rules: {
length: 16
length: 36
},
interval: 24,
accounts: [],

View File

@@ -21,7 +21,7 @@ export default {
initial: {
is_periodic: false,
password_rules: {
length: 30
length: 36
},
interval: 24,
secret_type: 'password',

View File

@@ -4,7 +4,7 @@ import InputWithUnit from '@/components/Form/FormFields/InputWithUnit.vue'
import store from '@/store'
const validatorInterval = (rule, value, callback) => {
if (parseInt(value) < 1) {
if (isNaN(parseInt(value, 10)) || parseInt(value) < 1) {
return callback(new Error(i18n.t('EnsureThisValueIsGreaterThanOrEqualTo1')))
}
callback()

View File

@@ -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: [],

View File

@@ -2,7 +2,6 @@
<TabPage
:active-menu.sync="config.activeMenu"
:submenu="config.submenu"
@tab-click="handleTabClick"
/>
</template>
@@ -35,18 +34,6 @@ export default {
]
}
}
},
methods: {
handleTabClick(tab) {
const query = _.cloneDeep(this.$route.query)
const newQuery = {
...query,
tab: tab.name
}
this.$nextTick(() => {
this.$router.replace({ query: newQuery })
})
}
}
}
</script>

View File

@@ -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', 'pg_ssl_mode'],
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
@@ -52,7 +52,14 @@ export default {
},
use_ssl: {
label: this.$t('UseSSL'),
component: 'el-switch'
component: 'el-switch',
on: {
change: ([event], updateForm) => {
updateForm({
pg_ssl_mode: event ? 'require' : 'prefer'
})
}
}
},
allow_invalid_cert: {
label: this.$t('AllowInvalidCert'),

View File

@@ -48,7 +48,8 @@ export default {
},
tableConfig: {
url: tableUrl,
category: 'all'
category: 'all',
extraQuery: { 'order': '-date_updated' }
},
headerActions: {
handleImportClick: ({ selectedRows }) => {

View File

@@ -61,7 +61,7 @@ export default {
row['auto_config'].ansible_enabled &&
row['auto_config'].ping_enabled,
callback: ({ row }) => {
if (row.platform.name === 'Gateway') {
if (row.platform.name.startsWith('Gateway')) {
this.GatewayVisible = true
const port = row.protocols.find(item => item.name === 'ssh').port
if (!port) {

View File

@@ -191,7 +191,7 @@ export default {
row?.auto_config?.ansible_enabled &&
row?.auto_config?.ping_enabled,
callback: ({ row }) => {
if (row.platform.name === 'Gateway') {
if (row.platform.name.startsWith('Gateway')) {
this.GatewayVisible = true
const port = row.protocols.find(item => item.name === 'ssh').port
if (!port) {

View File

@@ -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;
}

View File

@@ -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 })
// })
}
}
}

View File

@@ -109,7 +109,7 @@ export default {
}
},
{
title: this.$t('Timer'),
title: this.$t('TimerExecution'),
attrs: {
model: this.object.task,
type: 'primary',

View File

@@ -39,7 +39,8 @@ export default {
submitBtnSize: 'mini',
submitBtnText: this.$t('Add'),
hasReset: false,
onSubmit: () => {},
onSubmit: () => {
},
submitMethod: () => 'post',
getUrl: () => '',
cleanFormValue(data) {
@@ -86,7 +87,11 @@ export default {
this.formConfig.fieldsMeta.protocols.el.hidden = true
}
this.resourceType = val
this.formConfig.fieldsMeta.value.el.ajax.url = url
if (url) {
this.formConfig.fieldsMeta.value.el.ajax.url = url
} else {
this.formConfig.fieldsMeta.attr.el.remote = false
}
this.formConfig.fieldsMeta.value.el.options = options
}
}
@@ -151,21 +156,31 @@ export default {
tableConfig: {
columns: [
{ prop: 'attr', label: this.$t('ResourceType'), formatter: tableFormatter('resource_type') },
{ prop: 'value', label: this.$t('Resource'), formatter: tableFormatter('resource', () => { return this.globalResource }) },
{
prop: 'value', label: this.$t('Resource'), formatter: tableFormatter('resource', () => {
return this.globalResource
})
},
{ prop: 'protocols', label: this.$t('Other'), formatter: tableFormatter('protocols') },
{ prop: 'action', label: this.$t('Action'), align: 'center', width: '100px', formatter: (row, col, cellValue, index) => {
return (
<div className='input-button'>
<el-button
icon='el-icon-minus'
size='mini'
style={{ 'flexShrink': 0 }}
type='danger'
onClick={ this.handleDelete(index) }
/>
</div>
)
} }
{
prop: 'action',
label: this.$t('Action'),
align: 'center',
width: '100px',
formatter: (row, col, cellValue, index) => {
return (
<div className='input-button'>
<el-button
icon='el-icon-minus'
size='mini'
style={{ 'flexShrink': 0 }}
type='danger'
onClick={this.handleDelete(index)}
/>
</div>
)
}
}
],
totalData: this.value || [],
hasPagination: false
@@ -177,7 +192,9 @@ export default {
},
methods: {
init() {
this.nameOptions.map((o) => { this.globalResource[o.value] = o.label })
this.nameOptions.map((o) => {
this.globalResource[o.value] = o.label
})
},
onSubmit() {
this.$emit('input', this.tableConfig.totalData)
@@ -218,9 +235,11 @@ export default {
::v-deep .el-form-item:nth-child(-n+3) {
width: 43.5%;
}
::v-deep .el-form-item:last-child {
width: 6%;
}
.action-input {
margin-top: -10px;
}

View File

@@ -30,7 +30,7 @@ export default {
domain_enabled: true
},
canSelect: (row) => {
return row.platform?.name !== 'Gateway'
return !row.platform?.name.startsWith('Gateway')
}
}
},

View File

@@ -25,7 +25,7 @@ export default {
return {
tableConfig: {
category: 'all',
url: `/api/v1/assets/assets/?domain=${this.$route.params.id}&exclude_platform=Gateway`,
url: `/api/v1/assets/assets/?domain=${this.$route.params.id}&is_gateway=0`,
tableConfig: {
columnsMeta: {
actions: {

View File

@@ -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 }
}

View File

@@ -39,7 +39,7 @@ export default {
data() {
return {
tableConfig: {
url: `/api/v1/assets/gateways/?domain=${this.$route.params.id}&platform=Gateway`,
url: `/api/v1/assets/gateways/?domain=${this.$route.params.id}`,
columnsExclude: [
'info', 'spec_info', 'auto_config'
],
@@ -174,7 +174,7 @@ export default {
name: 'GatewayAdd',
title: this.$t('Add'),
callback: function() {
this.AddGatewaySetting.AddGatewayDialogVisible = true
this.AddGatewaySetting.addGatewayDialogVisible = true
}.bind(this)
}
]
@@ -183,7 +183,7 @@ export default {
cell: '',
visible: false,
AddGatewaySetting: {
AddGatewayDialogVisible: false
addGatewayDialogVisible: false
}
}
},

View File

@@ -68,7 +68,7 @@ export default {
domain_enabled: true
},
canSelect: (row) => {
return row.platform?.name !== 'Gateway' && this.object.assets.map(item => item.id).indexOf(row.id) === -1
return !row.platform?.name.startsWith('Gateway') && this.object.assets.map(item => item.id).indexOf(row.id) === -1
}
}
}

View File

@@ -1,11 +1,11 @@
<template>
<Dialog
v-if="setting.AddGatewayDialogVisible"
v-if="setting.addGatewayDialogVisible"
:destroy-on-close="true"
:show-cancel="false"
:show-confirm="false"
:title="$tc('AddGatewayInDomain')"
:visible.sync="setting.AddGatewayDialogVisible"
:visible.sync="setting.addGatewayDialogVisible"
after
custom-class="asset-select-dialog"
top="15vh"
@@ -31,7 +31,7 @@ export default {
setting: {
type: Object,
default: () => {
return { AddGatewayDialogVisible: false }
return { addGatewayDialogVisible: false }
}
},
object: {
@@ -64,7 +64,7 @@ export default {
multiple: true,
clearable: true,
ajax: {
url: '/api/v1/assets/assets/?domain_enabled=true&platform=Gateway'
url: '/api/v1/assets/assets/?domain_enabled=true&is_gateway=1'
},
disabledValues: this.object.gateways.map(item => item.id)
}
@@ -83,7 +83,7 @@ export default {
},
methods: {
onSubmitSuccess(res) {
this.setting.AddGatewayDialogVisible = false
this.setting.addGatewayDialogVisible = false
this.$emit('close', res)
}
}

View File

@@ -141,6 +141,7 @@ export default {
if (Object.keys(filterField?.children || {}).length > 0) {
for (const [k, v] of Object.entries(filterField.children)) {
let component = 'el-input'
const el = {}
switch (v?.type) {
case 'list':
component = DynamicInput
@@ -148,12 +149,15 @@ export default {
case 'boolean':
component = Switcher
break
case 'text':
el['type'] = 'textarea'
break
}
if (param) {
v.default = param[k] || v.default
}
const item = { ...v, component: component }
const item = { ...v, component: component, el: el }
fieldsMeta[method].fields.push(k)
fieldsMeta[method].fieldsMeta[k] = item
}

View File

@@ -162,7 +162,7 @@ export default {
},
activated() {
setTimeout(() => {
this.tab.activeMenu = window.localStorage.getItem('lastTab')
this.tab.activeMenu = window.localStorage.getItem('lastTab') || 'host'
this.$refs.genericListTable.reloadTable()
}, 300)
},

View File

@@ -128,7 +128,8 @@ export default {
tip += current.label + '' + current.total + '<br/>'
}
return tip
}
},
appendToBody: true
},
grid: {
top: '60%',

View File

@@ -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: {

View File

@@ -35,6 +35,7 @@
</template>
<script>
import $ from '@/utils/jquery-vendor.js'
import AssetTreeTable from '@/components/Apps/AssetTreeTable'
import QuickJobTerm from '@/views/ops/Adhoc/components/QuickJobTerm.vue'
import CodeEditor from '@/components/Form/FormFields/CodeEditor'
@@ -286,6 +287,14 @@ export default {
view: {
dblClickExpand: false,
showLine: true
},
callback: {
onCheck: function(_event, treeId, treeNode) {
const treeObj = $.fn.zTree.getZTreeObj(treeId)
if (treeNode.checked) {
treeObj.expandNode(treeNode, true, false, true)
}
}
}
},
iShowTree: true

View File

@@ -75,6 +75,7 @@ export default {
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
lineHeight: 1.2,
fontSize: 13,
scrollback: 9999999,
rightClickSelectsWord: true,
theme: {
background: '#fff',

View File

@@ -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 }
}
@@ -98,6 +98,7 @@ export default {
el: {
baseUrl: '/api/v1/perms/users/self/assets/',
baseNodeUrl: '/api/v1/perms/users/self/nodes/',
typeUrl: '/api/v1/perms/users/self/nodes/children-with-assets/category/tree',
value: []
}
},

View File

@@ -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', 'created_by']
},
columnsMeta: {
name: {
@@ -29,11 +30,16 @@ 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'),
hasClone: false
canDelete: ({ row }) => {
return this.$hasPerm('ops.delete_adhoc') && row.creator === currentUserID
},
hasClone: true,
cloneRoute: 'AdhocCreate'
}
}
}

View File

@@ -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',

View File

@@ -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', 'created_by']
},
columnsMeta: {
name: {
@@ -36,11 +37,16 @@ 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,
cloneRoute: 'PlaybookCreate'
}
}
}

View File

@@ -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'

View File

@@ -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()
}

View File

@@ -76,6 +76,8 @@ export default {
}],
el: {
value: [],
defaultPageSize: 300,
baseUrl: '/api/v1/assets/assets/?fields_size=mini',
treeSetting: {
showSearch: false,
showRefresh: false

View File

@@ -37,7 +37,12 @@ export default {
notShowBuiltinTree: true,
url: '/api/v1/perms/asset-permissions/',
nodeUrl: '/api/v1/perms/asset-permissions/',
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1'
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1',
edit: {
drag: {
isMove: false
}
}
},
tableConfig: {
url: '/api/v1/perms/asset-permissions/',

View File

@@ -5,6 +5,7 @@
<script>
import GenericListPage from '@/layout/components/GenericListPage'
import { download } from '@/utils/common'
import store from '@/store'
export default {
components: {
@@ -36,7 +37,24 @@ export default {
return row.has_file
},
tip: ({ row }) => {
return row.has_file ? this.$t('Download') : this.$t('DownloadFTPFileTip')
const ftpFileMaxStore = store.getters.publicSettings['FTP_FILE_MAX_STORE']
const downloadTip = this.$t('Download')
const fileNotStoredTip = this.$t('FTPFileNotStored')
const storageNotEnabledTip = this.$t('FTPStorageNotEnabled')
const unknownStorageStateTip = this.$t('FTPUnknownStorageState')
if (row.has_file) {
return downloadTip
}
if (ftpFileMaxStore === 0) {
return storageNotEnabledTip
} else if (ftpFileMaxStore > 0) {
return fileNotStoredTip
} else {
return unknownStorageStateTip
}
},
callback: function({ row }) {
// 跳转下载页面

View File

@@ -5,7 +5,7 @@
<script type="text/jsx">
import ListTable from '@/components/Table/ListTable'
import { timeOffset } from '@/utils/time'
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
import { ActionsFormatter, ChoicesFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
export default {
name: 'BaseList',
@@ -119,7 +119,11 @@ export default {
}
},
is_locked: {
label: this.$t('IsLocked')
label: this.$t('IsLocked'),
formatter: ChoicesFormatter,
formatterArgs: {
showFalse: true
}
},
actions: {
prop: 'actions',

View 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>

View File

@@ -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)
},

View 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>

View 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>

View File

@@ -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)
}

View File

@@ -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>

View File

@@ -23,6 +23,9 @@ export default {
'AUTH_PASSKEY', 'FIDO_SERVER_ID', 'FIDO_SERVER_NAME'
],
fieldsMeta: {
'FIDO_SERVER_ID': {
placeholder: 'js.example.org'
}
},
submitMethod() {
return 'patch'
@@ -30,8 +33,7 @@ export default {
}
}
},
methods: {
}
methods: {}
}
</script>

View File

@@ -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',

View File

@@ -33,7 +33,7 @@ export default {
fieldsMeta: {
ANNOUNCEMENT: {
fields: [
'SUBJECT', 'CONTENT', 'LINK'
'SUBJECT', 'CONTENT', 'DATE_START', 'DATE_END', 'LINK'
],
fieldsMeta: {
CONTENT: {

View File

@@ -26,11 +26,14 @@
</span>
</template>
</el-table-column>
<el-table-column :label="$tc('Receivers')" show-overflow-tooltip>
<template v-slot="scope">
<span v-if="!scope.row.children">
{{ scope.row.receivers.map(item => item.name).join(', ') }}
</span>
<el-table-column :label="$tc('Receivers')">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top" popper-class="black-theme-popover">
<p v-for="item in scope.row.receivers" :key="item.name">{{ item.name }}</p>
<span v-if="!scope.row.children" slot="reference" class="name-wrapper">
{{ scope.row.receivers.map(item => item.name).join(', ') }}
</span>
</el-popover>
</template>
</el-table-column>
<el-table-column :label="$tc('Actions')" width="200">
@@ -186,4 +189,25 @@ export default {
margin-bottom: 0;
width: 50%;
}
::v-deep .el-table .cell {
display: flex;
.name-wrapper {
display: inline-block;
max-height: 55px;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
cursor: pointer;
}
}
::v-deep .black-theme-popover .el-popover__inner {
background-color: #000 !important;
color: #fff !important;
border-color: #000 !important;
}
</style>

View File

@@ -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'
]
]
],

View File

@@ -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
}
]
}

View File

@@ -33,7 +33,7 @@ export default {
'redis_port', 'sqlserver_port', 'oracle_port_range'
]
],
[this.$t('Other'), ['comment']]
[this.$t('Other'), ['is_active', 'comment']]
],
fieldsMeta: {
host: {
@@ -41,13 +41,17 @@ export default {
},
oracle_port_range: {
disabled: true
},
is_active: {
disabled: this.$route.params.id === '00000000-0000-0000-0000-000000000001'
}
},
hasDetailInMsg: false
}
},
computed: {},
created() {},
created() {
},
methods: {}
}
</script>

View File

@@ -25,7 +25,7 @@ export default {
'name', 'host', 'actions',
'http_port', 'https_port', 'ssh_port', 'rdp_port',
'mysql_port', 'mariadb_port', 'postgresql_port',
'redis_port', 'sqlserver_port', 'oracle_port_range'
'redis_port', 'sqlserver_port', 'oracle_port_range', 'is_active'
]
},
columnsMeta: {

View File

@@ -25,7 +25,7 @@
<el-form-item style="float: right">
<template v-if="hasActionPerm">
<el-button
:disabled="object.status.value === 'closed'"
:disabled="isDisabled || object.status.value === 'closed'"
size="small"
type="primary"
@click="handleApprove"
@@ -33,7 +33,7 @@
<i class="fa fa-check" /> {{ $t('Accept') }}
</el-button>
<el-button
:disabled="object.status.value === 'closed'"
:disabled="isDisabled || object.status.value === 'closed'"
size="small"
type="warning"
@click="handleReject"
@@ -43,7 +43,7 @@
</template>
<el-button
v-if="isSelfTicket"
:disabled="object.status.value === 'closed'"
:disabled="isDisabled || object.status.value === 'closed'"
size="small"
type="danger"
@click="handleClose"
@@ -92,6 +92,7 @@ export default {
},
data() {
return {
isDisabled: false,
comments: '',
type_api: '',
imageUrl: require('@/assets/img/avatar.png'),
@@ -154,17 +155,35 @@ export default {
this.createComment(function() {
})
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/approve/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
this.$axios.put(url).then(res => {
this.reloadPage()
}).catch(err => {
this.$message.error(err)
}).finally(() => {
this.isDisabled = false
})
},
defaultReject() {
this.createComment(function() {
})
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/reject/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
this.$axios.put(url).then(res => {
this.reloadPage()
}).catch(err => {
this.$message.error(err)
}).finally(() => {
this.isDisabled = false
})
},
defaultClose() {
const url = `/api/v1/tickets/${this.type_api}/${this.object.id}/close/`
this.$axios.put(url).then(res => this.reloadPage()).catch(err => this.$message.error(err))
this.$axios.put(url).then(res => {
this.reloadPage()
}).catch(err => {
this.$message.error(err)
}).finally(() => {
this.isDisabled = false
})
},
createComment(successCallback) {
const commentText = this.form.comments
@@ -185,17 +204,42 @@ export default {
}
})
},
handleAction(actionType) {
if (this.isDisabled) {
return
}
this.isDisabled = true
let handler
switch (actionType) {
case 'approve':
handler = this.approve || this.defaultApprove
break
case 'reject':
handler = this.reject || this.defaultReject
break
case 'close':
handler = this.close || this.defaultClose
break
default:
handler = null
break
}
if (handler) {
handler()
} else {
this.$message.error('No handler for action')
}
},
handleApprove() {
const handler = this.approve || this.defaultApprove
handler()
this.handleAction('approve')
},
handleReject() {
const handler = this.reject || this.defaultReject
handler()
this.handleAction('reject')
},
handleClose() {
const handler = this.close || this.defaultClose
handler()
this.handleAction('close')
},
handleComment() {
this.createComment(

View File

@@ -4,7 +4,6 @@
<script>
import { GenericCreateUpdatePage } from '@/layout/components'
import TransSelect from '@/components/Form/FormFields/TransferSelect.vue'
export default {
components: {
@@ -23,7 +22,6 @@ export default {
],
fieldsMeta: {
users: {
component: TransSelect,
el: {
url: '/api/v1/users/users/?fields_size=mini&order=name',
ajax: {

View File

@@ -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: {

View File

@@ -19,6 +19,7 @@ import AssetPermissionAsset from '@/views/perms/AssetPermission/AssetPermissionD
import AssetPermissionDetail from '@/views/perms/AssetPermission/AssetPermissionDetail/index.vue'
import AssetPermissionAccount from '@/views/perms/AssetPermission/AssetPermissionDetail/AssetPermissionAccount.vue'
import UserAssetPermissionRules from './UserAssetPermissionRules'
import store from '@/store'
export default {
components: {
@@ -63,7 +64,7 @@ export default {
{
title: this.$t('UserAclLists'),
name: 'UserLoginAcl',
hidden: () => !vm.$hasPerm('acls.view_loginacl')
hidden: () => !vm.$hasPerm('acls.view_loginacl') || !store.getters.publicSettings.XPACK_LICENSE_IS_VALID
},
{
title: this.$t('UserSession'),

Some files were not shown because too many files have changed in this diff Show More