diff --git a/Dockerfile b/Dockerfile index e8ec915ec..b54bf8999 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,23 @@ -FROM node:10 as stage-build +FROM node:14.16 as stage-build +ARG TARGETARCH +ARG VERSION +ENV VERSION=$VERSION ARG NPM_REGISTRY="https://registry.npmmirror.com" ENV NPM_REGISTY=$NPM_REGISTRY -ARG SASS_BINARY_SITE="https://npmmirror.com/mirrors/node-sass" -ENV SASS_BINARY_SITE=$SASS_BINARY_SITE WORKDIR /data -RUN npm config set sass_binary_site=${SASS_BINARY_SITE} -RUN npm config set registry ${NPM_REGISTRY} -RUN yarn config set registry ${NPM_REGISTRY} -COPY package.json yarn.lock /data/ -RUN yarn install -RUN npm rebuild node-sass +RUN set -ex \ + && npm config set registry ${NPM_REGISTRY} \ + && yarn config set registry ${NPM_REGISTRY} \ + && yarn config set cache-folder /root/.cache/yarn/lina -ARG VERSION -ENV VERSION=$VERSION ADD . /data -RUN cd utils && bash -xieu build.sh build +RUN --mount=type=cache,target=/root/.cache/yarn \ + sed -i "s@Version .*@Version ${VERSION}@g" src/layout/components/Footer/index.vue \ + && yarn install \ + && yarn build FROM nginx:alpine -COPY --from=stage-build /data/release/lina /opt/lina +COPY --from=stage-build /data/lina /opt/lina COPY nginx.conf /etc/nginx/conf.d/default.conf diff --git a/package.json b/package.json index 7552d82bb..015471211 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "scripts": { "dev": "vue-cli-service serve", "serve": "vue-cli-service serve", + "build": "vue-cli-service build --mode staging", "build:prod": "vue-cli-service build", "build:stage": "vue-cli-service build --mode staging", "preview": "node build/index.js --preview", @@ -32,7 +33,7 @@ "element-ui": "2.13.2", "eslint-plugin-html": "^6.0.0", "install": "^0.13.0", - "jquery": "^3.5.0", + "jquery": "^3.6.1", "js-cookie": "2.2.0", "jsencrypt": "^3.2.1", "krry-transfer": "^1.7.3", diff --git a/src/App.vue b/src/App.vue index fed5caa5f..8de84169c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,12 +1,19 @@ diff --git a/src/router/common.js b/src/router/common.js index 208bc277d..14d815a4d 100644 --- a/src/router/common.js +++ b/src/router/common.js @@ -15,6 +15,16 @@ export default [ permissions: [] } }, + { + path: '/ops/ansible/task/:id/log/', + component: () => import('@/views/ops/CeleryTaskLog'), + name: 'AnsibleTaskLog', + hidden: true, + meta: { + title: i18n.t('route.CeleryTaskLog'), + permissions: [] + } + }, { path: '/ops/task/task/:id/log/', component: () => import('@/views/ops/CeleryTaskLog'), diff --git a/src/router/console/xpack.js b/src/router/console/xpack.js index 0018a9bda..14fe9191d 100644 --- a/src/router/console/xpack.js +++ b/src/router/console/xpack.js @@ -1,6 +1,8 @@ import empty from '@/layout/empty' import i18n from '@/i18n/i18n' +const activateMenu = '/console/assets/assets' + export default [ { path: 'cloud', @@ -20,7 +22,7 @@ export default [ hidden: true, meta: { title: i18n.t('xpack.Cloud.CloudSync'), - activeMenu: '/console/assets/assets' + activeMenu: activateMenu } }, { @@ -71,6 +73,7 @@ export default [ hidden: true, meta: { title: i18n.t('xpack.Cloud.AccountDetail'), + activeMenu: activateMenu, permissions: ['xpack.view_account'] } } @@ -121,7 +124,8 @@ export default [ name: 'SyncInstanceTaskDetail', hidden: true, meta: { - title: i18n.t('xpack.Cloud.SyncInstanceTaskDetail') + title: i18n.t('xpack.Cloud.SyncInstanceTaskDetail'), + activeMenu: activateMenu } } ] diff --git a/src/store/modules/common.js b/src/store/modules/common.js index e26944216..f3ac0e80e 100644 --- a/src/store/modules/common.js +++ b/src/store/modules/common.js @@ -2,7 +2,8 @@ import { optionUrlMeta } from '@/api/common' const getDefaultState = () => { return { - metaMap: {} + metaMap: {}, + isRouterAlive: true } } @@ -11,6 +12,12 @@ const state = getDefaultState() const mutations = { SET_URL_META: (state, { url, meta }) => { state.metaMap[url] = meta + }, + reload: (state) => { + state.isRouterAlive = false + setTimeout(() => { + state.isRouterAlive = true + }, 0) } } diff --git a/src/utils/common.js b/src/utils/common.js index 4c260272c..68b321fab 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -302,3 +302,8 @@ export function groupedDropdownToCascader(group) { export { BASE_URL } +export function openWindow(url, name = '', iWidth = 900, iHeight = 600) { + var iTop = (window.screen.height - 30 - iHeight) / 2 + var iLeft = (window.screen.width - 10 - iWidth) / 2 + window.open(url, name, 'height=' + iHeight + ',width=' + iWidth + ',top=' + iTop + ',left=' + iLeft) +} diff --git a/src/utils/jms.js b/src/utils/jms.js index 05472ea32..588f7a107 100644 --- a/src/utils/jms.js +++ b/src/utils/jms.js @@ -1,8 +1,10 @@ import store from '@/store' import { constantRoutes } from '@/router' +import { openWindow } from './common' -export function openTaskPage(taskId) { - window.open(`/#/ops/celery/task/${taskId}/log/`, '', 'width=900,height=600') +export function openTaskPage(taskId, taskType) { + taskType = taskType || 'celery' + openWindow(`/#/ops/${taskType}/task/${taskId}/log/?type=${taskType}`) } export function checkPermission(permsRequired, permsAll) { diff --git a/src/utils/jquery-vendor.js b/src/utils/jquery-vendor.js index 97b3464ae..40ec6ad4d 100644 --- a/src/utils/jquery-vendor.js +++ b/src/utils/jquery-vendor.js @@ -1,4 +1,2 @@ -import $ from 'jquery' -window.$ = $ -window.jQuery = $ +import $ from 'jquery/dist/jquery.min.js' export default $ diff --git a/src/utils/org.js b/src/utils/org.js index 6cd0a5d3d..ea18b8f77 100644 --- a/src/utils/org.js +++ b/src/utils/org.js @@ -30,7 +30,7 @@ async function changeOrg(org) { if (index !== -1) { location.href = path.substring(0, index) } - setTimeout(() => location.reload(), 400) + setTimeout(() => store.commit('common/reload'), 400) } } diff --git a/src/views/accounts/ChangeAuthPlan/AppChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanApp/index.vue b/src/views/accounts/ChangeAuthPlan/AppChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanApp/index.vue index 74ae7e95e..4d8ef9037 100644 --- a/src/views/accounts/ChangeAuthPlan/AppChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanApp/index.vue +++ b/src/views/accounts/ChangeAuthPlan/AppChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanApp/index.vue @@ -98,7 +98,7 @@ export default { that.iHasObjects = [...that.iHasObjects, ...objects] that.$refs.select2.clearSelected() this.$message.success(this.$t('common.updateSuccessMsg')) - window.location.reload() + this.$store.commit('common/reload') this.$refs.listTable.$refs.ListTable.reloadTable() } } diff --git a/src/views/accounts/ChangeAuthPlan/AssetChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanAsset/index.vue b/src/views/accounts/ChangeAuthPlan/AssetChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanAsset/index.vue index ecca8a67e..e420c086a 100644 --- a/src/views/accounts/ChangeAuthPlan/AssetChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanAsset/index.vue +++ b/src/views/accounts/ChangeAuthPlan/AssetChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanAsset/index.vue @@ -93,7 +93,7 @@ export default { onAddSuccess: (items, that) => { this.$log.debug('AssetSelect value', that.assets) this.$message.success(this.$t('common.updateSuccessMsg')) - window.location.reload() + this.$store.commit('common/reload') } }, nodeRelationConfig: { @@ -120,7 +120,7 @@ export default { that.iHasObjects = [...that.iHasObjects, ...objects] that.$refs.select2.clearSelected() this.$message.success(this.$t('common.updateSuccessMsg')) - window.location.reload() + this.$store.commit('common/reload') }, performDelete: (item) => { const data = { diff --git a/src/views/applications/DatabaseApp/const.js b/src/views/applications/DatabaseApp/const.js index f9dbd99b0..f482d12fa 100644 --- a/src/views/applications/DatabaseApp/const.js +++ b/src/views/applications/DatabaseApp/const.js @@ -1,11 +1,9 @@ -import { ORACLE, MONGODB, REDIS } from '../const' +import { MONGODB, REDIS } from '../const' export function getDatabaseTypeFieldsMap(type) { const baseParams = ['host', 'port', 'database'] const tlsParams = ['use_ssl', 'ca_cert'] switch (type) { - case ORACLE: - return baseParams.concat(['version']) case REDIS: return baseParams.concat(tlsParams.concat(['client_cert', 'cert_key'])) case MONGODB: diff --git a/src/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskDetail/AssetList.vue b/src/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskDetail/AssetList.vue index 4d0f9407c..4a47bc984 100644 --- a/src/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskDetail/AssetList.vue +++ b/src/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskDetail/AssetList.vue @@ -38,6 +38,7 @@ export default { }, tableConfig: { url: `/api/v1/xpack/cloud/sync-instance-tasks/${this.object.id}/instances/`, + hasSelection: false, columns: [ 'instance_id', { diff --git a/src/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskList.vue b/src/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskList.vue index 107377a20..9f0597653 100644 --- a/src/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskList.vue +++ b/src/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskList.vue @@ -58,10 +58,7 @@ export default { formatter: DetailFormatter, formatterArgs: { permissions: 'xpack.view_syncinstancedetail', - route: 'SyncInstanceTaskDetail', - routeQuery: { - activeTab: 'detail' - } + route: 'SyncInstanceTaskDetail' } }, history_count: { diff --git a/src/views/assets/CommandFilter/CommandFilterCreateUpdate.vue b/src/views/assets/CommandFilter/CommandFilterCreateUpdate.vue index 1716905c1..2844954f6 100644 --- a/src/views/assets/CommandFilter/CommandFilterCreateUpdate.vue +++ b/src/views/assets/CommandFilter/CommandFilterCreateUpdate.vue @@ -15,7 +15,7 @@ export default { }, fields: [ [this.$t('common.Basic'), ['name']], - [this.$t('common.Correlation'), ['users', 'user_groups', 'assets', 'applications', 'system_users']], + [this.$t('common.Correlation'), ['users', 'user_groups', 'nodes', 'assets', 'applications', 'system_users']], [this.$t('common.Other'), ['is_active', 'comment']] ], fieldsMeta: { @@ -36,6 +36,17 @@ export default { url: '/api/v1/users/groups/' } }, + nodes: { + el: { + value: [], + ajax: { + url: '/api/v1/assets/nodes/', + transformOption: (item) => { + return { label: item.full_value, value: item.id } + } + } + } + }, assets: { type: 'assetSelect', component: AssetSelect, diff --git a/src/views/ops/TaskDetail/AdhocDetail/AdhocExecutionHistory.vue b/src/views/ops/TaskDetail/AdhocDetail/AdhocExecutionHistory.vue index 46413b8c0..bfaf1a090 100644 --- a/src/views/ops/TaskDetail/AdhocDetail/AdhocExecutionHistory.vue +++ b/src/views/ops/TaskDetail/AdhocDetail/AdhocExecutionHistory.vue @@ -6,6 +6,7 @@ import ListTable from '@/components/ListTable' import { ActionsFormatter } from '@/components/TableFormatters' import { toSafeLocalDateStr } from '@/utils/common' +import { openTaskPage } from '@/utils/jms' export default { name: 'AdhocExecutionHistory', @@ -86,6 +87,14 @@ export default { callback: function({ row, tableData }) { return this.$router.push({ name: 'HistoryExecutionDetail', params: { id: row.id }}) } + }, + { + name: 'log', + title: this.$t('ops.output'), + type: 'info', + callback: function({ row }) { + openTaskPage(row.id, 'ansible') + } } ] } diff --git a/src/views/ops/TaskDetail/HistoryExecutionDetail/HistoryExecutionDetail.vue b/src/views/ops/TaskDetail/HistoryExecutionDetail/HistoryExecutionDetail.vue index 80345f564..7d9901561 100644 --- a/src/views/ops/TaskDetail/HistoryExecutionDetail/HistoryExecutionDetail.vue +++ b/src/views/ops/TaskDetail/HistoryExecutionDetail/HistoryExecutionDetail.vue @@ -15,7 +15,6 @@ import DetailCard from '@/components/DetailCard' import { toSafeLocalDateStr } from '@/utils/common' import RunInfoCard from '../../RunInfoCard' import { toLastFailureDisplay, toLastSucessDisplay } from '../business' -import { openTaskPage } from '@/utils/jms' export default { name: 'HistoryExecutionDetail', @@ -72,17 +71,6 @@ export default { { key: this.$t('ops.isSuccess'), value: this.object.is_success - }, - { - key: this.$t('ops.output'), - value: this.object.id, - formatter: function(row, value) { - const onClick = function() { - openTaskPage(value, 'ansible') - } - const title = this.$t('common.View') - return { title } - } } ] } diff --git a/src/views/ops/TaskDetail/TaskDetail.vue b/src/views/ops/TaskDetail/TaskDetail.vue index c60d2d8c6..51ce796e1 100644 --- a/src/views/ops/TaskDetail/TaskDetail.vue +++ b/src/views/ops/TaskDetail/TaskDetail.vue @@ -108,7 +108,7 @@ export default { openTaskPage(value, 'ansible') } const title = this.$t('common.View') - return { title } + return { title } } } ] diff --git a/src/views/ops/TaskDetail/TaskHistory.vue b/src/views/ops/TaskDetail/TaskHistory.vue index 401568565..160b57846 100644 --- a/src/views/ops/TaskDetail/TaskHistory.vue +++ b/src/views/ops/TaskDetail/TaskHistory.vue @@ -6,6 +6,7 @@ import ListTable from '@/components/ListTable' import { DetailFormatter } from '@/components/TableFormatters' import { toSafeLocalDateStr } from '@/utils/common' +import { openTaskPage } from '@/utils/jms' export default { name: 'TaskHistory', @@ -95,6 +96,14 @@ export default { callback: function({ row, tableData }) { return this.$router.push({ name: 'HistoryExecutionDetail', params: { id: row.id }}) } + }, + { + name: 'log', + title: this.$t('ops.output'), + type: 'info', + callback: function({ row }) { + openTaskPage(row.id, 'ansible') + } } ] } diff --git a/src/views/perms/AssetPermission/AssetPermissionDetail/AssetPermissionAsset.vue b/src/views/perms/AssetPermission/AssetPermissionDetail/AssetPermissionAsset.vue index 2e4fa30e8..526e73bc4 100644 --- a/src/views/perms/AssetPermission/AssetPermissionDetail/AssetPermissionAsset.vue +++ b/src/views/perms/AssetPermission/AssetPermissionDetail/AssetPermissionAsset.vue @@ -93,7 +93,7 @@ export default { this.$message.success(this.$t('common.updateSuccessMsg')) this.$refs.ListTable.reloadTable() that.$refs.assetSelect.$refs.select2.clearSelected() - window.location.reload() + this.$store.commit('common/reload') } }, nodeRelationConfig: { diff --git a/src/views/profile/ProfileInfo.vue b/src/views/profile/ProfileInfo.vue index e1c3e8491..46c81cad4 100644 --- a/src/views/profile/ProfileInfo.vue +++ b/src/views/profile/ProfileInfo.vue @@ -37,7 +37,8 @@ import DetailCard from '@/components/DetailCard' import QuickActions from '@/components/QuickActions' import UserConfirmDialog from '@/components/UserConfirmDialog' import { toSafeLocalDateStr } from '@/utils/common' -import store from '@/store' +import { getProfile } from '@/api/users' +import { mapState } from 'vuex' export default { name: 'ProfileInfo', @@ -47,14 +48,9 @@ export default { QuickActions, UserConfirmDialog }, - props: { - object: { - type: Object, - default: () => store.state.users.profile - } - }, data() { return { + object: this.userProfile || {}, url: `/api/v1/users/profile/`, showPasswordDialog: false, currentEdit: '', @@ -160,7 +156,7 @@ export default { attrs: { disabled: true, name: 'site_msg', - model: this.object.receive_backends.indexOf('site_msg') !== -1 + model: this.object?.receive_backends.indexOf('site_msg') !== -1 }, callbacks: { change: this.updateUserReceiveBackends @@ -171,7 +167,7 @@ export default { type: 'switcher', attrs: { name: 'email', - model: this.object.receive_backends.indexOf('email') !== -1 + model: this.object?.receive_backends.indexOf('email') !== -1 }, callbacks: { change: this.updateUserReceiveBackends @@ -182,7 +178,7 @@ export default { type: 'switcher', attrs: { name: 'wecom', - model: this.object.receive_backends.indexOf('wecom') !== -1 + model: this.object?.receive_backends.indexOf('wecom') !== -1 }, has: this.$store.getters.publicSettings.AUTH_WECOM, callbacks: { @@ -194,7 +190,7 @@ export default { type: 'switcher', attrs: { name: 'dingtalk', - model: this.object.receive_backends.indexOf('dingtalk') !== -1 + model: this.object?.receive_backends.indexOf('dingtalk') !== -1 }, has: this.$store.getters.publicSettings.AUTH_DINGTALK, callbacks: { @@ -206,7 +202,7 @@ export default { type: 'switcher', attrs: { name: 'feishu', - model: this.object.receive_backends.indexOf('feishu') !== -1 + model: this.object?.receive_backends.indexOf('feishu') !== -1 }, has: this.$store.getters.publicSettings.AUTH_FEISHU, callbacks: { @@ -217,6 +213,9 @@ export default { } }, computed: { + ...mapState({ + userProfile: state => state.users.profile + }), detailCardItems() { return [ { @@ -299,6 +298,12 @@ export default { return url } }, + created() { + getProfile().then(res => { + this.object = res + this.$store.commit('users/SET_PROFILE', res) + }) + }, methods: { updateUserReceiveBackends(val) { this.$axios.patch( diff --git a/src/views/sessions/SessionDetail/SessionDetailInfo.vue b/src/views/sessions/SessionDetail/SessionDetailInfo.vue index 1bb503e75..4dc3315b8 100644 --- a/src/views/sessions/SessionDetail/SessionDetailInfo.vue +++ b/src/views/sessions/SessionDetail/SessionDetailInfo.vue @@ -49,7 +49,7 @@ export default { const msg = vm.$t('sessions.TerminateTaskSendSuccessMsg') vm.$message.success(msg) window.setTimeout(function() { - window.location.reload() + this.$store.commit('common/reload') }, 50000) }) } diff --git a/src/views/settings/Interface.vue b/src/views/settings/Interface.vue index b5b7b8800..2b1e7aeac 100644 --- a/src/views/settings/Interface.vue +++ b/src/views/settings/Interface.vue @@ -175,7 +175,7 @@ export default { }).then(() => { restoreInterface().then(res => { this.$message.success(res.success) - location.reload() + this.$store.commit('common/reload') }) }) }.bind(this) @@ -221,7 +221,7 @@ export default { } } updateInterface(form).then(res => { - location.reload() + this.$store.commit('common/reload') }) } } diff --git a/src/views/settings/License.vue b/src/views/settings/License.vue index d3d9c19a5..c485534a4 100644 --- a/src/views/settings/License.vue +++ b/src/views/settings/License.vue @@ -140,6 +140,7 @@ export default { window.open(url, '_blank') }, importLicense() { + const vm = this if (this.licenseFile['file'] === undefined) { return } @@ -148,7 +149,7 @@ export default { importLicense(formData).then(res => { if (res.status) { this.$message.success(res.msg) - setTimeout(() => location.reload(), 500) + setTimeout(() => vm.$store.commit('common/reload'), 500) } else { this.$message.error(res.msg) } diff --git a/src/views/settings/Org/OrganizationList.vue b/src/views/settings/Org/OrganizationList.vue index 376a566d8..08139ce0c 100644 --- a/src/views/settings/Org/OrganizationList.vue +++ b/src/views/settings/Org/OrganizationList.vue @@ -60,6 +60,7 @@ export default { actions: { prop: 'id', formatterArgs: { + canUpdate: this.$hasPerm('orgs.change_organization'), canDelete: function({ row }) { return !row.is_default && vm.$hasPerm('orgs.delete_organization') }, diff --git a/src/views/settings/SMS/Base.vue b/src/views/settings/SMS/Base.vue index 2694bf21e..9f7651d59 100644 --- a/src/views/settings/SMS/Base.vue +++ b/src/views/settings/SMS/Base.vue @@ -12,7 +12,7 @@ v-on="$listeners" @confirm="onConfirm()" > - + @@ -52,6 +52,16 @@ export default { submitSuccess(res) { this.$emit('input', !!res[this.enableField]) this.visible = false + }, + testPerformError(error) { + const data = error.response.data + for (const key of Object.keys(data)) { + let value = data[key] + if (value instanceof Array) { + value = value.join(';') + } + this.$refs.form.$refs.form.setFieldError(key, value) + } } } } diff --git a/src/views/settings/SMS/CMPP2.vue b/src/views/settings/SMS/CMPP2.vue index d43f226e0..78f4f56a8 100644 --- a/src/views/settings/SMS/CMPP2.vue +++ b/src/views/settings/SMS/CMPP2.vue @@ -1,5 +1,5 @@