mirror of
https://github.com/jumpserver/lina.git
synced 2025-07-18 01:01:31 +00:00
commit
00cf2c34b9
@ -46,6 +46,7 @@
|
|||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
"lodash.topairs": "^4.3.0",
|
"lodash.topairs": "^4.3.0",
|
||||||
"lodash.values": "^4.3.0",
|
"lodash.values": "^4.3.0",
|
||||||
|
"moment": "^2.29.1",
|
||||||
"moment-parseformat": "^3.0.0",
|
"moment-parseformat": "^3.0.0",
|
||||||
"normalize.css": "7.0.0",
|
"normalize.css": "7.0.0",
|
||||||
"npm": "^7.8.0",
|
"npm": "^7.8.0",
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.2 KiB |
@ -8,7 +8,6 @@
|
|||||||
<meta http-equiv="Cache-control" content="no-cache">
|
<meta http-equiv="Cache-control" content="no-cache">
|
||||||
<meta http-equiv="Cache" content="no-cache">
|
<meta http-equiv="Cache" content="no-cache">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
|
||||||
<title><%= webpackConfig.name %></title>
|
<title><%= webpackConfig.name %></title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB |
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
|
<ListTable ref="ListTable" :table-config="iTableConfig" :header-actions="headerActions" />
|
||||||
<Dialog v-if="showMFADialog" width="50" :title="this.$t('common.MFAConfirm')" :visible.sync="showMFADialog" :show-confirm="false" :show-cancel="false" :destroy-on-close="true">
|
<Dialog v-if="showMFADialog" width="50" :title="this.$t('common.MFAConfirm')" :visible.sync="showMFADialog" :show-confirm="false" :show-cancel="false" :destroy-on-close="true">
|
||||||
<div v-if="MFAConfirmed">
|
<div v-if="MFAConfirmed">
|
||||||
<el-form label-position="right" label-width="80px" :model="MFAInfo">
|
<el-form label-position="right" label-width="80px" :model="MFAInfo">
|
||||||
@ -93,6 +93,10 @@ export default {
|
|||||||
hasClone: {
|
hasClone: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
tableConfig: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -114,36 +118,31 @@ export default {
|
|||||||
password: '',
|
password: '',
|
||||||
private_key: ''
|
private_key: ''
|
||||||
},
|
},
|
||||||
tableConfig: {
|
defaultTableConfig: {
|
||||||
url: this.url,
|
url: this.url,
|
||||||
columns: [
|
columns: ['hostname', 'ip', 'username', 'version', 'date_created', 'actions'],
|
||||||
{
|
columnsMeta: {
|
||||||
prop: 'hostname',
|
'hostname': {
|
||||||
label: this.$t('assets.Hostname'),
|
label: this.$t('assets.Hostname'),
|
||||||
showOverflowTooltip: true
|
showOverflowTooltip: true
|
||||||
},
|
},
|
||||||
{
|
'ip': {
|
||||||
prop: 'ip',
|
|
||||||
label: this.$t('assets.ip'),
|
label: this.$t('assets.ip'),
|
||||||
width: '120px'
|
width: '120px'
|
||||||
},
|
},
|
||||||
{
|
'username': {
|
||||||
prop: 'username',
|
|
||||||
label: this.$t('assets.Username'),
|
label: this.$t('assets.Username'),
|
||||||
showOverflowTooltip: true
|
showOverflowTooltip: true
|
||||||
},
|
},
|
||||||
{
|
'version': {
|
||||||
prop: 'version',
|
|
||||||
label: this.$t('assets.Version'),
|
label: this.$t('assets.Version'),
|
||||||
width: '70px'
|
width: '70px'
|
||||||
},
|
},
|
||||||
{
|
'date_created': {
|
||||||
prop: 'date_created',
|
|
||||||
label: this.$t('assets.date_joined'),
|
label: this.$t('assets.date_joined'),
|
||||||
formatter: DateFormatter
|
formatter: DateFormatter
|
||||||
},
|
},
|
||||||
{
|
'actions': {
|
||||||
prop: 'id',
|
|
||||||
label: this.$t('common.Action'),
|
label: this.$t('common.Action'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 150,
|
width: 150,
|
||||||
@ -211,7 +210,7 @@ export default {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
extraQuery: {
|
extraQuery: {
|
||||||
latest: 1
|
latest: 1
|
||||||
}
|
}
|
||||||
@ -256,16 +255,22 @@ export default {
|
|||||||
const ttl = this.publicSettings.SECURITY_MFA_VERIFY_TTL
|
const ttl = this.publicSettings.SECURITY_MFA_VERIFY_TTL
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
return !(this.MFAVerifyAt && (now - this.MFAVerifyAt) < ttl * 1000)
|
return !(this.MFAVerifyAt && (now - this.MFAVerifyAt) < ttl * 1000)
|
||||||
|
},
|
||||||
|
iTableConfig() {
|
||||||
|
const columnsMeta = Object.assign({}, this.defaultTableConfig.columnsMeta, this.tableConfig.columnsMeta || {})
|
||||||
|
const config = Object.assign(this.defaultTableConfig, this.tableConfig)
|
||||||
|
config.columnsMeta = columnsMeta
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
url(iNew) {
|
url(iNew) {
|
||||||
this.$set(this.tableConfig, 'url', iNew)
|
this.$set(this.iTableConfig, 'url', iNew)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.otherActions) {
|
if (this.otherActions) {
|
||||||
const actionColumn = this.tableConfig.columns[this.tableConfig.columns.length - 1]
|
const actionColumn = this.iTableConfig.columns[this.iTableConfig.columns.length - 1]
|
||||||
for (const item of this.otherActions) {
|
for (const item of this.otherActions) {
|
||||||
actionColumn.formatterArgs.extraActions.push(item)
|
actionColumn.formatterArgs.extraActions.push(item)
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,11 @@
|
|||||||
class="action-item"
|
class="action-item"
|
||||||
@click="handleClick(action)"
|
@click="handleClick(action)"
|
||||||
>
|
>
|
||||||
<el-tooltip v-if="action.tip" effect="dark" :content="action.tip" placement="top">
|
<el-tooltip :disabled="!action.tip" :content="action.tip" placement="top">
|
||||||
<i v-if="action.fa" :class="'fa ' + action.fa" />{{ action.title }}
|
<span>
|
||||||
|
<i v-if="action.fa" :class="'fa ' + action.fa" />{{ action.title }}
|
||||||
|
</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span v-else>
|
|
||||||
<i v-if="action.fa" :class="'fa ' + action.fa" />{{ action.title }}
|
|
||||||
</span>
|
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
padding-right: 50px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -207,7 +207,7 @@ export default {
|
|||||||
if (!d) {
|
if (!d) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if (!itemColData || !itemColData.length) {
|
if (typeof itemColData !== 'number' && (!itemColData || !itemColData.length)) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return itemColData.length
|
return itemColData.length
|
||||||
|
@ -144,10 +144,11 @@ export default {
|
|||||||
let actions = [...this.defaultActions, ...this.extraActions]
|
let actions = [...this.defaultActions, ...this.extraActions]
|
||||||
actions = _.cloneDeep(actions)
|
actions = _.cloneDeep(actions)
|
||||||
actions = actions.map((v) => {
|
actions = actions.map((v) => {
|
||||||
v.has = this.cleanBoolean(v, 'has')
|
v.has = this.cleanBoolean(v, 'has', true)
|
||||||
v.can = this.cleanBoolean(v, 'can')
|
v.can = this.cleanBoolean(v, 'can', true)
|
||||||
v.callback = this.cleanCallback(v)
|
v.callback = this.cleanCallback(v, 'callback')
|
||||||
v.order = v.order || 100
|
v.order = v.order || 100
|
||||||
|
v.tip = this.cleanValue(v, 'tip')
|
||||||
return v
|
return v
|
||||||
})
|
})
|
||||||
actions = actions.filter((v) => v.has)
|
actions = actions.filter((v) => v.has)
|
||||||
@ -168,15 +169,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
cleanBoolean(item, attr) {
|
cleanBoolean(item, attr, defaults) {
|
||||||
const ok = item[attr]
|
const ok = item[attr]
|
||||||
if (typeof ok !== 'function') {
|
if (typeof ok !== 'function') {
|
||||||
return ok === undefined ? true : ok
|
return ok === undefined ? defaults : ok
|
||||||
}
|
}
|
||||||
return ok(this.row, this.cellValue)
|
return this.cleanValue(item, attr)
|
||||||
},
|
},
|
||||||
cleanCallback(item) {
|
cleanCallback(item, attr) {
|
||||||
const callback = item.callback
|
const callback = item[attr]
|
||||||
const attrs = {
|
const attrs = {
|
||||||
reload: this.reload,
|
reload: this.reload,
|
||||||
row: this.row,
|
row: this.row,
|
||||||
@ -185,6 +186,20 @@ export default {
|
|||||||
tableData: this.tableData
|
tableData: this.tableData
|
||||||
}
|
}
|
||||||
return () => { return callback.bind(this)(attrs) }
|
return () => { return callback.bind(this)(attrs) }
|
||||||
|
},
|
||||||
|
cleanValue(item, attr) {
|
||||||
|
const value = item[attr]
|
||||||
|
if (!value || typeof value !== 'function') {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
const attrs = {
|
||||||
|
reload: this.reload,
|
||||||
|
row: this.row,
|
||||||
|
col: this.col,
|
||||||
|
cellValue: this.cellValue,
|
||||||
|
tableData: this.tableData
|
||||||
|
}
|
||||||
|
return value(attrs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,6 +522,9 @@
|
|||||||
},
|
},
|
||||||
"route": {
|
"route": {
|
||||||
"": "",
|
"": "",
|
||||||
|
"Accounts": "账号管理",
|
||||||
|
"AssetAccount": "资产账号",
|
||||||
|
"ApplicationAccount": "应用账号",
|
||||||
"Ticket":"工单",
|
"Ticket":"工单",
|
||||||
"CommandConfirm": "命令复核",
|
"CommandConfirm": "命令复核",
|
||||||
"AdminUserCreate": "创建管理用户",
|
"AdminUserCreate": "创建管理用户",
|
||||||
@ -646,7 +649,8 @@
|
|||||||
"Users": "用户管理",
|
"Users": "用户管理",
|
||||||
"WebFTP": "文件管理",
|
"WebFTP": "文件管理",
|
||||||
"WebTerminal": "Web终端",
|
"WebTerminal": "Web终端",
|
||||||
"Notifications": "通知"
|
"Notifications": "通知",
|
||||||
|
"SiteMessageList": "站内信"
|
||||||
},
|
},
|
||||||
"sessions": {
|
"sessions": {
|
||||||
"StorageConfiguration": "存储配置",
|
"StorageConfiguration": "存储配置",
|
||||||
@ -709,6 +713,7 @@
|
|||||||
"common": "普通"
|
"common": "普通"
|
||||||
},
|
},
|
||||||
"Monitor": "监控",
|
"Monitor": "监控",
|
||||||
|
"XRDPNotSupport": "RDP 客户端会话, 暂不支持监控",
|
||||||
"sessionMonitor": "监控",
|
"sessionMonitor": "监控",
|
||||||
"TerminateTaskSendSuccessMsg": "终断任务已下发,请稍后刷新查看",
|
"TerminateTaskSendSuccessMsg": "终断任务已下发,请稍后刷新查看",
|
||||||
"helpText": {
|
"helpText": {
|
||||||
@ -1031,7 +1036,15 @@
|
|||||||
"MessageType": "消息类型",
|
"MessageType": "消息类型",
|
||||||
"Receivers": "接收人",
|
"Receivers": "接收人",
|
||||||
"Subscription": "消息订阅",
|
"Subscription": "消息订阅",
|
||||||
"ChangeReceiver": "修改消息接收人"
|
"ChangeReceiver": "修改消息接收人",
|
||||||
|
"Subject": "主题",
|
||||||
|
"Message": "消息",
|
||||||
|
"DeliveryTime": "发送时间",
|
||||||
|
"HasRead": "是否已读",
|
||||||
|
"Sender": "发送人",
|
||||||
|
"MarkAsRead": "标记已读",
|
||||||
|
"NoUnreadMsg": "暂无未读消息",
|
||||||
|
"SiteMessage": "站内信"
|
||||||
},
|
},
|
||||||
"xpack": {
|
"xpack": {
|
||||||
"Admin": "管理员",
|
"Admin": "管理员",
|
||||||
|
@ -520,6 +520,9 @@
|
|||||||
},
|
},
|
||||||
"route": {
|
"route": {
|
||||||
"": "",
|
"": "",
|
||||||
|
"Accounts": "Accounts",
|
||||||
|
"AssetAccount": "Asset account",
|
||||||
|
"ApplicationAccount": "Application account",
|
||||||
"Ticket": "Tickets",
|
"Ticket": "Tickets",
|
||||||
"CommandConfirm": "Command confirm",
|
"CommandConfirm": "Command confirm",
|
||||||
"AdminUserCreate": "Admin user create",
|
"AdminUserCreate": "Admin user create",
|
||||||
@ -644,7 +647,8 @@
|
|||||||
"Users": "Users",
|
"Users": "Users",
|
||||||
"WebFTP": "WebFTP",
|
"WebFTP": "WebFTP",
|
||||||
"WebTerminal": "Web Terminal",
|
"WebTerminal": "Web Terminal",
|
||||||
"Notifications": "Notifications"
|
"Notifications": "Notifications",
|
||||||
|
"SiteMessageList": "Site message"
|
||||||
},
|
},
|
||||||
"sessions": {
|
"sessions": {
|
||||||
"StorageConfiguration": "Storage configuration",
|
"StorageConfiguration": "Storage configuration",
|
||||||
@ -708,6 +712,7 @@
|
|||||||
},
|
},
|
||||||
"Monitor": "Monitor",
|
"Monitor": "Monitor",
|
||||||
"sessionMonitor": "Session Monitor",
|
"sessionMonitor": "Session Monitor",
|
||||||
|
"XRDPNotSupport": "RDP Client session not support now",
|
||||||
"TerminateTaskSendSuccessMsg": "Terminate task has been send, Please check later",
|
"TerminateTaskSendSuccessMsg": "Terminate task has been send, Please check later",
|
||||||
"helpText": {
|
"helpText": {
|
||||||
"esUrl": "Tip: If you have multiple hosts, use comma (,) to split (eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com)",
|
"esUrl": "Tip: If you have multiple hosts, use comma (,) to split (eg: http://www.jumpserver.a.com,http://www.jumpserver.b.com)",
|
||||||
@ -1025,7 +1030,15 @@
|
|||||||
"MessageType": "Message Type",
|
"MessageType": "Message Type",
|
||||||
"Receivers": "Receivers",
|
"Receivers": "Receivers",
|
||||||
"Subscription": "Subscription",
|
"Subscription": "Subscription",
|
||||||
"ChangeReceiver": "Change Receivers"
|
"ChangeReceiver": "Change Receivers",
|
||||||
|
"Subject": "Subject",
|
||||||
|
"Message": "Message",
|
||||||
|
"DeliveryTime": "Delivery time",
|
||||||
|
"HasRead": "Has read",
|
||||||
|
"Sender": "Sender",
|
||||||
|
"MarkAsRead": "Mark as read",
|
||||||
|
"NoUnreadMsg": "No unread messages",
|
||||||
|
"SiteMessage": "Site messages"
|
||||||
},
|
},
|
||||||
"xpack": {
|
"xpack": {
|
||||||
"Admin": "Admin",
|
"Admin": "Admin",
|
||||||
|
@ -2,13 +2,10 @@
|
|||||||
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
|
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
|
||||||
<transition name="sidebarLogoFade">
|
<transition name="sidebarLogoFade">
|
||||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||||
<img v-if="logo" :src="logo" class="sidebar-logo">
|
<img :src="logoSrc" class="sidebar-logo">
|
||||||
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||||
<img :src="logoSrc" class="sidebar-logo-text">
|
<img :src="logoTextSrc" class="sidebar-logo-text">
|
||||||
<!-- <img v-else-if="logoText" :src="logoText" class="sidebar-logo-text">-->
|
|
||||||
<!-- <h1 class="sidebar-title">{{ title }}</h1>-->
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@ -26,10 +23,6 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
title: 'JumpServer',
|
|
||||||
logoText: require('@/assets/img/logo-text.png'),
|
|
||||||
logo: require('@/assets/img/logo.png'),
|
|
||||||
xpackData: {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -37,16 +30,14 @@ export default {
|
|||||||
'publicSettings'
|
'publicSettings'
|
||||||
]),
|
]),
|
||||||
// eslint-disable-next-line vue/return-in-computed-property
|
// eslint-disable-next-line vue/return-in-computed-property
|
||||||
|
logoTextSrc() {
|
||||||
|
return this.publicSettings.LOGO_URLS.logo_index
|
||||||
|
},
|
||||||
logoSrc() {
|
logoSrc() {
|
||||||
if (this.publicSettings.LOGO_URLS.logo_index !== '/static/img/logo_text.png') {
|
return this.publicSettings.LOGO_URLS.logo_logout
|
||||||
return this.publicSettings.LOGO_URLS.logo_index
|
|
||||||
} else {
|
|
||||||
return this.logoText
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Language',
|
name: 'Language',
|
||||||
data() {
|
data() {
|
||||||
@ -47,11 +46,22 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.currentLang.code !== this.$i18n.locale) {
|
this.changeLang()
|
||||||
this.changeLangTo(this.currentLang)
|
this.changeMomentLang()
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
changeLang() {
|
||||||
|
if (this.currentLang.code !== this.$i18n.locale) {
|
||||||
|
this.changeLangTo(this.currentLang)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeMomentLang() {
|
||||||
|
if (this.currentLang.code.indexOf('en') > -1) {
|
||||||
|
this.$moment.locale('en')
|
||||||
|
} else {
|
||||||
|
this.$moment.locale('zh-cn')
|
||||||
|
}
|
||||||
|
},
|
||||||
changeLangTo(item) {
|
changeLangTo(item) {
|
||||||
this.$i18n.locale = item.code
|
this.$i18n.locale = item.code
|
||||||
localStorage.setItem('lang', item.code)
|
localStorage.setItem('lang', item.code)
|
||||||
|
262
src/layout/components/NavHeader/SiteMessages.vue
Normal file
262
src/layout/components/NavHeader/SiteMessages.vue
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-badge :value="unreadMsgCount" :hidden="unreadMsgCount === 0" :max="99" size="mini" type="primary">
|
||||||
|
<a style="color: #606266 !important; width: 30px" @click="toggleDrawer">
|
||||||
|
<i class="el-icon-message" style="font-size: 18px" />
|
||||||
|
</a>
|
||||||
|
</el-badge>
|
||||||
|
<el-drawer
|
||||||
|
:visible.sync="show"
|
||||||
|
:before-close="handleClose"
|
||||||
|
:modal="false"
|
||||||
|
:title="$t('notifications.SiteMessage')"
|
||||||
|
custom-class="site-msg"
|
||||||
|
size="25%"
|
||||||
|
@open="getMessages"
|
||||||
|
>
|
||||||
|
<div v-if="unreadMsgCount !== 0" class="msg-list">
|
||||||
|
<div
|
||||||
|
v-for="msg of messages"
|
||||||
|
:key="msg.id"
|
||||||
|
class="msg-item"
|
||||||
|
:class="msg.has_read ? 'msg-read' : 'msg-unread'"
|
||||||
|
@mouseover="hoverMsgId = msg.id"
|
||||||
|
@mouseleave="hoverMsgId = ''"
|
||||||
|
@click="showMsgDetail(msg)"
|
||||||
|
>
|
||||||
|
<div class="msg-item-head">
|
||||||
|
<span class="msg-item-head-type">
|
||||||
|
<i :class="msg.has_read ? 'fa-envelope-open-o' : 'fa-envelope'" class="fa msg-icon" />
|
||||||
|
{{ msg.subject }}
|
||||||
|
</span>
|
||||||
|
<span v-if="hoverMsgId !== msg.id || msg.has_read" class="msg-item-head-time">
|
||||||
|
{{ formatDate(msg.date_created) }}
|
||||||
|
</span>
|
||||||
|
<div v-else class="msg-item-read-btn" @click.stop="markAsRead(msg)">
|
||||||
|
<a>{{ $t('notifications.MarkAsRead') }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="msg-item-txt">
|
||||||
|
<span v-html="msg.message" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="no-msg">
|
||||||
|
{{ $t('notifications.NoUnreadMsg') }}
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
v-if="msgDetailVisible"
|
||||||
|
:visible.sync="msgDetailVisible"
|
||||||
|
:title="''"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:confirm-title="$t('notifications.MarkAsRead')"
|
||||||
|
@confirm="markAsRead(currentMsg)"
|
||||||
|
@cancel="cancelRead"
|
||||||
|
>
|
||||||
|
<div class="msg-detail">
|
||||||
|
<div class="msg-detail-head">
|
||||||
|
<h3>{{ currentMsg.subject }}</h3>
|
||||||
|
<h5>
|
||||||
|
<span class="msg-detail-time">{{ formatDate(currentMsg.date_created) }}</span>
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div class="msg-detail-txt">
|
||||||
|
<span v-html="currentMsg.message" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { toSafeLocalDateStr } from '@/utils/common'
|
||||||
|
import Dialog from '@/components/Dialog'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SiteMessages',
|
||||||
|
components: { Dialog },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: false,
|
||||||
|
messages: [],
|
||||||
|
hoverMsgId: '',
|
||||||
|
msgDetailVisible: false,
|
||||||
|
currentMsg: null,
|
||||||
|
unreadMsgCount: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.enablePullMsgCount()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClose() {
|
||||||
|
this.show = false
|
||||||
|
},
|
||||||
|
toggleDrawer() {
|
||||||
|
this.show = !this.show
|
||||||
|
},
|
||||||
|
showMsgDetail(msg) {
|
||||||
|
this.currentMsg = msg
|
||||||
|
this.msgDetailVisible = true
|
||||||
|
},
|
||||||
|
getMessages() {
|
||||||
|
const url = '/api/v1/notifications/site-message/?offset=0&limit=15&has_read=false'
|
||||||
|
this.$axios.get(url).then(resp => {
|
||||||
|
this.messages = [...resp.results]
|
||||||
|
this.unreadMsgCount = resp.count
|
||||||
|
})
|
||||||
|
},
|
||||||
|
formatDate(s) {
|
||||||
|
if (!s) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const d = new Date(s)
|
||||||
|
const now = new Date()
|
||||||
|
if (now.getTime() - d.getTime() > (3600 * 24 * 7) * 1000) {
|
||||||
|
return toSafeLocalDateStr(s)
|
||||||
|
} else {
|
||||||
|
return this.$moment(d).fromNow()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
markAsRead(msg) {
|
||||||
|
console.log(`${msg}`)
|
||||||
|
const url = `/api/v1/notifications/site-message/mark-as-read/`
|
||||||
|
this.$axios.patch(url, { ids: [msg.id] }).then(res => {
|
||||||
|
this.msgDetailVisible = false
|
||||||
|
this.getMessages()
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message(err.detail)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
cancelRead() {
|
||||||
|
this.msgDetailVisible = false
|
||||||
|
},
|
||||||
|
pullMsgCount() {
|
||||||
|
const url = '/api/v1/notifications/site-message/unread-total/'
|
||||||
|
this.$axios.get(url).then(res => {
|
||||||
|
this.unreadMsgCount = res.total
|
||||||
|
}).catch(err => {
|
||||||
|
this.$message(err.detail)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
enablePullMsgCount() {
|
||||||
|
this.pullMsgCount()
|
||||||
|
setInterval(() => {
|
||||||
|
this.pullMsgCount()
|
||||||
|
}, 10000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-badge ::v-deep .el-badge__content.is-fixed{
|
||||||
|
top:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-list {
|
||||||
|
padding: 0 25px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>>> .site-msg {
|
||||||
|
.el-drawer__header {
|
||||||
|
border-bottom: solid 1px rgb(231, 234, 239);
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-top: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer__body {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-item {
|
||||||
|
border-bottom: solid 1px rgb(231, 234, 239);
|
||||||
|
padding: 15px 0 10px;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
padding: 15px 20px 10px;
|
||||||
|
margin: 0 -20px;
|
||||||
|
border-bottom: 1px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-icon {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.msg-unread {
|
||||||
|
.msg-item-txt {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-item-head {
|
||||||
|
line-height: 20px;
|
||||||
|
color: #888;
|
||||||
|
font-size: 12px;
|
||||||
|
&:after {
|
||||||
|
clear: both;
|
||||||
|
content: ".";
|
||||||
|
display: block;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-item-head-type {
|
||||||
|
float: left;
|
||||||
|
width: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.msg-item-head-time {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.msg-item-read-btn {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-item-txt {
|
||||||
|
overflow: hidden;
|
||||||
|
color: #000;
|
||||||
|
padding: 4px 0 0;
|
||||||
|
line-height: 21px;
|
||||||
|
max-height: 21px;
|
||||||
|
display: -webkit-box;
|
||||||
|
font-size: 12px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-detail {
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
.msg-detail-time {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-detail-txt {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-msg {
|
||||||
|
padding-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
>>> :focus{ outline:0; }
|
||||||
|
</style>
|
@ -3,30 +3,26 @@
|
|||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-right">
|
<ul class="navbar-right">
|
||||||
<div class="header-item">
|
<li class="header-item header-icon">
|
||||||
|
<SiteMessages />
|
||||||
|
</li>
|
||||||
|
<li class="header-item" style="margin-left: 10px">
|
||||||
<Help />
|
<Help />
|
||||||
</div>
|
</li>
|
||||||
<div class="header-item">
|
<li class="header-item">
|
||||||
<Language />
|
<Language />
|
||||||
</div>
|
</li>
|
||||||
<div
|
<li v-if="showTickets" class="header-item">
|
||||||
v-if="
|
|
||||||
publicSettings.TICKETS_ENABLED
|
|
||||||
&& publicSettings.XPACK_LICENSE_IS_VALID
|
|
||||||
&& !isOrgAuditor
|
|
||||||
"
|
|
||||||
class="header-item"
|
|
||||||
>
|
|
||||||
<Tickets />
|
<Tickets />
|
||||||
</div>
|
</li>
|
||||||
<div class="header-item">
|
<li class="header-item">
|
||||||
<WebTerminal />
|
<WebTerminal />
|
||||||
</div>
|
</li>
|
||||||
<div class="header-item header-profile">
|
<li class="header-item header-profile">
|
||||||
<AccountDropdown />
|
<AccountDropdown />
|
||||||
</div>
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -34,6 +30,7 @@
|
|||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import Hamburger from '@/components/Hamburger'
|
import Hamburger from '@/components/Hamburger'
|
||||||
import AccountDropdown from './AccountDropdown'
|
import AccountDropdown from './AccountDropdown'
|
||||||
|
import SiteMessages from './SiteMessages'
|
||||||
import Help from './Help'
|
import Help from './Help'
|
||||||
import Language from './Language'
|
import Language from './Language'
|
||||||
import WebTerminal from './WebTerminal'
|
import WebTerminal from './WebTerminal'
|
||||||
@ -48,7 +45,8 @@ export default {
|
|||||||
Language,
|
Language,
|
||||||
Help,
|
Help,
|
||||||
Tickets,
|
Tickets,
|
||||||
WebTerminal
|
WebTerminal,
|
||||||
|
SiteMessages
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -60,13 +58,17 @@ export default {
|
|||||||
]),
|
]),
|
||||||
isOrgAuditor() {
|
isOrgAuditor() {
|
||||||
return rolc.getRolesDisplay(this.currentOrgRoles).includes('OrgAuditor') || rolc.getRolesDisplay(this.currentOrgRoles).includes('Auditor')
|
return rolc.getRolesDisplay(this.currentOrgRoles).includes('OrgAuditor') || rolc.getRolesDisplay(this.currentOrgRoles).includes('Auditor')
|
||||||
|
},
|
||||||
|
showTickets() {
|
||||||
|
return this.publicSettings.TICKETS_ENABLED &&
|
||||||
|
this.publicSettings.XPACK_LICENSE_IS_VALID &&
|
||||||
|
!this.isOrgAuditor
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleSideBar() {
|
toggleSideBar() {
|
||||||
this.$store.dispatch('app/toggleSideBar')
|
this.$store.dispatch('app/toggleSideBar')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -91,12 +93,19 @@ export default {
|
|||||||
.navbar-right {
|
.navbar-right {
|
||||||
float: right;
|
float: right;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
|
||||||
|
|
||||||
.header-item {
|
.header-item {
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-right: 20px;
|
padding-right: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-icon {
|
||||||
|
&:hover {
|
||||||
|
background-color: #e6e6e6;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-container {
|
.breadcrumb-container {
|
||||||
@ -108,5 +117,9 @@ export default {
|
|||||||
.el-header {
|
.el-header {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -40,8 +40,13 @@ Vue.config.productionTip = false
|
|||||||
import VueCookie from 'vue-cookie'
|
import VueCookie from 'vue-cookie'
|
||||||
Vue.use(VueCookie)
|
Vue.use(VueCookie)
|
||||||
window.$cookie = VueCookie
|
window.$cookie = VueCookie
|
||||||
import VueMoment from 'vue-moment'
|
|
||||||
Vue.use(VueMoment)
|
const moment = require('moment')
|
||||||
|
require('moment/locale/zh-cn')
|
||||||
|
Vue.use(require('vue-moment'), {
|
||||||
|
moment
|
||||||
|
})
|
||||||
|
|
||||||
// logger
|
// logger
|
||||||
import VueLogger from 'vuejs-logger'
|
import VueLogger from 'vuejs-logger'
|
||||||
import loggerOptions from './utils/logger'
|
import loggerOptions from './utils/logger'
|
||||||
|
120
src/router/accounts.js
Normal file
120
src/router/accounts.js
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import i18n from '@/i18n/i18n'
|
||||||
|
import empty from '@/layout/empty'
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
path: 'asset-accounts',
|
||||||
|
component: empty,
|
||||||
|
meta: { title: i18n.t('route.AssetAccount') },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'AssetAccountList',
|
||||||
|
component: () => import('@/views/accounts/AssetAccount/AssetAccountList'),
|
||||||
|
meta: { title: i18n.t('route.AssetAccount') }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'application-accounts',
|
||||||
|
component: empty,
|
||||||
|
meta: { title: i18n.t('route.AssetAccount') },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'ApplicationAccountList',
|
||||||
|
component: () => import('@/views/accounts/ApplicationAccount/ApplicationAccountList'),
|
||||||
|
meta: { title: i18n.t('route.ApplicationAccount') }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'gathered-user',
|
||||||
|
component: empty,
|
||||||
|
redirect: '',
|
||||||
|
meta: { title: i18n.t('xpack.GatherUser.GatherUserList') },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: () => import('@/views/accounts/GatheredUser/index'),
|
||||||
|
name: 'GatherUserListIndex',
|
||||||
|
meta: { title: i18n.t('xpack.GatherUser.GatherUser'), activeMenu: '/accounts/gathered-user' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: () => import('@/views/accounts/GatheredUser/GatheredUserList'),
|
||||||
|
name: 'GatherUserList',
|
||||||
|
hidden: true,
|
||||||
|
meta: { title: i18n.t('xpack.GatherUser.GatherUserList'), activeMenu: '/accounts/gathered-user' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tasks',
|
||||||
|
component: () => import('@/views/accounts/GatheredUser/TaskList'),
|
||||||
|
name: 'GatherUserTaskList',
|
||||||
|
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskList'), activeMenu: '/accounts/gathered-user' },
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tasks/:id',
|
||||||
|
component: () => import('@/views/accounts/GatheredUser/TaskDetail/index'),
|
||||||
|
name: 'GatherUserTaskDetail',
|
||||||
|
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskDetail'), activeMenu: '/accounts/gathered-user' },
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tasks/create',
|
||||||
|
component: () => import('@/views/accounts/GatheredUser/TaskCreateUpdate'),
|
||||||
|
name: 'GatherUserTaskCreate',
|
||||||
|
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskCreate'), activeMenu: '/accounts/gathered-user' },
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tasks/:id/update',
|
||||||
|
component: () => import('@/views/accounts/GatheredUser/TaskCreateUpdate'),
|
||||||
|
name: 'GatherUserTaskUpdate',
|
||||||
|
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskUpdate'), action: 'update', activeMenu: '/accounts/gathered-user' },
|
||||||
|
hidden: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'change-auth-plan',
|
||||||
|
component: empty,
|
||||||
|
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'), activeMenu: '/accounts/change-auth-plan/plan' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'plan',
|
||||||
|
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanList.vue'),
|
||||||
|
name: 'ChangeAuthPlanList',
|
||||||
|
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'), activeMenu: '/accounts/change-auth-plan/plan' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'plan/create',
|
||||||
|
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanCreateUpdate.vue'),
|
||||||
|
name: 'ChangeAuthPlanCreate',
|
||||||
|
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlanCreate'), activeMenu: '/accounts/change-auth-plan/plan', action: 'create' },
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'plan/:id/update',
|
||||||
|
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanCreateUpdate.vue'),
|
||||||
|
name: 'ChangeAuthPlanUpdate',
|
||||||
|
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlanUpdate'), activeMenu: '/accounts/change-auth-plan/plan', action: 'update' },
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'plan/:id',
|
||||||
|
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanDetail/index.vue'),
|
||||||
|
name: 'ChangeAuthPlanDetail',
|
||||||
|
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'), activeMenu: '/accounts/change-auth-plan/plan' },
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'plan-execution/:id',
|
||||||
|
component: () => import('@/views/accounts/ChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanExecution/ChangeAuthPlanExecutionDetail/index.vue'),
|
||||||
|
name: 'ChangeAuthPlanExecutionDetail',
|
||||||
|
meta: { title: i18n.t('xpack.ChangeAuthPlan.ExecutionDetail'), activeMenu: '/accounts/change-auth-plan/plan' },
|
||||||
|
hidden: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
@ -37,6 +37,7 @@ import TicketsRoutes from './tickets'
|
|||||||
import AuditsRoutes from './audits'
|
import AuditsRoutes from './audits'
|
||||||
import commonRoutes from './common'
|
import commonRoutes from './common'
|
||||||
import aclRoutes from './acl'
|
import aclRoutes from './acl'
|
||||||
|
import AccountRoutes from './accounts'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* constantRoutes
|
* constantRoutes
|
||||||
@ -110,6 +111,18 @@ export const allRoleRoutes = [
|
|||||||
meta: { title: i18n.t('route.Applications'), icon: 'th' },
|
meta: { title: i18n.t('route.Applications'), icon: 'th' },
|
||||||
children: ApplicationsRoute
|
children: ApplicationsRoute
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/accounts',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/accounts/asset-accounts/',
|
||||||
|
name: 'Accounts',
|
||||||
|
meta: {
|
||||||
|
licenseRequired: true,
|
||||||
|
title: i18n.t('route.Accounts'),
|
||||||
|
icon: 'address-book'
|
||||||
|
},
|
||||||
|
children: AccountRoutes
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/perms/',
|
path: '/perms/',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
title: '.',
|
||||||
title: 'JumpServer',
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {boolean} true | false
|
* @type {boolean} true | false
|
||||||
|
@ -35,15 +35,18 @@ const actions = {
|
|||||||
getPublicSettings({ commit, state }) {
|
getPublicSettings({ commit, state }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getPublicSettings().then(response => {
|
getPublicSettings().then(response => {
|
||||||
const link = document.querySelector("link[rel*='icon']") || document.createElement('link')
|
const faviconURL = response.data.LOGO_URLS.favicon
|
||||||
link.type = 'image/x-icon'
|
let link = document.querySelector("link[rel*='icon']")
|
||||||
link.rel = 'shortcut icon'
|
if (!link) {
|
||||||
link.href = response.data.LOGO_URLS.favicon
|
link = document.createElement('link')
|
||||||
document.getElementsByTagName('head')[0].appendChild(link)
|
link.type = 'image/x-icon'
|
||||||
|
link.rel = 'shortcut icon'
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(link)
|
||||||
|
}
|
||||||
|
link.href = faviconURL
|
||||||
|
|
||||||
// 动态修改Title
|
// 动态修改Title
|
||||||
if (response.data.LOGIN_TITLE) { document.title = response.data.LOGIN_TITLE }
|
document.title = response.data.LOGIN_TITLE
|
||||||
|
|
||||||
commit('SET_PUBLIC_SETTINGS', response.data)
|
commit('SET_PUBLIC_SETTINGS', response.data)
|
||||||
resolve(response)
|
resolve(response)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
@ -117,7 +117,7 @@ export default {
|
|||||||
name: 'connect',
|
name: 'connect',
|
||||||
fa: 'fa-terminal',
|
fa: 'fa-terminal',
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
can: (row, cellValue) => {
|
can: ({ row, cellValue }) => {
|
||||||
return row.is_active
|
return row.is_active
|
||||||
},
|
},
|
||||||
callback: function({ row, col, cellValue, reload }) {
|
callback: function({ row, col, cellValue, reload }) {
|
||||||
|
@ -88,11 +88,14 @@ export function toSafeLocalDateStr(d) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getApiPath(that) {
|
export function getApiPath(that) {
|
||||||
const pagePath = that.$route.path
|
let pagePath = that.$route.path
|
||||||
const isOrgPath = pagePath.split('/').indexOf('orgs') !== -1
|
const pagePathArray = pagePath.split('/')
|
||||||
if (isOrgPath) {
|
if (pagePathArray.indexOf('orgs') !== -1) {
|
||||||
return `/api/v1/orgs/orgs/${pagePath.split('/').pop()}/`
|
pagePathArray[pagePathArray.indexOf('xpack')] = 'orgs'
|
||||||
|
} else if (pagePathArray.indexOf('gathered-user') !== -1 || pagePathArray.indexOf('change-auth-plan') !== -1) {
|
||||||
|
pagePathArray[pagePathArray.indexOf('accounts')] = 'xpack'
|
||||||
}
|
}
|
||||||
|
pagePath = pagePathArray.join('/')
|
||||||
return `/api/v1${pagePath}/`
|
return `/api/v1${pagePath}/`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import getPageTitle from '@/utils/get-page-title'
|
// import getPageTitle from '@/utils/get-page-title'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import { Message } from 'element-ui'
|
import { Message } from 'element-ui'
|
||||||
@ -15,9 +15,9 @@ function reject(msg) {
|
|||||||
return new Promise((resolve, reject) => reject(msg))
|
return new Promise((resolve, reject) => reject(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
function setHeadTitle({ to, from, next }) {
|
// function setHeadTitle({ to, from, next }) {
|
||||||
document.title = getPageTitle(to.meta.title)
|
// document.title = getPageTitle(to.meta.title)
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function checkLogin({ to, from, next }) {
|
async function checkLogin({ to, from, next }) {
|
||||||
if (whiteList.indexOf(to.path) !== -1) {
|
if (whiteList.indexOf(to.path) !== -1) {
|
||||||
@ -145,7 +145,7 @@ export async function startup({ to, from, next }) {
|
|||||||
|
|
||||||
// set page title
|
// set page title
|
||||||
await getPublicSetting({ to, from, next })
|
await getPublicSetting({ to, from, next })
|
||||||
await setHeadTitle({ to, from, next })
|
// await setHeadTitle({ to, from, next })
|
||||||
await checkLogin({ to, from, next })
|
await checkLogin({ to, from, next })
|
||||||
await changeCurrentOrgIfNeed({ to, from, next })
|
await changeCurrentOrgIfNeed({ to, from, next })
|
||||||
await changeCurrentRoleIfNeed({ to, from, next })
|
await changeCurrentRoleIfNeed({ to, from, next })
|
||||||
|
247
src/views/accounts/ApplicationAccount/ApplicationAccountList.vue
Normal file
247
src/views/accounts/ApplicationAccount/ApplicationAccountList.vue
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
<template>
|
||||||
|
<Page>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="11">
|
||||||
|
<GenericListTable
|
||||||
|
ref="LeftTable"
|
||||||
|
class="application-table"
|
||||||
|
:header-actions="leftTable.headerActions"
|
||||||
|
:table-config="leftTable.tableConfig"
|
||||||
|
@row-click="leftTable.tableConfig.rowClick"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="13">
|
||||||
|
<GenericListTable
|
||||||
|
ref="RightTable"
|
||||||
|
class="application-user-table"
|
||||||
|
:header-actions="rightTable.headerActions"
|
||||||
|
:table-config="rightTable.tableConfig"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<Dialog v-if="showMFADialog" width="50" :title="this.$t('common.MFAConfirm')" :visible.sync="showMFADialog" :show-confirm="false" :show-cancel="false" :destroy-on-close="true">
|
||||||
|
<div v-if="MFAConfirmed">
|
||||||
|
<el-form label-position="right" label-width="80px" :model="MFAInfo">
|
||||||
|
<el-form-item :label="this.$t('assets.Applications')">
|
||||||
|
<el-input v-model="MFAInfo.application" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="this.$t('assets.Username')">
|
||||||
|
<el-input v-model="MFAInfo.username" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="this.$t('assets.Password')">
|
||||||
|
<el-input v-model="MFAInfo.password" type="password" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-row v-else :gutter="20">
|
||||||
|
<el-col :span="4">
|
||||||
|
<div style="line-height: 34px;text-align: center">MFA</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="14">
|
||||||
|
<el-input v-model="MFAInput" />
|
||||||
|
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<el-button size="mini" type="primary" style="line-height:20px " @click="MFAConfirm">{{ this.$t('common.Confirm') }}</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</Dialog>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Page from '@/layout/components/Page'
|
||||||
|
import GenericListTable from '@/layout/components/GenericListTable'
|
||||||
|
import { ActionsFormatter, DetailFormatter } from '@/components/TableFormatters'
|
||||||
|
import Dialog from '@/components/Dialog'
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AssetAccountList',
|
||||||
|
components: {
|
||||||
|
GenericListTable, Page, Dialog
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const vm = this
|
||||||
|
return {
|
||||||
|
showMFADialog: false,
|
||||||
|
MFAConfirmed: false,
|
||||||
|
MFAInput: '',
|
||||||
|
MFAInfo: {
|
||||||
|
systemUser: '',
|
||||||
|
application: '',
|
||||||
|
username: '',
|
||||||
|
password: ''
|
||||||
|
},
|
||||||
|
clickedRow: {},
|
||||||
|
leftTable: {
|
||||||
|
tableConfig: {
|
||||||
|
url: '/api/v1/applications/applications/',
|
||||||
|
columns: [
|
||||||
|
'name', 'category_display', 'type_display', 'comment'
|
||||||
|
],
|
||||||
|
columnsShow: {
|
||||||
|
min: ['name'],
|
||||||
|
default: ['name', 'category_display', 'type_display']
|
||||||
|
},
|
||||||
|
columnsMeta: {
|
||||||
|
name: {
|
||||||
|
formatter: DetailFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
getRoute({ row, col, cellValue }) {
|
||||||
|
return {
|
||||||
|
'db': 'DatabaseAppDetail', 'remote_app': 'RemoteAppDetail', 'cloud': 'KubernetesAppDetail'
|
||||||
|
}[row.category]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
sortable: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tableAttrs: {
|
||||||
|
stripe: false, // 斑马纹表格
|
||||||
|
border: true, // 表格边框
|
||||||
|
fit: true, // 宽度自适应,
|
||||||
|
tooltipEffect: 'dark',
|
||||||
|
rowClassName({ row, rowIndex }) {
|
||||||
|
if (row === vm.clickedRow) {
|
||||||
|
return 'row-clicked'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rowClick: function(row, column, event) {
|
||||||
|
vm.rightTable.tableConfig.url = `/api/v1/applications/application-users/?application_id=${row.id}`
|
||||||
|
vm.clickedRow = row
|
||||||
|
vm.MFAInfo.application = row.name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headerActions: {
|
||||||
|
hasLeftActions: false,
|
||||||
|
hasCreate: false,
|
||||||
|
hasExport: false,
|
||||||
|
hasImport: false,
|
||||||
|
hasBulkDelete: false,
|
||||||
|
hasBulkUpdate: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rightTable: {
|
||||||
|
tableConfig: {
|
||||||
|
url: `/api/v1/applications/application-users/?application_id=`,
|
||||||
|
columns: [
|
||||||
|
'name', 'username', 'username_same_with_user', 'protocol', 'login_mode', 'priority', 'comment', 'actions'
|
||||||
|
],
|
||||||
|
columnsShow: {
|
||||||
|
min: ['name', 'username', 'actions'],
|
||||||
|
default: ['name', 'username', 'date_created', 'actions']
|
||||||
|
},
|
||||||
|
columnsMeta: {
|
||||||
|
name: {
|
||||||
|
formatter: DetailFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
route: 'SystemUserDetail'
|
||||||
|
},
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
sortable: false
|
||||||
|
},
|
||||||
|
protocol: {
|
||||||
|
sortable: false
|
||||||
|
},
|
||||||
|
login_mode: {
|
||||||
|
sortable: false
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
label: this.$t('common.Action'),
|
||||||
|
align: 'center',
|
||||||
|
width: 150,
|
||||||
|
formatter: ActionsFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
hasUpdate: false, // can set function(row, value)
|
||||||
|
hasDelete: false, // can set function(row, value)
|
||||||
|
hasClone: false,
|
||||||
|
moreActionsTitle: this.$t('common.More'),
|
||||||
|
extraActions: [
|
||||||
|
{
|
||||||
|
name: 'View',
|
||||||
|
title: this.$t('common.View'),
|
||||||
|
type: 'primary',
|
||||||
|
callback: function(val) {
|
||||||
|
this.MFAInfo.systemUser = val.row
|
||||||
|
if (!this.needMFAVerify) {
|
||||||
|
this.showMFADialog = true
|
||||||
|
this.MFAConfirmed = true
|
||||||
|
this.$axios.get(`/api/v1/assets/system-users/${this.MFAInfo.systemUser.id}/auth-info/`).then(res => {
|
||||||
|
this.MFAConfirmed = true
|
||||||
|
this.MFAInfo.username = res.username
|
||||||
|
this.MFAInfo.password = res.password
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.showMFADialog = true
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tableAttrs: {
|
||||||
|
stripe: false, // 斑马纹表格
|
||||||
|
border: true, // 表格边框
|
||||||
|
fit: true, // 宽度自适应,
|
||||||
|
tooltipEffect: 'dark',
|
||||||
|
rowClassName({ row, rowIndex }) {
|
||||||
|
return 'row-background-color'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headerActions: {
|
||||||
|
hasLeftActions: false,
|
||||||
|
hasImport: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'MFA_TTl',
|
||||||
|
'MFAVerifyAt',
|
||||||
|
'publicSettings'
|
||||||
|
]),
|
||||||
|
needMFAVerify() {
|
||||||
|
if (!this.publicSettings.SECURITY_VIEW_AUTH_NEED_MFA) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const ttl = this.publicSettings.SECURITY_MFA_VERIFY_TTL
|
||||||
|
const now = new Date()
|
||||||
|
return !(this.MFAVerifyAt && (now - this.MFAVerifyAt) < ttl * 1000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
MFAConfirm() {
|
||||||
|
if (this.MFAInput.length !== 6) {
|
||||||
|
return this.$message.error(this.$t('common.MFAErrorMsg'))
|
||||||
|
}
|
||||||
|
this.$axios.post(
|
||||||
|
`/api/v1/authentication/otp/verify/`, {
|
||||||
|
code: this.MFAInput
|
||||||
|
}
|
||||||
|
).then(
|
||||||
|
res => {
|
||||||
|
this.$store.dispatch('users/setMFAVerify')
|
||||||
|
this.$axios.get(`/api/v1/assets/system-users/${this.MFAInfo.systemUser.id}/auth-info/`).then(res => {
|
||||||
|
this.MFAConfirmed = true
|
||||||
|
this.MFAInfo.username = res.username
|
||||||
|
this.MFAInfo.password = res.password
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.application-table ::v-deep .row-clicked, .application-user-table ::v-deep .row-background-color {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
</style>
|
176
src/views/accounts/AssetAccount/AssetAccountList.vue
Normal file
176
src/views/accounts/AssetAccount/AssetAccountList.vue
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<template>
|
||||||
|
<Page>
|
||||||
|
<el-row>
|
||||||
|
<el-col v-show="iShowTree" :span="iShowTree?4:0">
|
||||||
|
<AutoDataZTree
|
||||||
|
ref="AUtoDataZTree"
|
||||||
|
:setting="treeSetting"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="iShowTree?9:11">
|
||||||
|
<div class="mini">
|
||||||
|
<div style="display:block" class="mini-button" @click="iShowTree=!iShowTree">
|
||||||
|
<i v-show="iShowTree" class="fa fa-angle-left fa-x" />
|
||||||
|
<i v-show="!iShowTree" class="fa fa-angle-right fa-x" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<GenericListTable
|
||||||
|
ref="LeftTable"
|
||||||
|
class="asset-table"
|
||||||
|
:header-actions="leftTable.headerActions"
|
||||||
|
:table-config="leftTable.tableConfig"
|
||||||
|
@row-click="leftTable.tableConfig.rowClick"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="iShowTree?11:13">
|
||||||
|
<AssetUserTable
|
||||||
|
ref="RightTable"
|
||||||
|
class="asset-user-table"
|
||||||
|
:url="rightTable.url"
|
||||||
|
:has-left-actions="rightTable.hasLeftActions"
|
||||||
|
:table-config="rightTable.tableConfig"
|
||||||
|
:has-clone="false"
|
||||||
|
:has-import="false"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Page from '@/layout/components/Page'
|
||||||
|
import GenericListTable from '@/layout/components/GenericListTable'
|
||||||
|
import AutoDataZTree from '@/components/AutoDataZTree/index'
|
||||||
|
import { AssetUserTable } from '@/components'
|
||||||
|
import { DetailFormatter } from '@/components/TableFormatters'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AssetAccountList',
|
||||||
|
components: {
|
||||||
|
AutoDataZTree, GenericListTable, Page, AssetUserTable
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const vm = this
|
||||||
|
return {
|
||||||
|
clickedRow: {},
|
||||||
|
iShowTree: true,
|
||||||
|
treeSetting: {
|
||||||
|
showMenu: false,
|
||||||
|
showRefresh: false,
|
||||||
|
showAssets: false,
|
||||||
|
url: '',
|
||||||
|
treeUrl: '/api/v1/assets/nodes/children/tree/',
|
||||||
|
callback: {
|
||||||
|
onSelected: function(event, treeNode) {
|
||||||
|
vm.leftTable.tableConfig.url = `/api/v1/assets/assets/?node_id=${treeNode.meta.node.id}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
leftTable: {
|
||||||
|
tableConfig: {
|
||||||
|
url: '/api/v1/assets/assets/',
|
||||||
|
columns: [
|
||||||
|
'hostname', 'ip', 'protocols', 'platform', 'comment'
|
||||||
|
],
|
||||||
|
columnsShow: {
|
||||||
|
min: ['hostname', 'ip'],
|
||||||
|
default: ['hostname', 'ip', 'platform']
|
||||||
|
},
|
||||||
|
columnsMeta: {
|
||||||
|
hostname: {
|
||||||
|
formatter: DetailFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
route: 'AssetDetail',
|
||||||
|
routeQuery: {
|
||||||
|
activeTab: 'Detail'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showOverflowTooltip: true
|
||||||
|
},
|
||||||
|
ip: {
|
||||||
|
showOverflowTooltip: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tableAttrs: {
|
||||||
|
stripe: false, // 斑马纹表格
|
||||||
|
border: true, // 表格边框
|
||||||
|
fit: true, // 宽度自适应,
|
||||||
|
tooltipEffect: 'dark',
|
||||||
|
rowClassName({ row, rowIndex }) {
|
||||||
|
if (row === vm.clickedRow) {
|
||||||
|
return 'row-clicked'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rowClick: function(row, column, event) {
|
||||||
|
vm.rightTable.url = `/api/v1/assets/asset-users/?asset_id=${row.id}&latest=1`
|
||||||
|
vm.clickedRow = row
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headerActions: {
|
||||||
|
hasLeftActions: false,
|
||||||
|
hasCreate: false,
|
||||||
|
hasExport: false,
|
||||||
|
hasImport: false,
|
||||||
|
hasBulkDelete: false,
|
||||||
|
hasColumnSetting: true,
|
||||||
|
hasRefresh: true,
|
||||||
|
hasBulkUpdate: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rightTable: {
|
||||||
|
url: `/api/v1/assets/asset-users/?hostname=ShowFirstAssetRelated&latest=1`,
|
||||||
|
tableConfig: {
|
||||||
|
columns: ['name', 'username', 'version', 'backend_display', 'date_created', 'actions'],
|
||||||
|
columnsShow: {
|
||||||
|
min: ['username', 'actions'],
|
||||||
|
default: ['name', 'username', 'version', 'backend_display', 'date_created', 'actions']
|
||||||
|
},
|
||||||
|
columnsMeta: {
|
||||||
|
name: {
|
||||||
|
formatter: null,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
sortable: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tableAttrs: {
|
||||||
|
stripe: true, // 斑马纹表格
|
||||||
|
border: true, // 表格边框
|
||||||
|
fit: true, // 宽度自适应,
|
||||||
|
tooltipEffect: 'dark',
|
||||||
|
rowClassName({ row, rowIndex }) {
|
||||||
|
return 'row-background-color'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasLeftActions: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.asset-table ::v-deep .row-clicked, .asset-user-table ::v-deep .row-background-color {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
.asset-table {
|
||||||
|
& >>> .table-content {
|
||||||
|
margin-left: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mini-button{
|
||||||
|
width: 12px;
|
||||||
|
float: left;
|
||||||
|
margin-right: 10px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 9px 0;
|
||||||
|
background-color: #1ab394;
|
||||||
|
border-color: #1ab394;
|
||||||
|
color: #FFFFFF;
|
||||||
|
border-radius: 5px;
|
||||||
|
line-height: 1.428;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
</style>
|
203
src/views/accounts/GatheredUser/GatheredUserList.vue
Normal file
203
src/views/accounts/GatheredUser/GatheredUserList.vue
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-row>
|
||||||
|
<el-col v-show="iShowTree" :span="iShowTree?4:0">
|
||||||
|
<AutoDataZTree
|
||||||
|
ref="AUtoDataZTree"
|
||||||
|
:setting="treeSetting"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="iShowTree?9:11">
|
||||||
|
<div class="mini">
|
||||||
|
<div style="display:block" class="mini-button" @click="iShowTree=!iShowTree">
|
||||||
|
<i v-show="iShowTree" class="fa fa-angle-left fa-x" />
|
||||||
|
<i v-show="!iShowTree" class="fa fa-angle-right fa-x" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<GenericListTable
|
||||||
|
ref="LeftTable"
|
||||||
|
class="asset-table"
|
||||||
|
:header-actions="leftTable.headerActions"
|
||||||
|
:table-config="leftTable.tableConfig"
|
||||||
|
@row-click="leftTable.tableConfig.rowClick"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="iShowTree?11:13">
|
||||||
|
<GenericListTable
|
||||||
|
ref="RightTable"
|
||||||
|
class="asset-user-table"
|
||||||
|
:header-actions="rightTable.headerActions"
|
||||||
|
:table-config="rightTable.tableConfig"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import GenericListTable from '@/layout/components/GenericListTable'
|
||||||
|
import AutoDataZTree from '@/components/AutoDataZTree/index'
|
||||||
|
import { ChoicesFormatter, DetailFormatter } from '@/components/TableFormatters'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AssetAccountList',
|
||||||
|
components: {
|
||||||
|
AutoDataZTree, GenericListTable
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const vm = this
|
||||||
|
return {
|
||||||
|
clickedRow: {},
|
||||||
|
iShowTree: true,
|
||||||
|
treeSetting: {
|
||||||
|
showMenu: false,
|
||||||
|
showRefresh: false,
|
||||||
|
showAssets: false,
|
||||||
|
url: '',
|
||||||
|
treeUrl: '/api/v1/assets/nodes/children/tree/',
|
||||||
|
callback: {
|
||||||
|
onSelected: function(event, treeNode) {
|
||||||
|
vm.leftTable.tableConfig.url = `/api/v1/assets/assets/?node_id=${treeNode.meta.node.id}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
leftTable: {
|
||||||
|
tableConfig: {
|
||||||
|
url: '/api/v1/assets/assets/',
|
||||||
|
columns: [
|
||||||
|
'hostname', 'ip', 'public_ip', 'admin_user_display',
|
||||||
|
'protocols', 'platform', 'connectivity',
|
||||||
|
'created_by', 'date_created', 'comment', 'org_name'
|
||||||
|
],
|
||||||
|
columnsShow: {
|
||||||
|
min: ['hostname', 'ip', 'platform'],
|
||||||
|
default: ['hostname', 'ip', 'connectivity', 'platform']
|
||||||
|
},
|
||||||
|
columnsMeta: {
|
||||||
|
hostname: {
|
||||||
|
formatter: DetailFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
route: 'AssetDetail',
|
||||||
|
routeQuery: {
|
||||||
|
activeTab: 'Detail'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showOverflowTooltip: true
|
||||||
|
},
|
||||||
|
connectivity: {
|
||||||
|
label: this.$t('assets.Reachable'),
|
||||||
|
formatter: ChoicesFormatter,
|
||||||
|
formatterArgs: {
|
||||||
|
iconChoices: {
|
||||||
|
0: 'fa-times text-danger',
|
||||||
|
1: 'fa-check text-primary',
|
||||||
|
2: 'fa-circle text-warning'
|
||||||
|
},
|
||||||
|
typeChange: function(val) {
|
||||||
|
if (!val) {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return val.status
|
||||||
|
},
|
||||||
|
hasTips: true
|
||||||
|
},
|
||||||
|
width: '90px',
|
||||||
|
align: 'center'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tableAttrs: {
|
||||||
|
stripe: false, // 斑马纹表格
|
||||||
|
border: true, // 表格边框
|
||||||
|
fit: true, // 宽度自适应,
|
||||||
|
tooltipEffect: 'dark',
|
||||||
|
rowClassName({ row, rowIndex }) {
|
||||||
|
if (row === vm.clickedRow) {
|
||||||
|
return 'row-clicked'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rowClick: function(row, column, event) {
|
||||||
|
vm.rightTable.tableConfig.url = `/api/v1/assets/gathered-users/?asset_id=${row.id}`
|
||||||
|
vm.clickedRow = row
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headerActions: {
|
||||||
|
hasLeftActions: false,
|
||||||
|
hasCreate: false,
|
||||||
|
hasExport: false,
|
||||||
|
hasImport: false,
|
||||||
|
hasBulkDelete: false,
|
||||||
|
hasBulkUpdate: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rightTable: {
|
||||||
|
tableConfig: {
|
||||||
|
url: `/api/v1/assets/gathered-users/?asset__hostname=ShowFirstAssetRelated`,
|
||||||
|
columns: [
|
||||||
|
'username', 'date_last_login', 'present', 'ip_last_login', 'date_updated'
|
||||||
|
],
|
||||||
|
columnsShow: {
|
||||||
|
min: ['username'],
|
||||||
|
default: [
|
||||||
|
'username', 'date_last_login', 'present', 'ip_last_login', 'date_updated'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
columnsMeta: {
|
||||||
|
username: {
|
||||||
|
showOverflowTooltip: true
|
||||||
|
},
|
||||||
|
present: {
|
||||||
|
width: 80
|
||||||
|
},
|
||||||
|
ip_last_login: {
|
||||||
|
width: 120
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tableAttrs: {
|
||||||
|
stripe: true, // 斑马纹表格
|
||||||
|
border: true, // 表格边框
|
||||||
|
fit: true, // 宽度自适应,
|
||||||
|
tooltipEffect: 'dark',
|
||||||
|
rowClassName({ row, rowIndex }) {
|
||||||
|
return 'row-background-color'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headerActions: {
|
||||||
|
hasLeftActions: false,
|
||||||
|
hasCreate: false,
|
||||||
|
hasExport: true,
|
||||||
|
hasImport: false,
|
||||||
|
hasBulkDelete: false,
|
||||||
|
hasBulkUpdate: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.asset-table ::v-deep .row-clicked, .asset-user-table ::v-deep .row-background-color {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
.asset-table {
|
||||||
|
& >>> .table-content {
|
||||||
|
margin-left: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mini-button{
|
||||||
|
width: 12px;
|
||||||
|
float: left;
|
||||||
|
margin-right: 10px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 9px 0;
|
||||||
|
background-color: #1ab394;
|
||||||
|
border-color: #1ab394;
|
||||||
|
color: #FFFFFF;
|
||||||
|
border-radius: 5px;
|
||||||
|
line-height: 1.428;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,16 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<GenericTreeListPage
|
||||||
<GenericTreeListPage
|
ref="GenericTreeListPage"
|
||||||
ref="GenericTreeListPage"
|
v-loading="loading"
|
||||||
:table-config="tableConfig"
|
:table-config="tableConfig"
|
||||||
:header-actions="headerActions"
|
:header-actions="headerActions"
|
||||||
:tree-setting="treeSetting"
|
:tree-setting="treeSetting"
|
||||||
@TreeInitFinish="checkFirstNode"
|
class="command-list-table"
|
||||||
@TagSearch="handleTagChange"
|
@TreeInitFinish="checkFirstNode"
|
||||||
@TagFilter="handleFilterChange"
|
@TagSearch="handleTagChange"
|
||||||
@TagDateChange="handleDateChange"
|
@TagFilter="handleFilterChange"
|
||||||
/>
|
@TagDateChange="handleDateChange"
|
||||||
</div>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -39,6 +39,14 @@ export default {
|
|||||||
loading: true,
|
loading: true,
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
url: '',
|
url: '',
|
||||||
|
tableAttrs: {
|
||||||
|
rowClassName: ({ row }) => {
|
||||||
|
if (row.risk_level === 5) {
|
||||||
|
return 'risk-command'
|
||||||
|
}
|
||||||
|
return 'command'
|
||||||
|
}
|
||||||
|
},
|
||||||
columns: [
|
columns: [
|
||||||
'expandCol', 'input', 'risk_level', 'user',
|
'expandCol', 'input', 'risk_level', 'user',
|
||||||
'asset', 'system_user', 'session', 'timestamp'
|
'asset', 'system_user', 'session', 'timestamp'
|
||||||
@ -51,7 +59,7 @@ export default {
|
|||||||
expandCol: {
|
expandCol: {
|
||||||
type: 'expand',
|
type: 'expand',
|
||||||
prop: 'output',
|
prop: 'output',
|
||||||
label: '>',
|
label: '',
|
||||||
formatter: OutputExpandFormatter
|
formatter: OutputExpandFormatter
|
||||||
},
|
},
|
||||||
risk_level: {
|
risk_level: {
|
||||||
@ -151,7 +159,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checkFirstNode(obj) {
|
checkFirstNode(obj) {
|
||||||
@ -203,6 +210,13 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style lang="scss" scoped>
|
||||||
|
.command-list-table >>> .risk-command {
|
||||||
|
background-color: oldlace;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -17,7 +17,7 @@ export default {
|
|||||||
name: 'replay',
|
name: 'replay',
|
||||||
title: this.$t('sessions.replay'),
|
title: this.$t('sessions.replay'),
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
can: (row, cellValue) => {
|
can: ({ row, cellValue }) => {
|
||||||
return row.can_replay
|
return row.can_replay
|
||||||
},
|
},
|
||||||
callback: function({ row, tableData }) {
|
callback: function({ row, tableData }) {
|
||||||
@ -30,7 +30,7 @@ export default {
|
|||||||
name: 'download',
|
name: 'download',
|
||||||
title: this.$t('sessions.download'),
|
title: this.$t('sessions.download'),
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
can: (row, cellValue) => {
|
can: ({ row, cellValue }) => {
|
||||||
return row.can_replay
|
return row.can_replay
|
||||||
},
|
},
|
||||||
callback: function({ row, tableData }) {
|
callback: function({ row, tableData }) {
|
||||||
|
@ -19,7 +19,7 @@ export default {
|
|||||||
name: 'terminate',
|
name: 'terminate',
|
||||||
title: this.$t('sessions.terminate'),
|
title: this.$t('sessions.terminate'),
|
||||||
type: 'danger',
|
type: 'danger',
|
||||||
can: (row, cellValue) => {
|
can: ({ row, cellValue }) => {
|
||||||
return row.can_terminate
|
return row.can_terminate
|
||||||
},
|
},
|
||||||
callback: function({ reload, row }) {
|
callback: function({ reload, row }) {
|
||||||
@ -39,13 +39,15 @@ export default {
|
|||||||
name: 'join',
|
name: 'join',
|
||||||
title: this.$t('sessions.Monitor'),
|
title: this.$t('sessions.Monitor'),
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
can: (row, cellValue) => {
|
can: ({ row, cellValue }) => {
|
||||||
if (row.protocol === 'rdp' ||
|
|
||||||
row.protocol === 'vnc') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return row.can_join
|
return row.can_join
|
||||||
},
|
},
|
||||||
|
tip: ({ row }) => {
|
||||||
|
if (row.login_from === 'RT') {
|
||||||
|
return this.$t('sessions.XRDPNotSupport')
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
callback: function({ row, tableData }) {
|
callback: function({ row, tableData }) {
|
||||||
// 跳转到luna页面
|
// 跳转到luna页面
|
||||||
if (row.protocol === 'rdp' || row.protocol === 'vnc') {
|
if (row.protocol === 'rdp' || row.protocol === 'vnc') {
|
||||||
|
@ -27,7 +27,8 @@ export default {
|
|||||||
fields: [
|
fields: [
|
||||||
[
|
[
|
||||||
this.$t('common.BasicInfo'), [
|
this.$t('common.BasicInfo'), [
|
||||||
'SITE_URL', 'USER_GUIDE_URL', 'FORGOT_PASSWORD_URL', globalOrgName
|
'SITE_URL', 'USER_GUIDE_URL',
|
||||||
|
'FORGOT_PASSWORD_URL', globalOrgName
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
@ -64,7 +64,7 @@ export default {
|
|||||||
const selectedUsers = this.selectedUsers.map(item => {
|
const selectedUsers = this.selectedUsers.map(item => {
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
label: `${item.name}(${item.username})`
|
label: item.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -67,7 +67,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.$axios.patch(
|
this.$axios.patch(
|
||||||
`/api/v1/notifications/system/subscriptions/${sub.id}/`,
|
`/api/v1/notifications/system-msg-subscription/${sub.id}/`,
|
||||||
{ receive_backends: backends }
|
{ receive_backends: backends }
|
||||||
).catch(err => {
|
).catch(err => {
|
||||||
this.$log.error(err)
|
this.$log.error(err)
|
||||||
@ -78,11 +78,10 @@ export default {
|
|||||||
onDialogSelectSubmit(userIds) {
|
onDialogSelectSubmit(userIds) {
|
||||||
this.dialogVisible = false
|
this.dialogVisible = false
|
||||||
this.$axios.patch(
|
this.$axios.patch(
|
||||||
`/api/v1/notifications/system/subscriptions/${this.currentEditSub.id}/`,
|
`/api/v1/notifications/system-msg-subscription/${this.currentEditSub.id}/`,
|
||||||
{ users: userIds }
|
{ users: userIds }
|
||||||
).then(newSub => {
|
).then(newSub => {
|
||||||
const msgType = this.idMessageTypeMapper[newSub.id]
|
const msgType = this.idMessageTypeMapper[newSub.message_type]
|
||||||
msgType.users = newSub.users
|
|
||||||
msgType.receivers = newSub.receivers
|
msgType.receivers = newSub.receivers
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
@ -97,40 +96,37 @@ export default {
|
|||||||
this.receiveBackends = await this.$axios.get('/api/v1/notifications/backends/')
|
this.receiveBackends = await this.$axios.get('/api/v1/notifications/backends/')
|
||||||
},
|
},
|
||||||
async initSubscriptions() {
|
async initSubscriptions() {
|
||||||
const subscriptions = await this.$axios.get('/api/v1/notifications/system/subscriptions/')
|
const subscriptions = await this.$axios.get('/api/v1/notifications/system-msg-subscription/')
|
||||||
|
|
||||||
// 根据 app 分组
|
const trans_subscriptions = []
|
||||||
const appMessageTypesMapper = {}
|
|
||||||
subscriptions.forEach(sub => {
|
|
||||||
if (!(sub.message_category in appMessageTypesMapper)) {
|
|
||||||
appMessageTypesMapper[sub.message_category] = {
|
|
||||||
id: sub.message_category,
|
|
||||||
value: sub.message_category_label,
|
|
||||||
children: [sub]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
appMessageTypesMapper[sub.message_category].children.push(sub)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// sub 改成需要的数据结构
|
for (const category of subscriptions) {
|
||||||
for (const app of Object.values(appMessageTypesMapper)) {
|
const children = []
|
||||||
app.children = app.children.map(sub => {
|
trans_subscriptions.push({
|
||||||
|
id: category.category,
|
||||||
|
value: category.category_label,
|
||||||
|
children: children
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const sub of category.children) {
|
||||||
const backendsChecked = {}
|
const backendsChecked = {}
|
||||||
this.receiveBackends.forEach(backend => {
|
this.receiveBackends.forEach(backend => {
|
||||||
backendsChecked[backend.name] = sub.receive_backends.indexOf(backend.name) > -1
|
backendsChecked[backend.name] = sub.receive_backends.indexOf(backend.name) > -1
|
||||||
})
|
})
|
||||||
const subObj = {
|
|
||||||
id: sub.id,
|
const trans_sub = {
|
||||||
value: sub.message_label,
|
id: sub.message_type,
|
||||||
|
value: sub.message_type_label,
|
||||||
receivers: sub.receivers,
|
receivers: sub.receivers,
|
||||||
receive_backends: backendsChecked
|
receive_backends: backendsChecked
|
||||||
}
|
}
|
||||||
this.idMessageTypeMapper[sub.id] = subObj
|
children.push(trans_sub)
|
||||||
return subObj
|
|
||||||
})
|
this.idMessageTypeMapper[trans_sub.id] = trans_sub
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.tableData = Object.values(appMessageTypesMapper)
|
|
||||||
|
this.tableData = trans_subscriptions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,22 @@ export default {
|
|||||||
url: '/api/v1/settings/setting/?category=terminal'
|
url: '/api/v1/settings/setting/?category=terminal'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.$store.getters.hasValidLicense) {
|
||||||
|
const xRDPFields = [
|
||||||
|
'XRDP', [
|
||||||
|
'TERMINAL_RDP_ADDR'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
this.selectFields.splice(1, 0, xRDPFields)
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getMethod() {
|
getMethod() {
|
||||||
return 'put'
|
return 'put'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -69,6 +69,10 @@ export default {
|
|||||||
title: this.$t('setting.DingTalk'),
|
title: this.$t('setting.DingTalk'),
|
||||||
name: 'DingTalk'
|
name: 'DingTalk'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: this.$t('setting.SystemMessageSubscription'),
|
||||||
|
name: 'SystemMessageSubscription'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: this.$t('setting.Terminal'),
|
title: this.$t('setting.Terminal'),
|
||||||
name: 'Terminal'
|
name: 'Terminal'
|
||||||
@ -119,6 +123,9 @@ export default {
|
|||||||
case 'License':
|
case 'License':
|
||||||
this.activeMenu = 'License'
|
this.activeMenu = 'License'
|
||||||
break
|
break
|
||||||
|
case 'SystemMessageSubscription':
|
||||||
|
this.activeMenu = 'SystemMessageSubscription'
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
this.activeMenu = 'Basic'
|
this.activeMenu = 'Basic'
|
||||||
break
|
break
|
||||||
|
@ -59,7 +59,7 @@ export default {
|
|||||||
password: {
|
password: {
|
||||||
component: UserPassword,
|
component: UserPassword,
|
||||||
hidden: (formValue) => {
|
hidden: (formValue) => {
|
||||||
if (formValue.password_strategy) {
|
if (formValue.password_strategy === 'custom') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return !formValue.update_password
|
return !formValue.update_password
|
||||||
@ -81,7 +81,7 @@ export default {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
hidden: (formValue) => {
|
hidden: (formValue) => {
|
||||||
if (formValue.password_strategy) {
|
if (formValue.password_strategy === 'custom') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return !formValue.update_password || !this.user.can_public_key_auth
|
return !formValue.update_password || !this.user.can_public_key_auth
|
||||||
@ -148,7 +148,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
cleanFormValue(value) {
|
cleanFormValue(value) {
|
||||||
const method = this.getMethod()
|
const method = this.getMethod()
|
||||||
if (method === 'post' && !value.password_strategy) {
|
if (method === 'post' && value.password_strategy === 'email') {
|
||||||
delete value['password']
|
delete value['password']
|
||||||
if (this.currentOrgIsRoot) {
|
if (this.currentOrgIsRoot) {
|
||||||
delete value['groups']
|
delete value['groups']
|
||||||
|
@ -92,7 +92,7 @@ export default {
|
|||||||
name: 'remove',
|
name: 'remove',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
has: hasRemove,
|
has: hasRemove,
|
||||||
can: function(row, cellValue) {
|
can: ({ row }) => {
|
||||||
return row.can_delete
|
return row.can_delete
|
||||||
},
|
},
|
||||||
callback: this.removeUserFromOrg
|
callback: this.removeUserFromOrg
|
||||||
|
@ -1,78 +0,0 @@
|
|||||||
<template>
|
|
||||||
<TreeTable :table-config="tableConfig" :tree-setting="treeSetting" :header-actions="headerActions" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import TreeTable from '@/components/TreeTable'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
TreeTable
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
treeSetting: {
|
|
||||||
showMenu: false,
|
|
||||||
showRefresh: true,
|
|
||||||
showAssets: true,
|
|
||||||
url: '/api/v1/assets/gathered-users/',
|
|
||||||
nodeUrl: '/api/v1/assets/nodes/',
|
|
||||||
// ?assets=0不显示资产. =1显示资产
|
|
||||||
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1'
|
|
||||||
},
|
|
||||||
tableConfig: {
|
|
||||||
url: '/api/v1/assets/gathered-users/',
|
|
||||||
hasTree: true,
|
|
||||||
columns: [
|
|
||||||
'hostname', 'ip', 'username', 'date_last_login', 'present',
|
|
||||||
'ip_last_login', 'date_updated'
|
|
||||||
],
|
|
||||||
columnsMeta: {
|
|
||||||
hostname: {
|
|
||||||
showOverflowTooltip: true
|
|
||||||
},
|
|
||||||
ip: {
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
username: {
|
|
||||||
showOverflowTooltip: true
|
|
||||||
},
|
|
||||||
present: {
|
|
||||||
width: 80
|
|
||||||
},
|
|
||||||
ip_last_login: {
|
|
||||||
width: 120
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
headerActions: {
|
|
||||||
hasCreate: false,
|
|
||||||
hasLeftActions: false,
|
|
||||||
hasImport: false,
|
|
||||||
searchConfig: {
|
|
||||||
exclude: ['asset'],
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: this.$t('assets.Hostname'),
|
|
||||||
value: 'asset__hostname'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'IP',
|
|
||||||
value: 'asset__ip'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onGatherUserTasks() {
|
|
||||||
this.$router.push({ name: 'GatherUserTaskList' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
</style>
|
|
@ -85,9 +85,10 @@ export default {
|
|||||||
componentName() {
|
componentName() {
|
||||||
const nameMapper = {
|
const nameMapper = {
|
||||||
koko: 'KoKo',
|
koko: 'KoKo',
|
||||||
guacamole: 'Guacamole',
|
|
||||||
omnidb: 'OmniDB',
|
omnidb: 'OmniDB',
|
||||||
lion: 'Lion'
|
guacamole: 'Guacamole',
|
||||||
|
lion: 'Lion',
|
||||||
|
xrdp: 'XRDP'
|
||||||
}
|
}
|
||||||
return nameMapper[this.componentMetric.type]
|
return nameMapper[this.componentMetric.type]
|
||||||
}
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
<template>
|
|
||||||
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
|
|
||||||
import { UploadKey, Select2 } from '@/components'
|
|
||||||
export default {
|
|
||||||
name: 'VaultCreate',
|
|
||||||
components: {
|
|
||||||
GenericCreateUpdatePage
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
url: '/api/v1/assets/asset-users/',
|
|
||||||
initial: {
|
|
||||||
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
[this.$t('common.Basic'), ['asset', 'username', 'password', 'private_key', 'comment']]
|
|
||||||
],
|
|
||||||
fieldsMeta: {
|
|
||||||
asset: {
|
|
||||||
label: this.$t('perms.Asset'),
|
|
||||||
component: Select2,
|
|
||||||
el: {
|
|
||||||
multiple: false,
|
|
||||||
ajax: {
|
|
||||||
url: '/api/v1/assets/assets/',
|
|
||||||
transformOption: (item) => {
|
|
||||||
return { label: `${item.hostname}(${item.ip})`, value: item.id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
el: {
|
|
||||||
placeholder: this.$t('common.Name')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
username: {
|
|
||||||
el: {
|
|
||||||
placeholder: this.$t('common.Username')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
helpText: this.$t('common.passwordOrPassphrase')
|
|
||||||
},
|
|
||||||
private_key: {
|
|
||||||
component: UploadKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,340 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<GenericTreeListPage ref="TreeTablePage" :tree-setting="treeSetting">
|
|
||||||
<template #table>
|
|
||||||
<AssetUserTable ref="table" v-bind="assetUserConfig" />
|
|
||||||
</template>
|
|
||||||
</GenericTreeListPage>
|
|
||||||
<Dialog width="50" :title="this.$t('common.MFAConfirm')" :visible.sync="showMFADialog" :show-confirm="false" :show-cancel="false" :destroy-on-close="true">
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="4">
|
|
||||||
<div style="line-height: 34px;text-align: center">MFA</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="14">
|
|
||||||
<el-input v-model="MFAInput" />
|
|
||||||
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="4">
|
|
||||||
<el-button size="mini" type="primary" style="line-height:20px " @click="MFAConfirm">{{ this.$t('common.Confirm') }}</el-button>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</Dialog>
|
|
||||||
<Dialog :title="$t('common.Export')" :visible.sync="showExportDialog" @confirm="handleExportConfirm()" @cancel="handleExportCancel()">
|
|
||||||
<el-form label-position="left" style="padding-left: 50px">
|
|
||||||
<el-form-item :label="this.$t('common.imExport.ExportRange')" :label-width="'100px'">
|
|
||||||
<el-radio v-model="exportOption" class="export-item" label="1">{{ this.$t('common.imExport.ExportAll') }}</el-radio>
|
|
||||||
<br>
|
|
||||||
<el-radio v-model="exportOption" :disabled="selectedRows.length===0" class="export-item" label="2">{{ this.$t('common.imExport.ExportOnlySelectedItems') }}</el-radio>
|
|
||||||
<br>
|
|
||||||
<!-- 去掉导出搜索项-->
|
|
||||||
<!-- <el-radio v-model="exportOption" disabled class="export-item" label="3">{{ this.$t('common.imExport.ExportOnlyFiltered') }}</el-radio>-->
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</Dialog>
|
|
||||||
<Dialog :title="$t('common.Import')" :visible.sync="showImportDialog" @confirm="handleImportConfirm()" @cancel="handleImportCancel()">
|
|
||||||
<el-form label-position="left" style="padding-left: 50px">
|
|
||||||
<el-form-item :label="$t('common.Import' )" :label-width="'100px'">
|
|
||||||
<el-radio v-model="importOption" class="export-item" label="1">{{ this.$t('common.Create') }}</el-radio>
|
|
||||||
<el-radio v-model="importOption" disabled class="export-item" label="2">{{ this.$t('common.Update') }}</el-radio>
|
|
||||||
<div style="line-height: 1.5">
|
|
||||||
<span v-if="importOption==='1'" class="el-upload__tip">
|
|
||||||
{{ this.$t('common.imExport.downloadImportTemplateMsg') }}
|
|
||||||
<el-link type="success" :underline="false" :href="downloadImportTempUrl">{{ this.$t('common.Download') }}</el-link>
|
|
||||||
</span>
|
|
||||||
<span v-else class="el-upload__tip">`
|
|
||||||
{{ this.$t('common.imExport.downloadUpdateTemplateMsg') }}
|
|
||||||
<el-link type="success" :underline="false" @click="downloadUpdateTempUrl">{{ this.$t('common.Download') }}</el-link>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('common.Upload' )" :label-width="'100px'">
|
|
||||||
<el-upload
|
|
||||||
ref="upload"
|
|
||||||
action="string"
|
|
||||||
:http-request="handleImport"
|
|
||||||
list-type="text/csv"
|
|
||||||
:limit="1"
|
|
||||||
:auto-upload="false"
|
|
||||||
:before-upload="beforeUpload"
|
|
||||||
>
|
|
||||||
<el-button size="mini" type="default">{{ this.$t('common.SelectFile') }}</el-button>
|
|
||||||
<div slot="tip" :class="uploadHelpTextClass" style="line-height: 1.5">{{ this.$t('common.imExport.onlyCSVFilesTips') }}</div>
|
|
||||||
</el-upload>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div v-if="errorMsg" class="error-msg error-results">
|
|
||||||
<ul v-if="typeof errorMsg === 'object'">
|
|
||||||
<li v-for="(item, index) in errorMsg" :key="item + '-' + index"> {{ item }}</li>
|
|
||||||
</ul>
|
|
||||||
<span v-else>{{ errorMsg }}</span>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import GenericTreeListPage from '@/layout/components/GenericTreeListPage'
|
|
||||||
import { AssetUserTable } from '@/components'
|
|
||||||
import Dialog from '@/components/Dialog'
|
|
||||||
import { setUrlParam } from '@/utils/common'
|
|
||||||
import { createSourceIdCache } from '@/api/common'
|
|
||||||
import * as queryUtil from '@/components/DataTable/compenents/el-data-table/utils/query'
|
|
||||||
import { mapGetters } from 'vuex'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'VaultList',
|
|
||||||
components: {
|
|
||||||
GenericTreeListPage,
|
|
||||||
AssetUserTable,
|
|
||||||
Dialog
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
const vm = this
|
|
||||||
return {
|
|
||||||
showImportDialog: false,
|
|
||||||
importOption: '1',
|
|
||||||
isCsv: true,
|
|
||||||
errorMsg: '',
|
|
||||||
showExportDialog: false,
|
|
||||||
exportOption: '1',
|
|
||||||
meta: {},
|
|
||||||
MfaExpired: 0,
|
|
||||||
showMFADialog: false,
|
|
||||||
MFAInput: '',
|
|
||||||
selectedRows: '',
|
|
||||||
assetUserConfig: {
|
|
||||||
hasLeftActions: true,
|
|
||||||
hasCreate: true,
|
|
||||||
hasClone: false,
|
|
||||||
url: '/api/v1/assets/asset-users/',
|
|
||||||
handleImport: function({ selectedRows }) {
|
|
||||||
this.selectedRows = selectedRows
|
|
||||||
this.dialogStatus = 'import'
|
|
||||||
if (!this.needMFAVerify) {
|
|
||||||
this.showMFADialog = false
|
|
||||||
this.showImportDialog = true
|
|
||||||
} else {
|
|
||||||
this.showMFADialog = true
|
|
||||||
}
|
|
||||||
}.bind(this),
|
|
||||||
handleExport: function({ selectedRows }) {
|
|
||||||
this.selectedRows = selectedRows
|
|
||||||
this.dialogStatus = 'export'
|
|
||||||
if (!this.needMFAVerify) {
|
|
||||||
this.showMFADialog = false
|
|
||||||
this.showExportDialog = true
|
|
||||||
} else {
|
|
||||||
this.showMFADialog = true
|
|
||||||
}
|
|
||||||
}.bind(this)
|
|
||||||
},
|
|
||||||
treeSetting: {
|
|
||||||
showMenu: false,
|
|
||||||
showRefresh: false,
|
|
||||||
showAssets: true,
|
|
||||||
url: '/api/v1/assets/asset-users/',
|
|
||||||
treeUrl: '/api/v1/assets/nodes/children/tree/?assets=1',
|
|
||||||
callback: {
|
|
||||||
onSelected: function(event, treeNode) {
|
|
||||||
let url = vm.assetUserConfig.url
|
|
||||||
if (treeNode.meta.type === 'node') {
|
|
||||||
const nodeId = treeNode.meta.node.id
|
|
||||||
url = setUrlParam(url, 'asset_id', '')
|
|
||||||
url = setUrlParam(url, 'node_id', nodeId)
|
|
||||||
} else if (treeNode.meta.type === 'asset') {
|
|
||||||
const assetId = treeNode.meta.asset.id
|
|
||||||
url = setUrlParam(url, 'node_id', '')
|
|
||||||
url = setUrlParam(url, 'asset_id', assetId)
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
vm.assetUserConfig.url = url
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters([
|
|
||||||
'MFA_TTl',
|
|
||||||
'MFAVerifyAt',
|
|
||||||
'publicSettings'
|
|
||||||
]),
|
|
||||||
needMFAVerify() {
|
|
||||||
if (!this.publicSettings.SECURITY_VIEW_AUTH_NEED_MFA) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
const ttl = this.publicSettings.SECURITY_MFA_VERIFY_TTL
|
|
||||||
const now = new Date()
|
|
||||||
return !(this.MFAVerifyAt && (now - this.MFAVerifyAt) < ttl * 1000)
|
|
||||||
},
|
|
||||||
hasSelected() {
|
|
||||||
return this.selectedRows.length > 0
|
|
||||||
},
|
|
||||||
upLoadUrl() {
|
|
||||||
return this.url
|
|
||||||
},
|
|
||||||
downloadImportTempUrl() {
|
|
||||||
const baseUrl = `/api/v1/assets/asset-user-auth-infos/`
|
|
||||||
return baseUrl + '?format=csv&template=import&limit=1'
|
|
||||||
},
|
|
||||||
uploadHelpTextClass() {
|
|
||||||
const cls = ['el-upload__tip']
|
|
||||||
if (!this.isCsv) {
|
|
||||||
cls.push('error-msg')
|
|
||||||
}
|
|
||||||
return cls
|
|
||||||
},
|
|
||||||
...mapGetters([
|
|
||||||
'MFAVerifyAt',
|
|
||||||
'MFA_TTl'
|
|
||||||
])
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
performUpdate(item) {
|
|
||||||
this.$axios.put(
|
|
||||||
`/api/v1/assets/asset-users/`,
|
|
||||||
item.file,
|
|
||||||
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
|
|
||||||
).then((data) => {
|
|
||||||
const msg = this.$t('common.imExport.updateSuccessMsg', { count: data.length })
|
|
||||||
this.onSuccess(msg)
|
|
||||||
}).catch(error => {
|
|
||||||
this.catchError(error)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
performCreate(item) {
|
|
||||||
this.$axios.post(
|
|
||||||
`/api/v1/assets/asset-users/`,
|
|
||||||
item.file,
|
|
||||||
{ headers: { 'Content-Type': 'text/csv' }, disableFlashErrorMsg: true }
|
|
||||||
).then((data) => {
|
|
||||||
const msg = this.$t('common.imExport.createSuccessMsg', { count: data.length })
|
|
||||||
this.onSuccess(msg)
|
|
||||||
}).catch(error => {
|
|
||||||
this.$message.error(this.$t('common.updateErrorMsg') + ' ' + error)
|
|
||||||
this.catchError(error)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
catchError(error) {
|
|
||||||
this.$refs.upload.clearFiles()
|
|
||||||
if (error.response && error.response.status === 400) {
|
|
||||||
const errorData = error.response.data
|
|
||||||
const totalErrorMsg = []
|
|
||||||
errorData.forEach((value, index) => {
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
totalErrorMsg.push(`line ${index}. ${value}`)
|
|
||||||
} else {
|
|
||||||
const errorMsg = [`line ${index}. `]
|
|
||||||
for (const [k, v] of Object.entries(value)) {
|
|
||||||
if (v) {
|
|
||||||
errorMsg.push(`${k}: ${v}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (errorMsg.length > 1) {
|
|
||||||
totalErrorMsg.push(errorMsg.join(' '))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.errorMsg = totalErrorMsg
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSuccess(msg) {
|
|
||||||
this.errorMsg = ''
|
|
||||||
this.$message.success(msg)
|
|
||||||
},
|
|
||||||
handleImport(item) {
|
|
||||||
if (this.importOption === '1') {
|
|
||||||
this.performCreate(item)
|
|
||||||
} else {
|
|
||||||
this.performUpdate(item)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async downloadUpdateTempUrl() {
|
|
||||||
var resources = []
|
|
||||||
const data = this.selectedRows
|
|
||||||
for (let index = 0; index < data.length; index++) {
|
|
||||||
resources.push(data[index].id)
|
|
||||||
}
|
|
||||||
const spm = await createSourceIdCache(resources)
|
|
||||||
const baseUrl = `/api/v1/assets/asset-user-auth-infos/`
|
|
||||||
const url = `${baseUrl}?format=csv&template=update&spm=` + spm.spm
|
|
||||||
return this.downloadCsv(url)
|
|
||||||
},
|
|
||||||
async handleImportConfirm() {
|
|
||||||
this.$refs.upload.submit()
|
|
||||||
this.showImportDialog = false
|
|
||||||
},
|
|
||||||
handleImportCancel() {
|
|
||||||
this.showImportDialog = false
|
|
||||||
},
|
|
||||||
beforeUpload(file) {
|
|
||||||
this.isCsv = _.endsWith(file.name, 'csv')
|
|
||||||
return this.isCsv
|
|
||||||
},
|
|
||||||
downloadCsv(url) {
|
|
||||||
const a = document.createElement('a')
|
|
||||||
a.href = url
|
|
||||||
a.click()
|
|
||||||
window.URL.revokeObjectURL(url)
|
|
||||||
},
|
|
||||||
async handleExport() {
|
|
||||||
const url = `/api/v1/assets/asset-user-auth-infos/`
|
|
||||||
let query = {}
|
|
||||||
if (this.exportOption === '2') {
|
|
||||||
const resources = []
|
|
||||||
const data = this.selectedRows
|
|
||||||
for (let index = 0; index < data.length; index++) {
|
|
||||||
resources.push(data[index].id)
|
|
||||||
}
|
|
||||||
const spm = await createSourceIdCache(resources)
|
|
||||||
query['spm'] = spm.spm
|
|
||||||
} else if (this.exportOption === '3') {
|
|
||||||
const listTableRef = this.$parent.$parent.$parent.$parent
|
|
||||||
// console.log(listTableRef)
|
|
||||||
// console.log(listTableRef.dataTable)
|
|
||||||
query = listTableRef.dataTable.getQuery()
|
|
||||||
delete query['limit']
|
|
||||||
delete query['offset']
|
|
||||||
}
|
|
||||||
query['format'] = 'csv'
|
|
||||||
const queryStr =
|
|
||||||
(url.indexOf('?') > -1 ? '&' : '?') +
|
|
||||||
queryUtil.stringify(query, '=', '&')
|
|
||||||
return this.downloadCsv(url + queryStr)
|
|
||||||
},
|
|
||||||
async handleExportConfirm() {
|
|
||||||
await this.handleExport()
|
|
||||||
this.showExportDialog = false
|
|
||||||
},
|
|
||||||
handleExportCancel() {
|
|
||||||
this.showExportDialog = false
|
|
||||||
},
|
|
||||||
MFAConfirm() {
|
|
||||||
if (this.MFAInput.length !== 6) {
|
|
||||||
return this.$message.error(this.$t('common.MFAErrorMsg'))
|
|
||||||
}
|
|
||||||
this.$axios.post(
|
|
||||||
`/api/v1/authentication/otp/verify/`, {
|
|
||||||
code: this.MFAInput
|
|
||||||
}
|
|
||||||
).then(
|
|
||||||
res => {
|
|
||||||
this.$store.dispatch('users/setMFAVerify')
|
|
||||||
if (this.dialogStatus === 'import') {
|
|
||||||
this.showMFADialog = false
|
|
||||||
this.showImportDialog = true
|
|
||||||
} else {
|
|
||||||
this.showMFADialog = false
|
|
||||||
this.showExportDialog = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
@ -10,47 +10,6 @@ export default {
|
|||||||
name: 'Xpack',
|
name: 'Xpack',
|
||||||
meta: { title: 'X-Pack', icon: 'sitemap', licenseRequired: true },
|
meta: { title: 'X-Pack', icon: 'sitemap', licenseRequired: true },
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
path: 'change-auth-plan',
|
|
||||||
component: empty,
|
|
||||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'), activeMenu: '/xpack/change-auth-plan/plan' },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'plan',
|
|
||||||
component: () => import('@/views/xpack/ChangeAuthPlan/ChangeAuthPlanList.vue'),
|
|
||||||
name: 'ChangeAuthPlanList',
|
|
||||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'), activeMenu: '/xpack/change-auth-plan/plan' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'plan/create',
|
|
||||||
component: () => import('@/views/xpack/ChangeAuthPlan/ChangeAuthPlanCreateUpdate.vue'),
|
|
||||||
name: 'ChangeAuthPlanCreate',
|
|
||||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlanCreate'), activeMenu: '/xpack/change-auth-plan/plan', action: 'create' },
|
|
||||||
hidden: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'plan/:id/update',
|
|
||||||
component: () => import('@/views/xpack/ChangeAuthPlan/ChangeAuthPlanCreateUpdate.vue'),
|
|
||||||
name: 'ChangeAuthPlanUpdate',
|
|
||||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlanUpdate'), activeMenu: '/xpack/change-auth-plan/plan', action: 'update' },
|
|
||||||
hidden: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'plan/:id',
|
|
||||||
component: () => import('@/views/xpack/ChangeAuthPlan/ChangeAuthPlanDetail/index.vue'),
|
|
||||||
name: 'ChangeAuthPlanDetail',
|
|
||||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ChangeAuthPlan'), activeMenu: '/xpack/change-auth-plan/plan' },
|
|
||||||
hidden: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'plan-execution/:id',
|
|
||||||
component: () => import('@/views/xpack/ChangeAuthPlan/ChangeAuthPlanDetail/ChangeAuthPlanExecution/ChangeAuthPlanExecutionDetail/index.vue'),
|
|
||||||
name: 'ChangeAuthPlanExecutionDetail',
|
|
||||||
meta: { title: i18n.t('xpack.ChangeAuthPlan.ExecutionDetail'), activeMenu: '/xpack/change-auth-plan/plan' },
|
|
||||||
hidden: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'cloud',
|
path: 'cloud',
|
||||||
component: empty,
|
component: empty,
|
||||||
@ -127,55 +86,6 @@ export default {
|
|||||||
name: 'InterfaceSetting',
|
name: 'InterfaceSetting',
|
||||||
meta: { title: i18n.t('xpack.InterfaceSettings'), permissions: [rolec.PERM_SUPER] }
|
meta: { title: i18n.t('xpack.InterfaceSettings'), permissions: [rolec.PERM_SUPER] }
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'gathered-user',
|
|
||||||
component: empty,
|
|
||||||
redirect: '',
|
|
||||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserList') },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: () => import('@/views/xpack/GatheredUser/index'),
|
|
||||||
name: 'GatherUserListIndex',
|
|
||||||
meta: { title: i18n.t('xpack.GatherUser.GatherUser'), activeMenu: '/xpack/gathered-user' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: () => import('@/views/xpack/GatheredUser/GatheredUserList'),
|
|
||||||
name: 'GatherUserList',
|
|
||||||
hidden: true,
|
|
||||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserList'), activeMenu: '/xpack/gathered-user' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'tasks',
|
|
||||||
component: () => import('@/views/xpack/GatheredUser/TaskList'),
|
|
||||||
name: 'GatherUserTaskList',
|
|
||||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskList'), activeMenu: '/xpack/gathered-user' },
|
|
||||||
hidden: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'tasks/:id',
|
|
||||||
component: () => import('@/views/xpack/GatheredUser/TaskDetail/index'),
|
|
||||||
name: 'GatherUserTaskDetail',
|
|
||||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskDetail'), activeMenu: '/xpack/gathered-user' },
|
|
||||||
hidden: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'tasks/create',
|
|
||||||
component: () => import('@/views/xpack/GatheredUser/TaskCreateUpdate'),
|
|
||||||
name: 'GatherUserTaskCreate',
|
|
||||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskCreate'), activeMenu: '/xpack/gathered-user' },
|
|
||||||
hidden: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'tasks/:id/update',
|
|
||||||
component: () => import('@/views/xpack/GatheredUser/TaskCreateUpdate'),
|
|
||||||
name: 'GatherUserTaskUpdate',
|
|
||||||
meta: { title: i18n.t('xpack.GatherUser.GatherUserTaskUpdate'), action: 'update', activeMenu: '/xpack/gathered-user' },
|
|
||||||
hidden: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'orgs',
|
path: 'orgs',
|
||||||
component: empty,
|
component: empty,
|
||||||
@ -211,27 +121,6 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'vault',
|
|
||||||
component: empty,
|
|
||||||
redirect: '',
|
|
||||||
meta: { },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: () => import('@/views/xpack/Vault/VaultList.vue'),
|
|
||||||
name: 'VaultList',
|
|
||||||
meta: { title: i18n.t('xpack.Vault.Vault'), activeMenu: '/xpack/vault' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'create',
|
|
||||||
component: () => import('@/views/xpack/Vault/VaultCreate'),
|
|
||||||
name: 'VaultCreate',
|
|
||||||
meta: { title: i18n.t('xpack.Vault.Create'), activeMenu: '/xpack/vault' },
|
|
||||||
hidden: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'system-monitor',
|
path: 'system-monitor',
|
||||||
component: () => import('@/views/xpack/SystemMonitor/index.vue'),
|
component: () => import('@/views/xpack/SystemMonitor/index.vue'),
|
||||||
|
@ -4,12 +4,11 @@ const defaultSettings = require('./src/settings.js')
|
|||||||
const CompressionWebpackPlugin = require('compression-webpack-plugin')
|
const CompressionWebpackPlugin = require('compression-webpack-plugin')
|
||||||
const productionGzipExtensions = /\.(js|css|json|txt|ico|svg)(\?.*)?$/i
|
const productionGzipExtensions = /\.(js|css|json|txt|ico|svg)(\?.*)?$/i
|
||||||
|
|
||||||
|
|
||||||
function resolve(dir) {
|
function resolve(dir) {
|
||||||
return path.join(__dirname, dir)
|
return path.join(__dirname, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = defaultSettings.title || 'JumpServer' // page title
|
const name = '' // page title
|
||||||
|
|
||||||
// If your port is set to 80,
|
// If your port is set to 80,
|
||||||
// use administrator privileges to execute the command line.
|
// use administrator privileges to execute the command line.
|
||||||
|
68
yarn.lock
68
yarn.lock
@ -2357,13 +2357,15 @@ browserify-zlib@^0.2.0:
|
|||||||
pako "~1.0.5"
|
pako "~1.0.5"
|
||||||
|
|
||||||
browserslist@^4.0.0, browserslist@^4.3.4, browserslist@^4.5.4, browserslist@^4.8.3:
|
browserslist@^4.0.0, browserslist@^4.3.4, browserslist@^4.5.4, browserslist@^4.8.3:
|
||||||
version "4.10.0"
|
version "4.16.6"
|
||||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.10.0.tgz#f179737913eaf0d2b98e4926ac1ca6a15cbcc6a9"
|
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
|
||||||
|
integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite "^1.0.30001035"
|
caniuse-lite "^1.0.30001219"
|
||||||
electron-to-chromium "^1.3.378"
|
colorette "^1.2.2"
|
||||||
node-releases "^1.1.52"
|
electron-to-chromium "^1.3.723"
|
||||||
pkg-up "^3.1.0"
|
escalade "^3.1.1"
|
||||||
|
node-releases "^1.1.71"
|
||||||
|
|
||||||
bser@2.1.1:
|
bser@2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
@ -2556,9 +2558,10 @@ caniuse-api@^3.0.0:
|
|||||||
lodash.memoize "^4.1.2"
|
lodash.memoize "^4.1.2"
|
||||||
lodash.uniq "^4.5.0"
|
lodash.uniq "^4.5.0"
|
||||||
|
|
||||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001035:
|
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001219:
|
||||||
version "1.0.30001035"
|
version "1.0.30001230"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz#2bb53b8aa4716b2ed08e088d4dc816a5fe089a1e"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71"
|
||||||
|
integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==
|
||||||
|
|
||||||
capture-exit@^1.2.0:
|
capture-exit@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
@ -2874,6 +2877,11 @@ color@^3.0.0:
|
|||||||
color-convert "^1.9.1"
|
color-convert "^1.9.1"
|
||||||
color-string "^1.5.2"
|
color-string "^1.5.2"
|
||||||
|
|
||||||
|
colorette@^1.2.2:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
|
||||||
|
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
|
||||||
|
|
||||||
colors@^1.1.2:
|
colors@^1.1.2:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npm.taobao.org/colors/download/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
resolved "https://registry.npm.taobao.org/colors/download/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
||||||
@ -3605,8 +3613,9 @@ dns-equal@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
|
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
|
||||||
|
|
||||||
dns-packet@^1.3.1:
|
dns-packet@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a"
|
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f"
|
||||||
|
integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==
|
||||||
dependencies:
|
dependencies:
|
||||||
ip "^1.1.0"
|
ip "^1.1.0"
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
@ -3758,9 +3767,10 @@ ejs@^2.6.1:
|
|||||||
version "2.7.4"
|
version "2.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba"
|
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba"
|
||||||
|
|
||||||
electron-to-chromium@^1.3.378:
|
electron-to-chromium@^1.3.723:
|
||||||
version "1.3.379"
|
version "1.3.739"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.379.tgz#81dc5e82a3e72bbb830d93e15bc35eda2bbc910e"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz#f07756aa92cabd5a6eec6f491525a64fe62f98b9"
|
||||||
|
integrity sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A==
|
||||||
|
|
||||||
elegant-spinner@^1.0.1:
|
elegant-spinner@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
@ -3903,6 +3913,11 @@ es6-object-assign@1.1.0:
|
|||||||
resolved "https://registry.npm.taobao.org/es6-object-assign/download/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
resolved "https://registry.npm.taobao.org/es6-object-assign/download/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
||||||
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
||||||
|
|
||||||
|
escalade@^3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||||
|
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
||||||
|
|
||||||
escape-html@~1.0.3:
|
escape-html@~1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||||
@ -7324,6 +7339,11 @@ moment@^2.19.2:
|
|||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
|
||||||
integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
|
integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
|
||||||
|
|
||||||
|
moment@^2.29.1:
|
||||||
|
version "2.29.1"
|
||||||
|
resolved "https://registry.npm.taobao.org/moment/download/moment-2.29.1.tgz?cache=0&sync_timestamp=1601983320283&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmoment%2Fdownload%2Fmoment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||||
|
integrity sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M=
|
||||||
|
|
||||||
move-concurrently@^1.0.1:
|
move-concurrently@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
|
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
|
||||||
@ -7495,11 +7515,10 @@ node-notifier@^5.2.1:
|
|||||||
shellwords "^0.1.1"
|
shellwords "^0.1.1"
|
||||||
which "^1.3.0"
|
which "^1.3.0"
|
||||||
|
|
||||||
node-releases@^1.1.52:
|
node-releases@^1.1.71:
|
||||||
version "1.1.52"
|
version "1.1.72"
|
||||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9"
|
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
|
||||||
dependencies:
|
integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
|
||||||
semver "^6.3.0"
|
|
||||||
|
|
||||||
nopt@^5.0.0:
|
nopt@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
@ -8307,12 +8326,6 @@ pkg-up@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
find-up "^2.1.0"
|
find-up "^2.1.0"
|
||||||
|
|
||||||
pkg-up@^3.1.0:
|
|
||||||
version "3.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
|
||||||
dependencies:
|
|
||||||
find-up "^3.0.0"
|
|
||||||
|
|
||||||
please-upgrade-node@^3.2.0:
|
please-upgrade-node@^3.2.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
|
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
|
||||||
@ -9372,8 +9385,9 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
|
|
||||||
safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
||||||
version "5.2.0"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
|
|
||||||
safe-regex@^1.1.0:
|
safe-regex@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user