diff --git a/src/api/ops.js b/src/api/ops.js
index 3c22f6938..d0adf0cd0 100644
--- a/src/api/ops.js
+++ b/src/api/ops.js
@@ -58,7 +58,7 @@ export function JobUploadFile(form) {
url: '/api/v1/ops/jobs/upload/',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
- timeout: 10 * 60 * 1000,
+ timeout: 60 * 60 * 1000,
data: form
})
}
diff --git a/src/components/Apps/AccountListTable/AccountList.vue b/src/components/Apps/AccountListTable/AccountList.vue
index 23019526f..fe50f58a8 100644
--- a/src/components/Apps/AccountListTable/AccountList.vue
+++ b/src/components/Apps/AccountListTable/AccountList.vue
@@ -392,9 +392,18 @@ export default {
can: this.$hasPerm('accounts.delete_account'),
type: 'primary',
callback: ({ row }) => {
- this.$axios.delete(`/api/v1/accounts/accounts/${row.id}/`).then(() => {
- this.$message.success(this.$tc('common.deleteSuccessMsg'))
- this.$refs.ListTable.reloadTable()
+ const msg = this.$t('accounts.AccountDeleteConfirmMsg')
+ this.$confirm(msg, this.$tc('common.Info'), {
+ type: 'warning',
+ confirmButtonClass: 'el-button--danger',
+ beforeClose: async(action, instance, done) => {
+ if (action !== 'confirm') return done()
+ this.$axios.delete(`/api/v1/accounts/accounts/${row.id}/`).then(() => {
+ done()
+ this.$refs.ListTable.reloadTable()
+ this.$message.success(this.$tc('common.deleteSuccessMsg'))
+ })
+ }
})
}
}
diff --git a/src/components/Apps/ChatAi/components/ChitChat/index.vue b/src/components/Apps/ChatAi/components/ChitChat/index.vue
index 445f6a209..25cef6810 100644
--- a/src/components/Apps/ChatAi/components/ChitChat/index.vue
+++ b/src/components/Apps/ChatAi/components/ChitChat/index.vue
@@ -42,7 +42,6 @@ const {
addMessageToActiveChat,
newChatAndAddMessageById,
removeLoadingMessageInChat,
- removeLoadingAndAddMessageToChat,
updateChaMessageContentById,
addTemporaryLoadingToChat
} = useChat()
@@ -120,12 +119,11 @@ export default {
}
},
onChatMessage(data) {
- if (!data.message.content && data.conversation_id) {
+ if (data.conversation_id) {
setLoading(true)
- removeLoadingAndAddMessageToChat(data)
+ removeLoadingMessageInChat()
this.currentConversationId = data.conversation_id
- } else {
- updateChaMessageContentById(data.message.id, data.message.content)
+ updateChaMessageContentById(data.message.id, data)
}
if (data.message?.type === 'finish') {
setLoading(false)
diff --git a/src/components/Apps/ChatAi/useChat.js b/src/components/Apps/ChatAi/useChat.js
index 269aa09d2..5c2753280 100644
--- a/src/components/Apps/ChatAi/useChat.js
+++ b/src/components/Apps/ChatAi/useChat.js
@@ -59,13 +59,8 @@ export function useChat() {
addChatMessageById(chat)
}
- const removeLoadingAndAddMessageToChat = (chat) => {
- store.commit('chat/removeLoadingMessageInChat')
- store.commit('chat/addMessageToActiveChat', chat)
- }
-
- const updateChaMessageContentById = (id, content) => {
- store.commit('chat/updateChaMessageContentById', { id, content })
+ const updateChaMessageContentById = (id, data) => {
+ store.commit('chat/updateChaMessageContentById', { id, data })
pageScroll('scrollRef')
}
@@ -78,7 +73,6 @@ export function useChat() {
addMessageToActiveChat,
newChatAndAddMessageById,
removeLoadingMessageInChat,
- removeLoadingAndAddMessageToChat,
addChatMessageById,
addTemporaryLoadingToChat,
updateChaMessageContentById
diff --git a/src/i18n/langs/en.json b/src/i18n/langs/en.json
index 795e00a77..507ba31d8 100644
--- a/src/i18n/langs/en.json
+++ b/src/i18n/langs/en.json
@@ -124,7 +124,8 @@
"AddAccountResult": "Add account result",
"AutoPush": "Auto Push",
"GeneralAccounts": "General Accounts",
- "VirtualAccounts": "Virtual Accounts"
+ "VirtualAccounts": "Virtual Accounts",
+ "AccountDeleteConfirmMsg": "Delete account, do you want to continue?"
},
"acl": {
"CommandFilterACLHelpMsg": "You can control whether commands can be executed on assets. Based on the rules, certain commands can be allowed while others are prohibited.",
@@ -1757,6 +1758,7 @@
"LDAPServerInfo": "LDAP Server",
"LDAPUser": "LDAP User",
"ChatAI": "Chat ai",
+ "Example": "Example: {example}",
"InsecureCommandAlert": "Insecure command alert",
"helpText": {
"TempPassword": "For a while, there is a period of 300 seconds, failure immediately after use",
@@ -2110,7 +2112,7 @@
"passwordWillExpiredPrefixMsg": "The password will expire in ",
"passwordWillExpiredSuffixMsg": " days.Please change your password as soon as possible.",
"dateLastLogin": "Date last login",
- "AddAllMembersWarningMsg": "Are you sure you want to add all members"
+ "AddAllMembersWarningMsg": "Are you sure you want to add all members?"
},
"notifications": {
"MessageType": "Message Type",
diff --git a/src/i18n/langs/ja.json b/src/i18n/langs/ja.json
index f420531eb..ec640a813 100644
--- a/src/i18n/langs/ja.json
+++ b/src/i18n/langs/ja.json
@@ -124,7 +124,8 @@
"AddAccountResult": "账号批量添加结果",
"AutoPush": "自動プッシュ",
"GeneralAccounts": "一般アカウント",
- "VirtualAccounts": "仮想アカウント"
+ "VirtualAccounts": "仮想アカウント",
+ "AccountDeleteConfirmMsg": "アカウントを削除します,続行しますか?"
},
"acl": {
"CommandFilterACLHelpMsg": "コマンドフィルタリングを使用すると、コマンドがアセット上で実行されるかどうかを制御できます。ルールに基づいて、特定のコマンドは許可され、他のコマンドは禁止されることがあります。",
@@ -1763,6 +1764,7 @@
"LDAPServerInfo": "LDAPサーバー",
"LDAPUser": "LDAPユーザー",
"ChatAI": "チャットAI",
+ "Example": "例: {example}",
"helpText": {
"TempPassword": "一時パスワードの有効期間は300秒で、使用後すぐに失効します",
"ApiKeyList": "Api keyを使用してリクエストヘッダに署名します。リクエストのヘッダごとに異なります。使用ドキュメントを参照してください",
@@ -2099,7 +2101,7 @@
"passwordExpired": "パスワードが期限切れです",
"passwordWillExpiredPrefixMsg": "パスワードはまもなく",
"passwordWillExpiredSuffixMsg": "期限が切れた後、できるだけ早くパスワードを変更してください。",
- "AddAllMembersWarningMsg": "すべてのメンバーを追加してもよろしいですか"
+ "AddAllMembersWarningMsg": "すべてのメンバーを追加してもよろしいですか?"
},
"notifications": {
"MessageType": "メッセージタイプ",
diff --git a/src/i18n/langs/zh.json b/src/i18n/langs/zh.json
index 9cf90c0dc..8e40dd9c7 100644
--- a/src/i18n/langs/zh.json
+++ b/src/i18n/langs/zh.json
@@ -30,6 +30,7 @@
"PleaseClickLeftAssetToViewGatheredUser": "收集用户列表,点击左侧资产进行查看",
"AutoCreate": "自动创建",
"AccountExportTips": "导出信息中包含账号密文涉及敏感信息,导出的格式为一个加密的zip文件(若没有设置加密密码,请前往个人信息中设置文件加密密码)。",
+ "AccountDeleteConfirmMsg": "删除账号,是否继续?",
"AccountPush": {
"WindowsPushHelpText": "windows 资产暂不支持推送密钥",
"AccountPushList": "账号推送",
@@ -1752,6 +1753,7 @@
"LDAPServerInfo": "LDAP 服务器",
"LDAPUser": "LDAP 用户",
"ChatAI": "智能问答",
+ "Example": "例: {example}",
"helpText": {
"TempPassword": "临时密码有效期为 300 秒,使用后立刻失效",
"ApiKeyList": "使用 Api key 签名请求头进行认证,每个请求的头部是不一样的, 相对于 Token 方式,更加安全,请查阅文档使用;
为降低泄露风险,Secret 仅在生成时可以查看, 每个用户最多支持创建 10 个",
@@ -1965,7 +1967,7 @@
"KokoSettingUpdate": "Koko 配置设置",
"UserSetting": "偏好设置",
"AllMembers": "全部成员",
- "AddAllMembersWarningMsg": "你确定要添加全部成员",
+ "AddAllMembersWarningMsg": "你确定要添加全部成员?",
"UnbindHelpText": "本地用户为此认证来源用户,无法解绑",
"SetStatus": "设置状态",
"Set": "已设置",
diff --git a/src/store/modules/chat.js b/src/store/modules/chat.js
index f90062288..979360154 100644
--- a/src/store/modules/chat.js
+++ b/src/store/modules/chat.js
@@ -38,10 +38,14 @@ const mutations = {
}
},
- updateChaMessageContentById(state, { id, content }) {
+ updateChaMessageContentById(state, { id, data }) {
const chats = state.activeChat.chats || []
const filterChat = chats.filter((chat) => chat.message.id === id)?.[0] || {}
- filterChat.message.content = content
+ if (Object.keys(filterChat).length > 0) {
+ filterChat.message.content = data.message.content
+ } else {
+ chats?.push(data)
+ }
}
}
diff --git a/src/utils/startup.js b/src/utils/startup.js
index 04cec7553..8022861ba 100644
--- a/src/utils/startup.js
+++ b/src/utils/startup.js
@@ -15,6 +15,17 @@ function reject(msg) {
return new Promise((resolve, reject) => reject(msg))
}
+function isRenewalExpired(renewalTime) {
+ const currentTimeStamp = Math.floor(new Date().getTime() / 1000)
+ const sessionExpireTimestamp = VueCookie.get('jms_session_expire_timestamp')
+
+ if (!sessionExpireTimestamp) {
+ return false
+ }
+ const timeDifferenceInSeconds = currentTimeStamp - parseInt(sessionExpireTimestamp, 10)
+ return timeDifferenceInSeconds > renewalTime
+}
+
async function checkLogin({ to, from, next }) {
if (whiteList.indexOf(to.path) !== -1) {
next()
@@ -28,12 +39,16 @@ async function checkLogin({ to, from, next }) {
return reject('No session mark found in cookie')
} else if (sessionExpire === 'close') {
let startTime = new Date().getTime()
- setInterval(() => {
+ const intervalId = setInterval(() => {
const endTime = new Date().getTime()
const delta = (endTime - startTime)
startTime = endTime
Vue.$log.debug('Set session expire: ', delta)
- VueCookie.set('jms_session_expire', 'close', { expires: '2m' })
+ if (!isRenewalExpired(120)) {
+ VueCookie.set('jms_session_expire', 'close', { expires: '2m' })
+ } else {
+ clearInterval(intervalId)
+ }
}, 10 * 1000)
} else if (sessionExpire === 'age') {
Vue.$log.debug('Session expire on age')
@@ -156,7 +171,9 @@ export async function changeCurrentViewIfNeed({ to, from, next }) {
export async function startup({ to, from, next }) {
// if (store.getters.inited) { return true }
- if (store.getters.inited) { return true }
+ if (store.getters.inited) {
+ return true
+ }
await store.dispatch('app/init')
// set page title
diff --git a/src/views/accounts/AccountGather/AccountGatherList.vue b/src/views/accounts/AccountGather/AccountGatherList.vue
index c4e86e995..1cfeae9f3 100644
--- a/src/views/accounts/AccountGather/AccountGatherList.vue
+++ b/src/views/accounts/AccountGather/AccountGatherList.vue
@@ -146,7 +146,7 @@ export default {
},
{
name: 'BulkSyncDelete',
- title: this.$t('common.BulkSyncDelete'),
+ title: this.$t('accounts.BulkSyncDelete'),
type: 'primary',
icon: 'fa fa-exchange',
can: ({ selectedRows }) => {
diff --git a/src/views/assets/Asset/AssetDetail/AssetCommand.vue b/src/views/assets/Asset/AssetDetail/AssetCommand.vue
index fda0e783d..031af0613 100644
--- a/src/views/assets/Asset/AssetDetail/AssetCommand.vue
+++ b/src/views/assets/Asset/AssetDetail/AssetCommand.vue
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/src/views/assets/Asset/AssetDetail/AssetSession.vue b/src/views/assets/Asset/AssetDetail/AssetSession.vue
index 88426d67b..9f184bb21 100644
--- a/src/views/assets/Asset/AssetDetail/AssetSession.vue
+++ b/src/views/assets/Asset/AssetDetail/AssetSession.vue
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/src/views/assets/Domain/DomainDetail/GatewayList.vue b/src/views/assets/Domain/DomainDetail/GatewayList.vue
index 8d66af8b0..2ba0c5182 100644
--- a/src/views/assets/Domain/DomainDetail/GatewayList.vue
+++ b/src/views/assets/Domain/DomainDetail/GatewayList.vue
@@ -117,6 +117,7 @@ export default {
name: 'GatewayCreate',
query: {
domain: this.object.id,
+ platform_type: row.type.value,
clone_from: row.id
}
}
diff --git a/src/views/assets/const.js b/src/views/assets/const.js
index 7cfe21621..b1df29019 100644
--- a/src/views/assets/const.js
+++ b/src/views/assets/const.js
@@ -119,6 +119,20 @@ export const assetFieldsMeta = (vm) => {
clearable: true
}
},
+ labels: {
+ name: 'labels',
+ label: vm.$t('assets.Label'),
+ type: 'm2m',
+ el: {
+ multiple: true,
+ url: '/api/v1/labels/labels/',
+ ajax: {
+ transformOption: (item) => {
+ return { label: `${item.name}:${item.value}`, value: `${item.name}:${item.value}` }
+ }
+ }
+ }
+ },
is_active: {
type: 'switch'
},
diff --git a/src/views/ops/File/index.vue b/src/views/ops/File/index.vue
index 4854cd75b..51a1fe651 100644
--- a/src/views/ops/File/index.vue
+++ b/src/views/ops/File/index.vue
@@ -82,16 +82,18 @@
{{ $tc('ops.output') }}:
-
+
{{ $tc('common.Status') }}:
{{ $tc('ops.' + executionInfo.status) }}
+ v-if="executionInfo.status==='timeout'"
+ class="status_warning"
+ >{{ $tc('ops.timeout') }}
+
+ {{ $tc('ops.success') + ': ' + summary.success }}
+ {{ $tc('ops.Skip') + ': ' + summary.skip }}
+ {{ $tc('ops.failed') + ': ' + summary.failed }}
+
{{ $tc('ops.timeDelta') }}:
@@ -210,7 +212,12 @@ export default {
ShowProgress: false,
upload_interval: null,
uploadFileList: [],
- SizeLimitMb: store.getters.publicSettings['FILE_UPLOAD_SIZE_LIMIT_MB']
+ SizeLimitMb: store.getters.publicSettings['FILE_UPLOAD_SIZE_LIMIT_MB'],
+ summary: {
+ 'success': 0,
+ 'failed': 0,
+ 'skip': 0
+ }
}
},
computed: {
@@ -273,9 +280,21 @@ export default {
}
}
},
+ taskStatusStat(summary) {
+ const { ok, failures, dark, excludes, skipped } = summary
+
+ const failedKeys = Object.keys(failures)
+ const darkKeys = Object.keys(dark)
+ const excludesKeys = Object.keys(excludes)
+
+ this.summary['success'] = ok.length
+ this.summary['failed'] = failedKeys.length + darkKeys.length
+ this.summary['skip'] = excludesKeys.length + skipped.length
+ },
getTaskStatus() {
getTaskDetail(this.currentTaskId).then(data => {
this.executionInfo.status = data['status']
+ this.taskStatusStat(data['summary'])
if (this.executionInfo.status === 'success') {
this.$message.success(this.$tc('ops.runSucceed'))
clearInterval(this.upload_interval)
@@ -334,11 +353,11 @@ export default {
const filenameList = fileList.map((file) => file.name)
const filenameCount = _.countBy(filenameList)
for (const file of fileList) {
- file.is_same = filenameCount[file.name] > 1
+ file.isSame = filenameCount[file.name] > 1
}
},
sameFileStyle(file) {
- if (file.is_same) {
+ if (file.isSame) {
return { backgroundColor: 'var(--color-danger)' }
}
return ''
@@ -362,7 +381,7 @@ export default {
execute() {
const { hosts, nodes } = this.getSelectedNodesAndHosts()
for (const file of this.uploadFileList) {
- if (file.is_same) {
+ if (file.isSame) {
this.$message.error(this.$tc('ops.DuplicateFileExists'))
return
}
@@ -476,7 +495,7 @@ export default {
}
.status_success {
- color: var(--color-success);
+ color: var(--color-primary);
}
.status_warning {
diff --git a/src/views/ops/Job/QuickJob.vue b/src/views/ops/Job/QuickJob.vue
index eee38bbbc..e2efe994e 100644
--- a/src/views/ops/Job/QuickJob.vue
+++ b/src/views/ops/Job/QuickJob.vue
@@ -80,8 +80,7 @@ export default {
timeCost: 0,
cancel: 0
},
- xtermConfig: {
- },
+ xtermConfig: {},
showHelpDialog: false,
showOpenAdhocDialog: false,
showOpenAdhocSaveDialog: false,
@@ -484,7 +483,7 @@ export default {
}
.status_success {
- color: var(--color-success);
+ color: var(--color-primary);
}
.status_warning {
diff --git a/src/views/settings/Applet/Applet/UploadDialog.vue b/src/views/settings/Applet/Applet/UploadDialog.vue
index 14635526f..3ff087c65 100644
--- a/src/views/settings/Applet/Applet/UploadDialog.vue
+++ b/src/views/settings/Applet/Applet/UploadDialog.vue
@@ -90,7 +90,7 @@ export default {
form,
{
headers: { 'Content-Type': 'multipart/form-data' },
- timeout: 10 * 60 * 1000,
+ timeout: 60 * 60 * 1000,
disableFlashErrorMsg: true,
params: { update: true }
}
diff --git a/src/views/settings/Applet/index.vue b/src/views/settings/Applet/index.vue
index e5062bb36..81a933fef 100644
--- a/src/views/settings/Applet/index.vue
+++ b/src/views/settings/Applet/index.vue
@@ -39,14 +39,14 @@ export default {
title: this.$t('terminal.VirtualApp'),
name: 'VirtualApp',
hidden: () => {
- return !store.getters.publicSettings['VIRTUAL_APP_ENABLED']
+ return !store.getters.publicSettings['VIRTUAL_APP_ENABLED'] || !this.$store.getters.hasValidLicense
}
},
{
title: this.$t('terminal.AppProvider'),
name: 'AppProvider',
hidden: () => {
- return !store.getters.publicSettings['VIRTUAL_APP_ENABLED']
+ return !store.getters.publicSettings['VIRTUAL_APP_ENABLED'] || !this.$store.getters.hasValidLicense
}
}
]
diff --git a/src/views/settings/Feature/Chat.vue b/src/views/settings/Feature/Chat.vue
index e746888fa..6f3638dcc 100644
--- a/src/views/settings/Feature/Chat.vue
+++ b/src/views/settings/Feature/Chat.vue
@@ -51,6 +51,22 @@ export default {
]
]
],
+ fieldsMeta: {
+ GPT_BASE_URL: {
+ el: {
+ autocomplete: 'new-password'
+ },
+ helpText: this.$t('setting.Example', { example: 'https://api.openai.com/v1' })
+ },
+ GPT_API_KEY: {
+ el: {
+ autocomplete: 'new-password'
+ }
+ },
+ GPT_PROXY: {
+ helpText: this.$t('setting.Example', { example: 'http://ip:port' })
+ }
+ },
submitMethod() {
return 'patch'
}
diff --git a/src/views/settings/Terminal/Endpoint/EndpointCreateUpdate.vue b/src/views/settings/Terminal/Endpoint/EndpointCreateUpdate.vue
index 6926615f8..80726ae7c 100644
--- a/src/views/settings/Terminal/Endpoint/EndpointCreateUpdate.vue
+++ b/src/views/settings/Terminal/Endpoint/EndpointCreateUpdate.vue
@@ -30,7 +30,7 @@ export default {
this.$t('terminal.DatabasePort'),
[
'mysql_port', 'mariadb_port', 'postgresql_port',
- 'redis_port', 'oracle_port_range'
+ 'redis_port', 'sqlserver_port', 'oracle_port_range'
]
],
[this.$t('common.Other'), ['comment']]
diff --git a/src/views/settings/Terminal/Endpoint/EndpointList.vue b/src/views/settings/Terminal/Endpoint/EndpointList.vue
index 786b51889..152b97450 100644
--- a/src/views/settings/Terminal/Endpoint/EndpointList.vue
+++ b/src/views/settings/Terminal/Endpoint/EndpointList.vue
@@ -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', 'oracle_port_range'
+ 'redis_port', 'sqlserver_port', 'oracle_port_range'
]
},
columnsMeta: {
diff --git a/src/views/users/Group/UserGroupDetail/GroupUser.vue b/src/views/users/Group/UserGroupDetail/GroupUser.vue
index 72be9c4c3..33ba8a08c 100644
--- a/src/views/users/Group/UserGroupDetail/GroupUser.vue
+++ b/src/views/users/Group/UserGroupDetail/GroupUser.vue
@@ -46,7 +46,7 @@ export default {
},
callbacks: Object.freeze({
click: () => {
- const msg = `${this.$t('users.AddAllMembersWarningMsg')} ?`
+ const msg = this.$t('users.AddAllMembersWarningMsg')
this.$confirm(msg, this.$tc('common.Info'), {
type: 'warning',
confirmButtonClass: 'el-button--danger',
@@ -60,7 +60,8 @@ export default {
window.location.reload()
})
}
- }).catch(() => {})
+ }).catch(() => {
+ })
}
})
}
@@ -85,6 +86,9 @@ export default {
width: 150,
objects: this.object.users,
formatter: DeleteActionFormatter,
+ formatterArgs: {
+ disabled: !this.$hasPerm('users.change_usergroup')
+ },
onDelete: function(col, row, cellValue, reload) {
this.$axios.delete(
'/api/v1/users/users-groups-relations/', {
@@ -133,6 +137,7 @@ export default {
},
showHasObjects: false,
hasObjectsId: this.object.users,
+ disabled: !this.$hasPerm('users.change_usergroup'),
performAdd: (items) => {
const relationUrl = `/api/v1/users/users-groups-relations/`
const groupId = this.object.id
diff --git a/src/views/users/Group/UserGroupList.vue b/src/views/users/Group/UserGroupList.vue
index a3109cfe0..6aca8eda7 100644
--- a/src/views/users/Group/UserGroupList.vue
+++ b/src/views/users/Group/UserGroupList.vue
@@ -28,6 +28,9 @@ export default {
width: '160px',
formatter: AmountFormatter,
formatterArgs: {
+ getItem(item) {
+ return item.is_service_account ? null : item.name
+ },
routeQuery: {
activeTab: 'GroupUser'
}
diff --git a/src/views/users/User/UserCreateUpdate.vue b/src/views/users/User/UserCreateUpdate.vue
index 23068358d..d6f987313 100644
--- a/src/views/users/User/UserCreateUpdate.vue
+++ b/src/views/users/User/UserCreateUpdate.vue
@@ -174,6 +174,13 @@ export default {
return 'post'
}
},
+ afterGetFormValue(obj) {
+ if (obj?.id) {
+ obj.org_roles = obj.org_roles.map(({ id }) => id)
+ obj.system_roles = obj.system_roles.map(({ id }) => id)
+ }
+ return obj
+ },
cleanFormValue(value) {
const method = this.submitMethod()
if (method === 'post' && value.password_strategy === 'email') {