Merge pull request #1654 from jumpserver/dev

v2.21.0-rc1
This commit is contained in:
Jiangjie.Bai
2022-04-13 20:31:57 +08:00
committed by GitHub
118 changed files with 2850 additions and 454 deletions

View File

@@ -19,7 +19,7 @@ VUE_APP_CORE_HOST = 'JUMPSERVER_APIHOST'
$ yarn serve
4. 构建
$ yarn build
$ yarn build:prod
```
## 生产中部署

View File

@@ -1,4 +1,3 @@
const tokens = {
admin: {
token: 'admin-token'
@@ -46,7 +45,6 @@ export default [
}
}
},
// get user info
{
url: '/vue-admin-template/user/info\.*',

View File

@@ -7,7 +7,7 @@
:title="title"
@close="onClose"
>
<span class="announcement-main"> {{ announcement.content }}</span>
<span class="announcement-main">{{ announcement.content }}</span>
<span v-if="announcement.link">
<el-link :href="announcement.link" target="_blank" class="link-more">
{{ $t('common.ViewMore') }}
@@ -60,6 +60,7 @@ export default {
}
.announcement-main {
word-wrap:break-word;
white-space: pre-wrap;
}
.link-more {
font-size: 10px;

View File

@@ -1,5 +1,5 @@
<template>
<div :class="grouped ? 'el-button-group' : 'el-button-ungroup'">
<div :class="grouped ? 'el-button-group' : 'el-button-ungroup'" class="layout">
<template v-for="action in iActions">
<el-dropdown
v-if="action.dropdown"
@@ -13,9 +13,14 @@
<el-button :size="size" v-bind="cleanButtonAction(action)">
{{ action.title }}<i class="el-icon-arrow-down el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-menu slot="dropdown" style="overflow: auto;max-height: 60vh">
<template v-for="option in action.dropdown">
<div v-if="option.group" :key="'group:'+option.name" class="dropdown-menu-title" style="width:130px">
<div
v-if="option.group"
:key="'group:'+option.name"
class="dropdown-menu-title"
style="width:130px"
>
{{ option.group }}
</div>
<el-dropdown-item
@@ -149,6 +154,11 @@ export default {
</script>
<style scoped>
.layout {
display: flex;
justify-content: center;
}
.dropdown-menu-title {
text-align: left;
font-size: 12px;
@@ -165,7 +175,7 @@ export default {
}
.el-button-ungroup .action-item {
margin-left: 4px
margin-left: 4px;
}
.el-button-ungroup .action-item:first-child {

View File

@@ -1,9 +1,10 @@
<template>
<ElFormRender
ref="form"
:class="mobile? 'mobile' : 'desktop'"
:content="fields"
:form="basicForm"
label-position="right"
:label-position="labelPosition"
label-width="20%"
v-bind="$attrs"
v-on="$listeners"
@@ -68,6 +69,14 @@ export default {
basicForm: this.form
}
},
computed: {
mobile() {
return this.$store.state.app.device === 'mobile'
},
labelPosition() {
return this.mobile ? 'top' : 'right'
}
},
methods: {
// 获取表单数据
submitForm(formName, addContinue) {
@@ -99,27 +108,35 @@ export default {
</script>
<style lang="less" scoped>
.el-form ::v-deep .el-form-item {
.el-form ::v-deep .el-form-item {
margin-bottom: 12px;
}
.el-form ::v-deep .el-form-item__content {
.el-form ::v-deep .el-form-item__content {
width: 75%;
}
.el-form ::v-deep .el-form-item__label {
.mobile.el-form ::v-deep .el-form-item__content {
width: 100%;
}
.el-form ::v-deep .el-form-item__label {
padding: 0 30px 0 0;
}
.el-form ::v-deep .el-form-item__error {
.el-form ::v-deep .el-form-item__error {
position: inherit;
}
.el-form ::v-deep .form-group-header {
.el-form ::v-deep .form-group-header {
margin-left: 50px;
}
.el-form ::v-deep .help-block {
.el-form.mobile ::v-deep .form-group-header {
margin-left: 0;
}
.el-form ::v-deep .help-block {
display: block;
margin-top: 5px;
margin-bottom: 10px;
@@ -127,7 +144,7 @@ export default {
font-size: 12px;
line-height: 18px;
}
.el-form ::v-deep .help-block a {
.el-form ::v-deep .help-block a {
color: #1c84c6;
}
.form-buttons {

View File

@@ -2,6 +2,7 @@
<el-dialog
:title="title"
:top="top"
:width="iWidth"
class="dialog"
v-bind="$attrs"
append-to-body
@@ -39,6 +40,10 @@ export default {
type: String,
default: '3vh'
},
width: {
type: String,
default: '60%'
},
showConfirm: {
type: Boolean,
default: true
@@ -58,6 +63,11 @@ export default {
return {
}
},
computed: {
iWidth() {
return this.$store.getters.isMobile ? '80%' : this.width
}
},
methods: {
onCancel() {
this.$emit('cancel')

View File

@@ -91,18 +91,22 @@ export default {
}
</script>
<style lang='less' scoped>
<style lang='scss' scoped>
.datepicker{
width: 233px;
&>>> .el-range__icon {
margin-top: 2px;
margin-right: 3px;
}
&>>> .el-range-input {
width: 49%;
}
}
.el-input__inner{
border: 1px solid #dcdee2;
border-radius: 3px;
height: 36px;
}
/*.el-date-editor ::v-deep .el-input__icon{*/
/* line-height: 28px;*/
/*}*/
.el-date-editor ::v-deep .el-range-separator{
line-height: 28px;
}

View File

@@ -1,7 +1,7 @@
<template>
<div>
<el-row>
<el-col :span="8">
<el-col :md="8" :sm="24">
<div class="tableFilter">
<el-radio-group v-model="importStatusFilter" size="small">
<el-radio-button label="all">{{ $t('common.Total') }}</el-radio-button>
@@ -11,7 +11,7 @@
</el-radio-group>
</div>
</el-col>
<el-col :span="8" style="text-align: center">
<el-col :md="8" :sm="24" style="text-align: center">
<span class="summary-item summary-total"> {{ $t('common.Total') }}: {{ totalCount }}</span>
<span class="summary-item summary-success"> {{ $t('common.Success') }}: {{ successCount }}</span>
<span class="summary-item summary-failed"> {{ $t('common.Failed') }}: {{ failedCount }}</span>

View File

@@ -1,9 +1,9 @@
<template>
<div class="table-header clearfix">
<div class="table-header clearfix" :class="device">
<slot name="header">
<LeftSide v-if="hasLeftActions" style="float: left" :class="'left-side ' + device" :selected-rows="selectedRows" :table-url="tableUrl" v-bind="$attrs" v-on="$listeners" />
<RightSide v-if="hasRightActions" style="float: right" :selected-rows="selectedRows" :table-url="tableUrl" v-bind="$attrs" v-on="$listeners" />
<div style="display: flex;flex-direction: row" class="search" :class="hasLeftActions ? 'right' : 'left'">
<LeftSide v-if="hasLeftActions" class="left-side" :selected-rows="selectedRows" :table-url="tableUrl" v-bind="$attrs" v-on="$listeners" />
<RightSide v-if="hasRightActions" class="right-side" :selected-rows="selectedRows" :table-url="tableUrl" v-bind="$attrs" v-on="$listeners" />
<div class="search" :class="searchClass">
<AutoDataSearch v-if="hasSearch" class="right-side-item action-search" v-bind="iSearchTableConfig" @tagSearch="handleTagSearch" />
<DatetimeRangePicker v-if="hasDatePicker" v-bind="datePicker" class="datepicker" @dateChange="handleDateChange" />
</div>
@@ -77,6 +77,9 @@ export default {
return 'mobile'
}
return ''
},
searchClass() {
return this.hasLeftActions ? 'right' : 'left'
}
},
methods: {
@@ -143,11 +146,33 @@ export default {
.datepicker{
margin-left: 10px;
}
.left-side {
float: left;
}
.right-side {
float: right;
}
.search {
display: flex;
flex-direction: row
}
.mobile .search {
display: inherit;
}
.mobile .search .datepicker {
margin-left: 0;
}
.search.left {
float: left;
}
.search.right {
float: right;
}
.mobile .search.right {
float: left;
padding-top: 5px;
}
.mobile .right-side {
padding-top: 6px;
}
</style>

View File

@@ -192,6 +192,15 @@ export default {
& >>> .el-table__header thead > tr > th {
background-color: white;
}
&>>> .el-table__row .cell {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&>>> .el-table__expanded-cell pre {
max-height: 500px;
overflow-y: scroll;
}
}
//修改颜色

View File

@@ -10,14 +10,14 @@
v-on="$listeners"
>
<el-row :gutter="20">
<el-col :span="4">
<el-col :md="4" :sm="24">
<div style="line-height: 34px;text-align: center">MFA</div>
</el-col>
<el-col :span="14">
<el-col :md="14" :sm="24">
<el-input v-model="MFAToken" />
<span class="help-tips help-block">{{ $t('common.MFARequireForSecurity') }}</span>
</el-col>
<el-col :span="4">
<el-col :md="4" :sm="24">
<el-button size="mini" type="primary" style="line-height:20px " @click="verifyMFA">
{{ this.$t('common.Confirm') }}
</el-button>

View File

@@ -26,5 +26,19 @@ export default {
year: 'numeric', month: 'short', day: 'numeric',
hour: 'numeric', minute: 'numeric', hour12: true
}
},
'ja': {
short: {
year: 'numeric', month: 'short', day: 'numeric'
},
medium: {
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hourCycle: 'h23', hour12: false
},
long: {
year: 'numeric', month: 'short', day: 'numeric',
hour: 'numeric', minute: 'numeric', hour12: true
}
}
}

View File

@@ -7,8 +7,12 @@ import date from './date'
import VueCookie from 'vue-cookie'
Vue.use(VueI18n)
const cookieLang = VueCookie.get('django_language')
const browserLang = navigator.systemLanguage || navigator.language
let lang = cookieLang || browserLang || 'zh'
lang = lang.slice(0, 2)
const i18n = new VueI18n({
locale: (VueCookie.get('django_language') || 'zh-hans') === 'zh-hans' ? 'cn' : 'en',
locale: lang,
fallbackLocale: 'en',
silentFallbackWarn: true,
silentTranslationWarn: true,

View File

@@ -314,12 +314,14 @@
"Delete": "Delete",
"Disable": "Disable",
"Download": "Download",
"Copy": "Copy",
"Enable": "Enable",
"On/Off": "On/Off",
"EnterForSearch": "Press enter to search",
"Export": "Export",
"Import": "Import",
"ContinueImport": "ContinueImport",
"ImportAll": "Import All",
"Continue": "Continue",
"Stop": "Stop",
"Finished": "Finished",
@@ -416,8 +418,9 @@
},
"isValid": "Is valid",
"nav": {
"TempPassword": "Temporary password",
"APIKey": "API Key",
"Workspace": "Workbench",
"Workbench": "Workbench",
"Navigation": "Navigation",
"Console": "Console",
"Audits": "Audit",
@@ -651,6 +654,10 @@
},
"route": {
"": "",
"CreateEndpoint": "Create endpoint",
"UpdateEndpoint": "Update endpoint",
"CreateEndpointRule": "Create endpoint rule",
"UpdateEndpointRule": "Update endpoint rule",
"SystemSetting": "System setting",
"Index": "Index",
"Role": "Role",
@@ -767,6 +774,7 @@
"Activity": "Activity",
"SessionOffline": "Sessions offline",
"SessionOnline": "Sessions online",
"RecentSession": "Recent session",
"Sessions": "Sessions",
"Settings": "Settings",
"SystemUserCreate": "System user create",
@@ -982,6 +990,7 @@
"LDAPUser": "LDAP User",
"InsecureCommandAlert": "Insecure command alert",
"helpText": {
"TempPassword": "For a while, there is a period of 300 seconds, and there is a period of reuse.",
"ApiKeyList": "The API key is used to sign the request header. The header of each request is different. Please refer to the usage documentation",
"authLdapSearchFilter": "Choice may be (cn|uid|sAMAccountName)=%(user)s)",
"authLdapSearchOu": "Use | split User OUs",
@@ -1057,7 +1066,7 @@
"refreshLdapCache":"Refreshing Ldap cache ",
"LicenseExpired": "License expired",
"LicenseWillBe": "License will expire at ",
"Expire": "",
"Expire": "Expire",
"WeCom": "WeCom",
"DingTalk": "DingTalk",
"dingTalkTest": "Test",
@@ -1366,6 +1375,8 @@
"AWS_China": "AWS(China)",
"AWS_Int": "AWS(International)",
"HuaweiCloud": "Huawei Cloud",
"BaiduCloud": "Baidu Cloud",
"JDCloud": "JD Cloud",
"Azure":"Azure(China)",
"Azure_Int": "Azure(International)",
"HostnameStrategy": "Used to produce the asset hostname. For example, 1. Instance name (instanceDemo)2. Instance name and Partial IP (instanceDemo-250.1)",
@@ -1432,6 +1443,8 @@
"License": "License",
"LicenseDetail": "License detail",
"ComponentMonitor": "System Monitor",
"Endpoint": "Endpoint",
"EndpointRule": "Endpoint rule",
"ServiceRatio": "Service ratio",
"LoadStatus":"Status",
"NormalLoad":"Normal",

View File

@@ -1,15 +1,21 @@
import zhLocale from 'element-ui/lib/locale/lang/zh-CN'
import enLocale from 'element-ui/lib/locale/lang/en'
import zh from './cn.json'
import jaLocale from 'element-ui/lib/locale/lang/ja'
import zh from './zh.json'
import en from './en.json'
import ja from './ja.json'
export default {
cn: {
zh: {
...zhLocale,
...zh
},
en: {
...enLocale,
...en
},
ja: {
...jaLocale,
...ja
}
}

1555
src/i18n/langs/ja.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,8 @@ import json
i18n_report_path = '/tmp/abc.json'
lang_paths = {
'cn': 'cn.json',
'en': 'en.json'
'en': 'en.json',
'ja': 'ja.json'
}
@@ -51,5 +52,5 @@ def remove_keys(lang):
f.write(data)
for i in ['cn', 'en']:
for i in ['cn', 'en', 'ja']:
remove_keys(i)

View File

@@ -254,6 +254,7 @@
"ReLogin": "重新登录"
},
"common": {
"Component": "组件",
"PrivateCloud": "私有云",
"PublicCloud": "公有云",
"Correlation": "关联",
@@ -326,12 +327,14 @@
"Delete": "删除",
"Disable": "禁用",
"Download": "下载",
"Copy": "复制",
"Enable": "启用",
"On/Off": "启/停",
"EnterForSearch": "按回车进行搜索",
"Export": "导出",
"Import": "导入",
"ContinueImport": "继续导入",
"ImportAll": "导入全部",
"Continue": "继续",
"Stop": "停止",
"Finished": "完成",
@@ -428,8 +431,9 @@
"fileType": "文件类型",
"isValid": "有效",
"nav": {
"TempPassword": "临时密码",
"APIKey": "API Key",
"Workspace": "工作台",
"Workbench": "工作台",
"Navigation": "导航",
"Console": "控制台",
"Audits": "审计台",
@@ -661,6 +665,10 @@
},
"route": {
"": "",
"CreateEndpoint": "创建端点",
"UpdateEndpoint": "更新端点",
"CreateEndpointRule": "创建端点规则",
"UpdateEndpointRule": "更新端点规则",
"Index": "首页",
"SystemSetting": "系统设置",
"WorkBench": "工作台",
@@ -783,6 +791,7 @@
"Detail": "详情",
"SessionOffline": "历史会话",
"SessionOnline": "在线会话",
"RecentSession": "最近会话",
"Sessions": "会话管理",
"Settings": "系统设置",
"SystemUserCreate": "创建系统用户",
@@ -1008,6 +1017,7 @@
"LDAPServerInfo": "LDAP 服务器",
"LDAPUser": "LDAP 用户",
"helpText": {
"TempPassword": "临时密码有效期为 300 秒,有效期内可重复使用",
"ApiKeyList": "使用api key签名请求头每个请求的头部是不一样的, 请查阅使用文档",
"authLdapSearchFilter": "可能的选项是(cn或uid或sAMAccountName=%(user)s)",
"authLdapSearchOu": "使用|分隔各OU",
@@ -1409,6 +1419,8 @@
"AWS_China": "AWS(中国)",
"AWS_Int": "AWS(国际)",
"HuaweiCloud": "华为云",
"BaiduCloud": "百度云",
"JDCloud": "京东云",
"Azure":"Azure(中国)",
"Azure_Int": "Azure(国际)",
"HostnameStrategy": "用于生成资产主机名。例如1. 实例名称 (instanceDemo)2. 实例名称和部分IP(后两位) (instanceDemo-250.1)",
@@ -1477,6 +1489,8 @@
"InterfaceSettings": "界面设置",
"License": "许可证",
"ComponentMonitor": "组件监控",
"Endpoint": "终端节点",
"EndpointRule": "终端节点规则",
"ServiceRatio": "组件负载统计",
"LoadStatus":"组件状态",
"NormalLoad":"正常",

View File

@@ -7,15 +7,22 @@
:show-cancel="false"
:show-confirm="false"
>
<el-row>
<el-col :span="4">
<el-row :gutter="20">
<el-col :md="4" :sm="24">
<div class="select-prop-label">
<label>{{ selectPropertiesLabel }}</label>
</div>
</el-col>
<el-col :span="18">
<el-col :md="18" :sm="24">
<el-checkbox-group v-model="checkedFields" @change="handleCheckedFieldsChange">
<el-checkbox v-for="(value, name) in iFormSetting.fieldsMeta" :key="name" :checked="true" :label="name">{{ value.label }}</el-checkbox>
<el-checkbox
v-for="(value, name) in iFormSetting.fieldsMeta"
:key="name"
:checked="true"
:label="name"
>
{{ value.label }}
</el-checkbox>
</el-checkbox-group>
</el-col>
</el-row>

View File

@@ -9,6 +9,7 @@
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-user" command="profile">{{ $t('common.nav.Profile') }}</el-dropdown-item>
<el-dropdown-item v-if="$hasPerm('authentication.view_accesskey')" icon="el-icon-key" command="apiKey">{{ $t('common.nav.APIKey') }}</el-dropdown-item>
<el-dropdown-item icon="el-icon-magic-stick" command="tempPassword">{{ $t('common.nav.TempPassword') }}</el-dropdown-item>
<el-dropdown-item divided command="logout"><svg-icon icon-class="logout" style="margin-right: 4px" />{{ $t('common.nav.Logout') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@@ -50,6 +51,8 @@ export default {
case 'apiKey':
this.$router.push('/profile/key')
break
case 'tempPassword':
this.$router.push('/profile/tempPassword')
}
},
logout() {
@@ -65,4 +68,7 @@ export default {
margin-right: 5px;
vertical-align: middle;
}
.mobile .header-avatar {
display: none;
}
</style>

View File

@@ -25,6 +25,11 @@ export default {
title: '中文(简体)',
code: 'cn',
cookieCode: 'zh-hans' // cookie code是为了让后端知道当前语言
},
{
title: '日本語',
code: 'ja',
cookieCode: 'ja' // cookie code是为了让后端知道当前语言
}
]
}
@@ -58,6 +63,8 @@ export default {
changeMomentLang() {
if (this.currentLang.code.indexOf('en') > -1) {
this.$moment.locale('en')
} else if (this.currentLang.code.indexOf('ja') > -1) {
this.$moment.locale('ja')
} else {
this.$moment.locale('zh-cn')
}

View File

@@ -146,25 +146,25 @@ export default {
background-color: rgba(144, 147, 152, .5);
}
& > > > .el-input__prefix {
&>>> .el-input__prefix {
left: 8px
}
& > > > .el-input--prefix .el-input__inner {
&>>> .el-input--prefix .el-input__inner {
line-height: 35px !important;
height: 35px !important;
}
& > > > .fa-sitemap {
&>>> .fa-sitemap {
padding-left: 4px;
}
& > > > .el-input__icon {
&>>> .el-input__icon {
color: #606266;
}
}
.option-group > > > .el-select-group__title {
.option-group >>> .el-select-group__title {
color: #909399 !important;
padding-left: 15px;
font-size: 12px;

View File

@@ -2,7 +2,8 @@
<el-menu
default-active="activeIndex"
class="menu-main"
mode="horizontal"
:class="mode"
:mode="mode"
@select="handleSelectView"
>
<el-submenu
@@ -21,7 +22,7 @@
v-perms="view.perms"
:index="view.name"
>
<i class="icons" :class="view.meta.icon" />
<i v-if="mode === 'horizontal'" class="icons" :class="view.meta.icon" />
<span slot="title" class="icons-title">{{ view.meta.title }}</span>
</el-menu-item>
</el-submenu>
@@ -37,6 +38,10 @@ export default {
showTitle: {
type: Boolean,
default: false
},
mode: {
type: String,
default: 'horizontal'
}
},
data() {
@@ -131,4 +136,8 @@ export default {
.el-menu-item.is-active {
font-weight: bold;
}
.menu-main.mobile-view-switch >>> .el-submenu__icon-arrow {
right: 10px;
}
</style>

View File

@@ -4,13 +4,13 @@
<div class="nav-logo">
<Logo v-if="showLogo" :collapse="isCollapse" />
</div>
<div class="active-mobile">
<ViewSwitcher mode="vertical" class="mobile-view-switch" />
<Organization class="organization" />
</div>
<div class="nav-title" :class="{'collapsed': isCollapse}">
{{ isTitle }}
</div>
<div class="active-mobile">
<ViewSwitcher />
<Organization class="organization" />
</div>
</div>
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
@@ -174,6 +174,9 @@ export default {
&>>> .menu-main {
margin-left: -10px;
}
&>>> .title-label {
color: white !important;
}
}
@media screen and (max-width: 992px) {
.active-mobile {

View File

@@ -7,7 +7,9 @@
</template>
</PageHeading>
<PageContent>
<el-alert v-if="helpMessage" type="success"> <span v-html="helpMessage" /> </el-alert>
<el-alert v-if="helpMessage" type="success">
<span class="announcement-main">{{ helpMessage }}</span>
</el-alert>
<slot />
</PageContent>
</div>
@@ -34,7 +36,6 @@ export default {
},
computed: {
iTitle() {
// return ''
return this.title || this.$route.meta.title
}
}
@@ -52,5 +53,9 @@ export default {
.print-margin{
margin-top: 10px;
}
.announcement-main {
word-wrap:break-word;
white-space: pre-wrap;
}
}
</style>

View File

@@ -30,8 +30,12 @@
</el-tab-pane>
</template>
</el-tabs>
<transition name="fade-transform" mode="out-in">
<slot />
<transition name="fade-transform" mode="out-in" appear>
<slot>
<keep-alive>
<component :is="computeActiveComponent" />
</keep-alive>
</slot>
</transition>
</div>
</Page>
@@ -76,9 +80,19 @@ export default {
}
})
return map
},
computeActiveComponent() {
let needActiveComponent = ''
for (const i of this.submenu) {
if (i.component && (i.name === this.iActiveMenu)) {
needActiveComponent = i.component
break
}
}
return needActiveComponent
}
},
mounted() {
created() {
this.iActiveMenu = this.getPropActiveTab()
},
methods: {
@@ -106,7 +120,7 @@ export default {
const currentTab = typeof preTab === 'object' ? preTab.name : preTab
for (const tabName of this.tabIndices) {
const currentTabName = tabName?.name || ''
if (currentTab && currentTabName && currentTab.toLowerCase() === currentTabName.toLowerCase()) {
if (currentTab?.toLowerCase() === currentTabName?.toLowerCase()) {
return currentTabName
}
}

View File

@@ -79,7 +79,7 @@ export const constantRoutes = [
// 权限路由
import consoleViewRoutes from './console'
import auditViewRoutes from './audit'
import workspaceViewRoutes from './workspace'
import workbenchViewRoutes from './workbench'
import ticketsRoutes from './tickets'
import settingsRoutes from './settings'
import profileRoutes from './profile'
@@ -92,7 +92,7 @@ import { getPermedPreferView } from '@/utils/jms'
export const viewRoutes = [
consoleViewRoutes,
auditViewRoutes,
workspaceViewRoutes,
workbenchViewRoutes,
ticketsRoutes,
settingsRoutes,
profileRoutes

View File

@@ -53,6 +53,16 @@ export default {
resource: 'accesskey',
app: 'authentication'
}
},
{
path: '/profile/tempPassword',
component: () => import('@/views/profile/TempPassword'),
name: 'TempPassword',
meta: {
title: i18n.t('common.nav.TempPassword'),
icon: 'magic',
permissions: []
}
}
]
}

View File

@@ -85,7 +85,7 @@ export default {
component: () => import('@/views/settings/Terminal'),
meta: {
title: i18n.t('setting.Terminal'),
icon: 'terminal',
icon: 'tasks',
permissions: ['settings.change_terminal']
}
},
@@ -152,6 +152,50 @@ export default {
permissions: ['terminal.change_commandstorage']
},
hidden: true
},
{
path: 'endpoint/create',
name: 'EndpointCreate',
component: () => import('@/views/settings/Terminal/Endpoint/EndpointCreateUpdate'),
meta: {
title: i18n.t('route.CreateEndpoint'),
activeMenu: '/settings/terminal',
permissions: ['terminal.add_endpoint']
},
hidden: true
},
{
path: 'endpoint/:id/update',
name: 'EndpointUpdate',
component: () => import('@/views/settings/Terminal/Endpoint/EndpointCreateUpdate'),
meta: {
title: i18n.t('route.UpdateEndpoint'),
activeMenu: '/settings/terminal',
permissions: ['terminal.change_endpoint']
},
hidden: true
},
{
path: 'endpoint-rule/create',
name: 'EndpointRuleCreate',
component: () => import('@/views/settings/Terminal/EndpointRule/EndpointRuleCreateUpdate'),
meta: {
title: i18n.t('route.CreateEndpointRule'),
activeMenu: '/settings/terminal',
permissions: ['terminal.add_endpointrule']
},
hidden: true
},
{
path: 'endpoint-rule/:id/update',
name: 'EndpointRuleUpdate',
component: () => import('@/views/settings/Terminal/EndpointRule/EndpointRuleCreateUpdate'),
meta: {
title: i18n.t('route.UpdateEndpointRule'),
activeMenu: '/settings/terminal',
permissions: ['terminal.change_endpointrule']
},
hidden: true
}
]
},

View File

@@ -4,23 +4,23 @@ import { BASE_URL } from '@/utils/common'
import empty from '@/layout/empty'
export default {
path: '/workspace/',
path: '/workbench/',
component: Layout,
name: 'workspace',
redirect: '/workspace/home',
name: 'workbench',
redirect: '/workbench/home',
meta: {
title: i18n.t('common.nav.Workspace'),
title: i18n.t('common.nav.Workbench'),
type: 'view',
view: 'workspace',
view: 'workbench',
icon: 'el-icon-user-solid',
showNavSwitcher: true,
showOrganization: true,
permissions: ['rbac.view_workspace']
permissions: ['rbac.view_workbench']
},
children: [
// 404 page must be placed at the end !!!
{
path: '/workspace/home',
path: '/workbench/home',
name: 'MyHome',
component: () => import('@/views/myhome'),
meta: {
@@ -30,7 +30,7 @@ export default {
}
},
{
path: '/workspace/assets',
path: '/workbench/assets',
name: 'MyAssets',
component: () => import('@/views/myassets'),
meta: {
@@ -40,7 +40,7 @@ export default {
}
},
{
path: '/workspace/apps',
path: '/workbench/apps',
name: 'Apps',
component: empty,
redirect: 'remoteapp',
@@ -82,7 +82,7 @@ export default {
]
},
{
path: '/workspace/ops',
path: '/workbench/ops',
component: empty,
meta: {
permissions: ['ops.add_commandexecution'],

View File

@@ -1,6 +1,7 @@
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
isMobile: state => state.app.device === 'mobile',
token: state => state.users.token,
currentOrg: state => state.users.currentOrg,
currentOrgIsDefault: state => state.users.currentOrg['is_default'],

View File

@@ -170,3 +170,11 @@ input[type=file] {
min-width: 70px!important;
}
}
.el-col.el-col-sm-24 .ibox {
margin-bottom: 10px;
}
.el-pagination {
overflow: auto;
}

View File

@@ -109,7 +109,7 @@ export function getApiPath(that) {
// ticket ...
pagePath = pagePathArray.slice(1, pagePathArray.length).join('/')
} else {
// console,audit,workspace
// console,audit,workbench
pagePath = pagePathArray.slice(2, pagePathArray.length).join('/')
}
return `/api/v1/${pagePath}/`
@@ -276,5 +276,19 @@ const scheme = document.location.protocol
const port = document.location.port ? ':' + document.location.port : ''
const BASE_URL = scheme + '//' + document.location.hostname + port
export function groupedDropdownToCascader(group) {
const firstType = group[0]
return {
value: firstType.category,
label: firstType.group,
children: group.map(item => {
return {
value: item.name,
label: item.title
}
})
}
}
export { BASE_URL }

View File

@@ -43,7 +43,6 @@ export function getResourceNameByPath(path) {
export function getResourceFromApiUrl(apiUrl) {
const re = new RegExp('/api/v1/([A-Za-z0-9_-]+)/([A-Za-z0-9_-]+)/.*')
console.log('Api url: ', apiUrl)
const matched = apiUrl.match(re)
if (!matched) {
return { path: '', app: '', resource: '' }
@@ -84,7 +83,7 @@ export function hasActionPerm(route, action) {
const viewRequirePermsMapper = {
console: 'rbac.view_console',
audit: 'rbac.view_audit',
workspace: 'rbac.view_workspace'
workbench: 'rbac.view_workbench'
}
export function getViewRequirePerms(view) {

View File

@@ -1,21 +1,14 @@
<template>
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu">
<keep-alive>
<component :is="config.activeMenu" />
</keep-alive>
</TabPage>
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu" />
</template>
<script>
import { TabPage } from '@/layout/components'
import AssetChangeAuthPlanList from './AssetChangeAuthPlan/ChangeAuthPlanList'
import AppChangeAuthPlanList from './AppChangeAuthPlan/AppChangeAuthPlanList'
export default {
name: 'Index',
components: {
TabPage,
AssetChangeAuthPlanList,
AppChangeAuthPlanList
TabPage
},
data() {
return {
@@ -25,13 +18,14 @@ export default {
{
title: this.$t('xpack.ChangeAuthPlan.AssetChangeAuthPlan'),
name: 'AssetChangeAuthPlanList',
hidden: () => !this.$hasPerm('xpack.view_changeauthplan')
hidden: () => !this.$hasPerm('xpack.view_changeauthplan'),
component: () => import('@/views/accounts/ChangeAuthPlan/AssetChangeAuthPlan/ChangeAuthPlanList.vue')
},
{
title: this.$t('xpack.ChangeAuthPlan.AppChangeAuthPlan'),
name: 'AppChangeAuthPlanList',
hidden: () => !this.$hasPerm('xpack.view_applicationchangeauthplan')
hidden: () => !this.$hasPerm('xpack.view_applicationchangeauthplan'),
component: () => import('@/views/accounts/ChangeAuthPlan/AppChangeAuthPlan/AppChangeAuthPlanList.vue')
}
]
}

View File

@@ -1,6 +1,6 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
</el-row>

View File

@@ -1,21 +1,14 @@
<template>
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu">
<keep-alive>
<component :is="config.activeMenu" />
</keep-alive>
</TabPage>
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu" />
</template>
<script>
import { TabPage } from '@/layout/components'
import GatheredUserList from './GatheredUserList'
import TaskList from './TaskList'
export default {
name: 'Index',
components: {
TabPage,
GatheredUserList,
TaskList
TabPage
},
data() {
return {
@@ -25,12 +18,14 @@ export default {
{
title: this.$t('xpack.GatherUser.GatherUserList'),
name: 'GatheredUserList',
hidden: !this.$hasPerm('assets.view_gathereduser')
hidden: !this.$hasPerm('assets.view_gathereduser'),
component: () => import('@/views/accounts/GatheredUser/GatheredUserList.vue')
},
{
title: this.$t('xpack.GatherUser.GatherUserTaskList'),
name: 'TaskList',
hidden: !this.$hasPerm('xpack.view_gatherusertask')
hidden: !this.$hasPerm('xpack.view_gatherusertask'),
component: () => import('@/views/accounts/GatheredUser/TaskList.vue')
}
]
}

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<!-- <RelationCard ref="RelationCard" type="info" v-bind="nodeRelationConfig" />-->
</el-col>
</el-row>

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<QuickActions type="primary" :actions="quickActions" />
</el-col>
</el-row>

View File

@@ -17,11 +17,6 @@ export default {
],
fieldsMeta: {
type: {
type: 'select',
options: [{
label: 'MySQL',
value: 'mysql'
}],
disabled: true
},
domain: {

View File

@@ -4,7 +4,7 @@
<script>
import { GenericListPage } from '@/layout/components'
import { DATABASE } from '@/views/applications/const'
import { DATABASE, KV_DATABASE } from '@/views/applications/const'
function getAppType(arr) {
const searchAppType = []
@@ -25,7 +25,7 @@ export default {
},
data() {
const vm = this
const appType = DATABASE
const appType = [...DATABASE, ...KV_DATABASE]
return {
tableConfig: {
url: '/api/v1/applications/applications/?category=db',

View File

@@ -1,5 +1,7 @@
import i18n from '@/i18n/i18n'
import store from '@/store'
import { groupedDropdownToCascader } from '@/utils/common'
export const CHROME = 'chrome'
export const MYSQL_WORKBENCH = 'mysql_workbench'
export const VMWARE_CLIENT = 'vmware_client'
@@ -87,7 +89,10 @@ export const DATABASE = [
type: 'primary',
category: DATABASE_CATEGORY,
has: hasLicence
},
}
]
export const KV_DATABASE = [
{
name: REDIS,
title: i18n.t(`applications.applicationsType.${REDIS}`),
@@ -95,53 +100,17 @@ export const DATABASE = [
category: DATABASE_CATEGORY,
has: true,
group: i18n.t('applications.NoSQLProtocol')
}
// {
// name: MONGODB,
// title: i18n.t(`applications.applicationsType.${MONGODB}`),
// type: 'primary',
// category: DATABASE_CATEGORY
// }
]
export const AppPlanDatabase = [
{
name: MYSQL,
title: i18n.t(`applications.applicationsType.${MYSQL}`),
type: 'primary',
category: DATABASE_CATEGORY,
has: true,
group: i18n.t('applications.RDBProtocol')
},
{
name: ORACLE,
title: i18n.t(`applications.applicationsType.${ORACLE}`),
type: 'primary',
category: DATABASE_CATEGORY,
has: hasLicence
},
{
name: POSTGRESQL,
title: i18n.t(`applications.applicationsType.${POSTGRESQL}`),
type: 'primary',
category: DATABASE_CATEGORY,
has: hasLicence
},
{
name: MARIADB,
title: i18n.t(`applications.applicationsType.${MARIADB}`),
name: MONGODB,
title: i18n.t(`applications.applicationsType.${MONGODB}`),
type: 'primary',
category: DATABASE_CATEGORY
},
{
name: SQLSERVER,
title: i18n.t(`applications.applicationsType.${SQLSERVER}`),
type: 'primary',
category: DATABASE_CATEGORY,
has: hasLicence
}
]
export const AppPlanDatabase = DATABASE
export const KUBERNETES = 'k8s'
export const CLOUD_CATEGORY = 'cloud'
@@ -157,5 +126,16 @@ export const CLOUD = [
]
export const ApplicationTypes = [
...REMOTE_APP, ...DATABASE, ...CLOUD
...DATABASE, ...KV_DATABASE, ...REMOTE_APP, ...CLOUD
]
export const ApplicationSystemUserTypes = [
...DATABASE, ...KV_DATABASE, ...CLOUD
]
export const ApplicationCascader = [
groupedDropdownToCascader(DATABASE),
groupedDropdownToCascader(KV_DATABASE),
groupedDropdownToCascader(REMOTE_APP),
groupedDropdownToCascader(CLOUD)
]

View File

@@ -1,10 +1,10 @@
<template>
<div>
<el-row :gutter="24">
<el-col :span="16">
<el-col :md="16" :sm="24">
<AccountListTable ref="ListTable" :url="assetUserUrl" :has-import="false" :has-clone="false" />
</el-col>
<el-col :span="8">
<el-col :md="8" :sm="24">
<QuickActions type="primary" :actions="quickActions" />
</el-col>
</el-row>

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<QuickActions type="primary" :actions="quickActions" />
<RelationCard ref="NodeRelation" v-perms="'assets.change_asset'" type="info" style="margin-top: 15px" v-bind="nodeRelationConfig" />
<LabelCard v-if="$hasPerm('assets.view_label')" type="warning" style="margin-top: 15px" v-bind="labelConfig" />

View File

@@ -1,10 +1,10 @@
<template>
<div>
<el-row :gutter="20">
<el-col :span="16">
<el-col :md="16" :sm="24">
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
<el-col :span="8">
<el-col :md="8" :sm="24">
<PermUserGroupCard v-bind="UserGroupCardConfig" />
</el-col>
</el-row>
@@ -54,6 +54,7 @@ export default {
},
columnsMeta: {
name: {
formatter: vm.$hasPerm('users.view_user') ? DetailFormatter : '',
formatterArgs: {
route: 'UserDetail'
},

View File

@@ -1,10 +1,10 @@
<template>
<div>
<el-row :gutter="20">
<el-col :span="16">
<el-col :md="16" :sm="24">
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
<el-col :span="8">
<el-col :md="8" :sm="24">
<QuickActions type="primary" :actions="quickActions" />
<RelationCard ref="systemUserRelation" style="margin-top: 15px" v-bind="systemUserRelationConfig" />
</el-col>

View File

@@ -36,7 +36,7 @@ export default {
{
title: this.$t('assets.SystemUser'),
name: 'SystemUserList',
hidden: () => !this.$hasPerm('assets.view_systemuser')
hidden: () => !this.$hasPerm('assets.view_authbook')
},
{
title: this.$t('assets.AccountList'),

View File

@@ -34,8 +34,8 @@
</GenericTreeListPage>
<Dialog width="30%" :title="this.$t('assets.NodeInformation')" :visible.sync="nodeInfoDialogSetting.dialogVisible" :show-cancel="false" :show-confirm="false">
<el-row v-for="item in nodeInfoDialogSetting.items" :key="'card-' + item.key" :gutter="10" class="item">
<el-col :span="6"><div class="item-label"><label>{{ item.label }}: </label></div></el-col>
<el-col :span="18"><div class="item-text">{{ item.value }}</div></el-col>
<el-col :md="6" :sm="24"><div class="item-label"><label>{{ item.label }}: </label></div></el-col>
<el-col :md="18" :sm="24"><div class="item-text">{{ item.value }}</div></el-col>
</el-row>
</Dialog>
<AssetBulkUpdateDialog
@@ -51,7 +51,11 @@
<script>
import GenericTreeListPage from '@/layout/components/GenericTreeListPage/index'
import { DetailFormatter, ActionsFormatter, TagsFormatter } from '@/components/TableFormatters'
import {
DetailFormatter,
ActionsFormatter,
TagsFormatter
} from '@/components/TableFormatters'
import $ from '@/utils/jquery-vendor'
import Dialog from '@/components/Dialog'
import { mapGetters } from 'vuex'
@@ -88,17 +92,14 @@ export default {
hasTree: true,
columns: [
'hostname', 'ip', 'public_ip', 'admin_user_display',
'protocols', 'platform', 'hardware_info', 'model',
'cpu_model', 'cpu_cores', 'cpu_count', 'cpu_vcpus',
'disk_info', 'disk_total', 'memory', 'os', 'os_arch',
'os_version', 'number', 'vendor', 'sn', 'is_active',
'protocols', 'category', 'type', 'platform', 'sn', 'is_active',
'connectivity', 'labels_display',
'created_by', 'date_created', 'comment', 'org_name', 'actions'
],
columnsShow: {
min: ['hostname', 'ip', 'actions'],
default: [
'hostname', 'ip', 'platform', 'protocols', 'hardware_info',
'hostname', 'ip', 'platform', 'category', 'type',
'connectivity', 'actions'
]
},
@@ -237,12 +238,12 @@ export default {
name: 'RemoveFromCurrentNode',
title: this.$t('assets.RemoveFromCurrentNode'),
can: ({ selectedRows }) => {
if (!this.$route.query.node) {
if (!vm.$route.query.node) {
return false
}
return selectedRows.length > 0 &&
!vm.currentOrgIsRoot &&
vm.$hasPerm('assets.change_asset')
vm.$hasPerm('assets.change_node')
},
callback: function({ selectedRows, reloadTable }) {
const assetsId = []

View File

@@ -4,9 +4,10 @@
<script type="text/jsx">
import GenericListTable from '@/layout/components/GenericListTable'
import { ACCOUNT_PROVIDER_ATTRS_MAP, aliyun, aws_china, aws_international, huaweicloud, qcloud, azure, azure_international, vmware, nutanix, qingcloud_private, huaweicloud_private, openstack, gcp } from '../const'
import { ACCOUNT_PROVIDER_ATTRS_MAP, aliyun, aws_china, aws_international, huaweicloud, qcloud, azure, azure_international, vmware, nutanix, qingcloud_private, huaweicloud_private, openstack, gcp, baiducloud, jdcloud } from '../const'
export default {
name: 'AccountList',
components: {
GenericListTable
},
@@ -85,6 +86,14 @@ export default {
name: huaweicloud,
title: ACCOUNT_PROVIDER_ATTRS_MAP[huaweicloud].title
},
{
name: baiducloud,
title: ACCOUNT_PROVIDER_ATTRS_MAP[baiducloud].title
},
{
name: jdcloud,
title: ACCOUNT_PROVIDER_ATTRS_MAP[jdcloud].title
},
{
name: aws_china,
title: ACCOUNT_PROVIDER_ATTRS_MAP[aws_china].title

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<QuickActions type="primary" :actions="quickActions" />
</el-col>
</el-row>

View File

@@ -8,6 +8,7 @@ import { DetailFormatter } from '@/components/TableFormatters'
import { openTaskPage } from '@/utils/jms'
export default {
name: 'SyncInstanceTaskList',
components: {
GenericListTable
},

View File

@@ -13,6 +13,8 @@ export const qingcloud_private = 'qingcloud_private'
export const huaweicloud_private = 'huaweicloud_private'
export const openstack = 'openstack'
export const gcp = 'gcp'
export const baiducloud = 'baiducloud'
export const jdcloud = 'jdcloud'
export const ACCOUNT_PROVIDER_ATTRS_MAP = {
[aliyun]: {
@@ -35,6 +37,16 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
title: i18n.t('xpack.Cloud.HuaweiCloud'),
attrs: ['access_key_id', 'access_key_secret']
},
[baiducloud]: {
name: baiducloud,
title: i18n.t('xpack.Cloud.BaiduCloud'),
attrs: ['access_key_id', 'access_key_secret']
},
[jdcloud]: {
name: jdcloud,
title: i18n.t('xpack.Cloud.JDCloud'),
attrs: ['access_key_id', 'access_key_secret']
},
[qcloud]: {
name: qcloud,
title: i18n.t('xpack.Cloud.Qcloud'),

View File

@@ -1,21 +1,14 @@
<template>
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu">
<keep-alive>
<component :is="config.activeMenu" />
</keep-alive>
</TabPage>
<TabPage :active-menu.sync="config.activeMenu" :submenu="config.submenu" />
</template>
<script>
import { TabPage } from '@/layout/components'
import AccountList from './Account/AccountList'
import SyncInstanceTaskList from './SyncInstanceTask/SyncInstanceTaskList'
export default {
name: 'Index',
name: 'CloudIndex',
components: {
TabPage,
AccountList,
SyncInstanceTaskList
TabPage
},
data() {
return {
@@ -25,13 +18,14 @@ export default {
{
title: this.$t('xpack.Cloud.SyncInstanceTaskList'),
name: 'SyncInstanceTaskList',
hidden: () => !this.$hasPerm('xpack.view_syncinstancetask')
hidden: () => !this.$hasPerm('xpack.view_syncinstancetask'),
component: () => import('@/views/assets/Cloud/SyncInstanceTask/SyncInstanceTaskList.vue')
},
{
title: this.$t('xpack.Cloud.AccountList'),
name: 'AccountList',
hidden: () => !this.$hasPerm('xpack.view_account')
hidden: () => !this.$hasPerm('xpack.view_account'),
component: () => import('@/views/assets/Cloud/Account/AccountList.vue')
}
]
}

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<RelationCard ref="systemUserRelation" v-bind="systemUserRelationConfig" />
</el-col>
</el-row>

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10" />
<el-col :md="10" :sm="24" />
</el-row>
</template>

View File

@@ -12,14 +12,14 @@
:destroy-on-close="true"
>
<el-row :gutter="20">
<el-col :span="4">
<div style="line-height: 34px;text-align: center">{{ $t('assets.SshPort') }}</div>
<el-col :md="4" :sm="24">
<div style="line-height: 34px">{{ $t('assets.SshPort') }}</div>
</el-col>
<el-col :span="14">
<el-col :md="14" :sm="24">
<el-input v-model="portInput" />
<span class="help-tips help-block">{{ $t('assets.TestGatewayHelpMessage') }}</span>
</el-col>
<el-col :span="4">
<el-col :md="4" :sm="24">
<el-button size="mini" type="primary" style="line-height:20px " :loading="buttonLoading" @click="dialogConfirm">{{ this.$t('common.Confirm') }}</el-button>
</el-col>
</el-row>

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10" />
<el-col :md="10" :sm="24" />
</el-row>
</template>

View File

@@ -29,6 +29,7 @@ export default {
[this.$t('common.Basic'), ['name', 'protocol', 'username', 'type']],
[this.$t('common.Auth'), ['password', 'private_key', 'passphrase']],
[this.$t('common.Command filter'), ['cmd_filters']],
[this.$t('assets.UserSwitch'), ['su_enabled', 'su_from']],
[this.$t('common.Other'), ['priority', 'sftp_root', 'comment']]
],
fieldsMeta: {
@@ -73,7 +74,9 @@ export default {
rules: [Required],
helpText: this.$t('assets.SFTPHelpMessage')
},
cmd_filters: fields.cmd_filters
cmd_filters: fields.cmd_filters,
su_enabled: fields.su_enabled,
su_from: fields.su_from
},
cleanFormValue: (values) => {
values['type'] = 'admin'

View File

@@ -184,6 +184,26 @@ function getFields() {
const type = {
}
const su_enabled = {
type: 'switch',
hidden: (item) => item.protocol !== 'ssh'
}
const su_from = {
hidden: (item) => !item.su_enabled,
rules: [Required],
el: {
multiple: false,
clearable: true,
ajax: {
url: '/api/v1/assets/system-users/su-from/',
transformOption: (item) => {
return { label: item.name + '(' + item.username + ')', value: item.id }
}
}
}
}
return {
login_mode: login_mode,
username: username,
@@ -197,7 +217,9 @@ function getFields() {
password: password,
passphrase: passphrase,
system_groups: system_groups,
type: type
type: type,
su_enabled: su_enabled,
su_from: su_from
}
}

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="20">
<el-col :md="20" :sm="24">
<AccountListTable ref="ListTable" :url="accountUrl" :has-import="false" :has-clone="false" />
</el-col>
<el-col :span="4" />
<el-col :md="4" :sm="24" />
</el-row>
</template>

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="20">
<el-col :md="20" :sm="24">
<AppAccountListTable ref="ListTable" :url="accountUrl" :has-import="false" :has-clone="false" />
</el-col>
<el-col :span="4" />
<el-col :md="4" :sm="24" />
</el-row>
</template>

View File

@@ -1,10 +1,10 @@
<template>
<div>
<el-row :gutter="20">
<el-col :span="16">
<el-col :md="16" :sm="24">
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
<el-col :span="8">
<el-col :md="8" :sm="24">
<RelationCard ref="assetSelect" type="primary" style="margin-top: 15px" v-bind="appRelationConfig" />
</el-col>
</el-row>

View File

@@ -1,10 +1,10 @@
<template>
<div>
<el-row :gutter="20">
<el-col :span="16">
<el-col :md="16" :sm="24">
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
<el-col :span="8">
<el-col :md="8" :sm="24">
<QuickActions type="primary" :actions="quickActions" />
<AssetRelationCard ref="assetSelect" type="primary" style="margin-top: 15px" v-bind="assetRelationConfig" />
<RelationCard ref="nodeRelation" type="info" style="margin-top: 15px" v-bind="nodeRelationConfig" />
@@ -68,7 +68,7 @@ export default {
name: 'Push',
title: this.$t('common.Push'),
type: 'primary',
can: this.object.auto_push,
can: this.object.auto_push && vm.$hasPerm('assets.push_assetsystemuser'),
callback: ({ row }) => {
const theUrl = `/api/v1/assets/system-users/${vm.object.id}/tasks/`
const data = { action: 'push', assets: [row.asset] }
@@ -81,7 +81,7 @@ export default {
name: 'Delete',
title: this.$t('common.Delete'),
type: 'danger',
can: !this.$store.getters.currentOrgIsRoot,
can: !this.$store.getters.currentOrgIsRoot && vm.$hasPerm('assets.delete_authbook'),
callback: (val) => {
this.$axios.delete(`/api/v1/assets/system-users-assets-relations/${val.row.id}/`).then(() => {
this.$message.success(this.$t('common.deleteSuccessMsg'))
@@ -103,7 +103,7 @@ export default {
title: this.$t('common.PushSelected'),
name: 'PushSelected',
can({ selectedRows }) {
return selectedRows.length > 0 && vm.object.auto_push
return selectedRows.length > 0 && vm.object.auto_push && vm.$hasPerm('assets.push_assetsystemuser')
},
callback: this.bulkPushCallback.bind(this)
},
@@ -111,7 +111,7 @@ export default {
title: this.$t('assets.TestAssetsConnective'),
name: 'TestSelected',
can({ selectedRows }) {
return selectedRows.length > 0
return selectedRows.length > 0 && vm.$hasPerm('assets.test_assetconnectivity')
},
callback: this.bulkTestCallback.bind(this)
}
@@ -122,7 +122,8 @@ export default {
title: this.$t('assets.TestAssetsConnective'),
attrs: {
type: 'primary',
label: this.$t('common.Test')
label: this.$t('common.Test'),
disabled: !vm.$hasPerm('assets.test_assetconnectivity')
},
callbacks: {
click: function() {

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<QuickActions type="primary" :actions="quickActions" />
<RelationCard
v-if="!(object.protocol === 'rdp' || object.protocol === 'vnc')"

View File

@@ -4,10 +4,10 @@
<b>{{ Tips.title }}</b>: <span>{{ Tips.body }}</span>
</el-alert>
<el-row :gutter="20">
<el-col :span="20">
<el-col :md="20" :sm="24">
<ListTable ref="ListTable" :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
<el-col :span="4" />
<el-col :md="4" :sm="24" />
</el-row>
</div>
</template>

View File

@@ -9,7 +9,7 @@
<script>
import { GenericListTable } from '@/layout/components'
import { ApplicationTypes } from '@/views/applications/const'
import { ApplicationSystemUserTypes } from '@/views/applications/const'
import { AssetProtocols } from '@/views/assets/const'
export default {
@@ -78,7 +78,7 @@ export default {
},
dropdown: [
...AssetProtocols,
...ApplicationTypes
...ApplicationSystemUserTypes
]
}
},

View File

@@ -1,4 +1,5 @@
import i18n from '@/i18n/i18n'
import { groupedDropdownToCascader } from '@/utils/common'
export const AssetProtocols = [
{
@@ -28,3 +29,5 @@ export const AssetProtocols = [
}
]
export const AssetCascader = groupedDropdownToCascader(AssetProtocols)

View File

@@ -1,11 +1,11 @@
<template>
<div class="white-bg dashboard-header print-margin">
<el-row>
<el-col :span="12">
<el-col :md="12" :sm="24">
<h2>{{ $t('dashboard.LoginOverview') }}</h2>
</el-col>
<el-col :span="12">
<el-button-group style="float: right; padding: 0">
<el-col :md="12" :sm="24" class="clearfix">
<el-button-group style="float: right; padding: 0" class="clearfix">
<el-button type="default" size="mini" :class="{ 'active': active === 'weekly'}" @click="changeDates('weekly')">{{ $t('dashboard.Weekly') }}</el-button>
<el-button type="default" size="mini" :class="{ 'active': active === 'monthly'}" @click="changeDates('monthly')">{{ $t('dashboard.Monthly') }}</el-button>
</el-button-group>
@@ -13,7 +13,7 @@
</el-row>
<el-row :gutter="20">
<el-col :lg="18" :sm="24">
<LoginMetric :range="active" class="card-item" style="margin-top: -30px" heigth="300px" />
<LoginMetric :range="active" class="card-item" heigth="300px" />
</el-col>
<el-col :lg="6" :sm="24">
<LoginActivePin :range="active" class="card-item" />

View File

@@ -28,19 +28,19 @@ export default {
{
title: this.$t('dashboard.UsersTotal'),
body: {
route: `/users/users`,
route: { name: 'UserList' },
count: this.counter.total_count_users,
comment: 'All users',
disabled: !this.$store.state.users.hasAdmin
comment: this.$t('dashboard.UsersTotal'),
disabled: !this.$hasPerm('users.view_user')
}
},
{
title: this.$t('dashboard.AssetsTotal'),
body: {
route: `/assets/assets`,
route: { name: 'AssetList' },
count: this.counter.total_count_assets,
comment: 'All assets',
disabled: !this.$store.state.users.hasAdmin
comment: this.$t('dashboard.AssetsTotal'),
disabled: !this.$hasPerm('assets.view_asset')
}
},
{
@@ -48,7 +48,8 @@ export default {
body: {
route: { name: `SessionList`, params: { activeMenu: 'OnlineList' }},
count: this.counter.total_count_online_users,
comment: 'Online users'
comment: this.$t('dashboard.OnlineUsers'),
disabled: !this.$hasPerm('terminal.view_session')
}
},
{
@@ -56,7 +57,8 @@ export default {
body: {
route: { name: `SessionList`, params: { activeMenu: 'OnlineList' }},
count: this.counter.total_count_online_sessions,
comment: 'Online sessions'
comment: this.$t('dashboard.OnlineSessions'),
disabled: !this.$hasPerm('terminal.view_session')
}
}
]

View File

@@ -1,12 +1,12 @@
<template>
<el-row :gutter="10" style="margin-bottom: 20px;margin-top: 20px">
<el-col :md="8" :sm="12">
<el-col :md="8" :sm="24">
<TopUser />
</el-col>
<el-col :md="8" :sm="12" class="print-margin-top">
<el-col :md="8" :sm="24" class="print-margin-top">
<TopAssets />
</el-col>
<el-col :md="8" :sm="12">
<el-col :md="8" :sm="24">
<Latest10Sessions class="card-item print-margin-top" />
</el-col>
</el-row>

View File

@@ -1,6 +1,6 @@
<template>
<Page>
<div v-if="this.$hasPerm('rbac.view_console|rbac.view_workspace')">
<div v-if="this.$hasPerm('rbac.view_console|rbac.view_audit')">
<Announcement />
<ResourceSummary />
<DatesLoginSummary />

View File

@@ -1,18 +1,20 @@
<template>
<div>
<Announcement />
<GenericTreeListPage :table-config="tableConfig" :header-actions="headerActions" :tree-setting="treeSetting" />
<GenericTreeListPage
:table-config="tableConfig"
:header-actions="headerActions"
:tree-setting="treeSetting"
/>
</div>
</template>
<script>
import GenericTreeListPage from '@/layout/components/GenericTreeListPage'
import { Announcement } from '@/components'
import { SystemUserFormatter, DialogDetailFormatter } from '@/components/TableFormatters'
export default {
components: {
GenericTreeListPage,
Announcement
GenericTreeListPage
},
data() {
return {
@@ -39,6 +41,10 @@ export default {
url: '/api/v1/perms/users/assets/',
hasTree: true,
columns: ['hostname', 'ip', 'system_users', 'platform', 'comment', 'actions'],
columnsShow: {
default: ['hostname', 'ip', 'system_users', 'platform', 'actions'],
min: ['hostname', 'actions']
},
columnsMeta: {
hostname: {
prop: 'hostname',
@@ -86,7 +92,7 @@ export default {
showOverflowTooltip: true,
align: 'center',
label: this.$t('assets.SystemUsers'),
width: '150px',
width: '120px',
formatter: SystemUserFormatter,
formatterArgs: {
getUrl: ({ row }) => {
@@ -102,6 +108,8 @@ export default {
width: '100px'
},
actions: {
width: '150px',
align: 'center',
formatterArgs: {
hasDelete: false,
loading: true,

View File

@@ -5,8 +5,8 @@
</div>
<ul class="content">
<li v-if="announcement.content" class="item">
<span class="item-title">{{ announcement.subject }}</span>
<span>{{ announcement.content }}</span>
<p class="item-title">{{ announcement.subject }}</p>
<p class="item-content">{{ announcement.content }}</p>
<span v-if="announcement.link">
<el-link :href="announcement.link" target="_blank" class="item-url">
{{ $t('common.ViewMore') }}
@@ -70,6 +70,11 @@ ul,li {
text-align: center;
font-size: 15px;
vertical-align: middle;
margin-left: -10px;
}
.item-content {
white-space: pre-wrap;
margin: 0;
}
}
.item-url {

View File

@@ -11,9 +11,10 @@ export default {
HomeCard
},
data() {
const vm = this
return {
cardConfig: {
title: this.$t('route.SessionOffline')
title: this.$t('route.RecentSession')
},
tableConfig: {
url: '/api/v1/terminal/my-sessions/?limit=5',
@@ -29,7 +30,11 @@ export default {
formatter: function(row, column, cellValue, index) {
const label = index + 1
const route = { to: { name: 'SessionDetail', params: { id: row.id }}}
return <router-link {...{ attrs: route }}>{ label }</router-link>
if (vm.$hasPerm('terminal.view_session')) {
return <router-link {...{ attrs: route }} >{ label }</router-link>
} else {
return label
}
}
},
user: {
@@ -59,7 +64,7 @@ export default {
}
},
hasSelection: false,
paginationSize: 5
paginationSize: 10
}
}
}

View File

@@ -39,8 +39,8 @@ export default {
return 'AssetsTicketDetail'
} else if (row.type === 'apply_application') {
return 'AppsTicketDetail'
} else if (row.type === 'login_asset_confirm') {
return 'loginAssetTicketDetail'
} else if (row.type === 'login_asset_confirm' || row.type === 'login_confirm') {
return 'LoginAssetTicketDetail'
} else if (row.type === 'command_confirm') {
return 'CommandConfirmDetail'
} else {
@@ -64,7 +64,7 @@ export default {
}
],
hasSelection: false,
paginationSize: 5
paginationSize: 10
}
}
},

View File

@@ -12,7 +12,10 @@
<ul>
<li><span class="title">{{ $t('audits.Username') }}</span><span>{{ users.name }}</span></li>
<li><span class="title">{{ $t('users.Email') }}</span><span>{{ users.email }}</span></li>
<li><span class="title">{{ $t('audits.LoginDate') }}</span><span>{{ this.$moment(users.last_login).format('YYYY-MM-DD HH:mm:ss') }}</span></li>
<li>
<span class="title">{{ $t('audits.LoginDate') }}</span>
<span>{{ $moment(users.last_login, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss') }}</span>
</li>
</ul>
</el-col>
</el-row>

View File

@@ -1,5 +1,6 @@
<template>
<Page>
<Announcement />
<div class="home">
<el-container class="container">
<el-main class="main">
@@ -8,12 +9,11 @@
<el-row>
<el-col :md="16" :xs="24" class="content-left">
<Session />
<Log />
<Ticket v-if="hasValidLicense" />
<Ticket v-if="$hasLicense() && $hasPerm('tickets.view_ticket')" />
</el-col>
<el-col :md="8" :xs="24">
<User />
<Announcement />
<Log />
</el-col>
</el-row>
</div>
@@ -26,8 +26,8 @@
<script>
import { Page } from '@/layout/components'
import { Announcement } from '@/components'
import User from './components/User'
import Announcement from './components/Announcement'
import Ticket from './components/Ticket'
import Log from './components/LoginLog'
import Session from './components/Session'
@@ -35,23 +35,12 @@ import Session from './components/Session'
export default {
name: 'Name',
components: {
Announcement,
Page,
User,
Announcement,
Ticket,
Log,
Session
},
data() {
return {
}
},
computed: {
hasValidLicense() {
return this.$store.getters.hasValidLicense
}
},
methods: {
}
}

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :title="cardTitle" :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<RunInfoCard type="danger" style="margin-top: 15px" v-bind="RunFailedConfig" />
<RunInfoCard type="info" v-bind="RunSuccessConfig" style="margin-top: 15px" />
</el-col>

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :title="cardTitle" :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<RunInfoCard type="danger" style="margin-top: 15px" v-bind="RunFailedConfig" />
<RunInfoCard type="info" v-bind="RunSuccessConfig" style="margin-top: 15px" />
</el-col>

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :title="cardTitle" :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<RunInfoCard type="danger" style="margin-top: 15px" v-bind="RunFailedConfig" />
<RunInfoCard type="info" v-bind="RunSuccessConfig" style="margin-top: 15px" />
</el-col>

View File

@@ -34,6 +34,7 @@ export default {
[this.$t('common.Other'), ['is_active', 'date_start', 'date_expired', 'comment']]
],
url: `/api/v1/perms/application-permissions/?category=${this.$route.query.category}&type=${this.$route.query.type}`,
createSuccessNextRoute: { name: 'ApplicationPermissionDetail' },
fieldsMeta: {
users: {
el: {

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<QuickActions type="primary" :actions="quickActions" />
</el-col>
</el-row>

View File

@@ -1,10 +1,17 @@
<template>
<GenericTreeListPage
ref="TreeTablePage"
:tree-setting="treeSetting"
:header-actions="headerActions"
:table-config="tableConfig"
/>
<div>
<GenericTreeListPage
ref="TreeTablePage"
:tree-setting="treeSetting"
:header-actions="headerActions"
:table-config="tableConfig"
/>
<PermBulkUpdateDialog
:visible.sync="updateSelectedDialogSetting.visible"
v-bind="updateSelectedDialogSetting"
:perm-type="permType"
/>
</div>
</template>
<script>
@@ -12,15 +19,19 @@ import GenericTreeListPage from '@/layout/components/GenericTreeListPage'
import { setUrlParam } from '@/utils/common'
import { DetailFormatter } from '@/components/TableFormatters'
import { ApplicationTypes } from '@/views/applications/const'
import PermBulkUpdateDialog from '@/views/perms/components/PermBulkUpdateDialog'
import { mapGetters } from 'vuex'
export default {
name: 'AssetAccountList',
components: {
GenericTreeListPage
GenericTreeListPage,
PermBulkUpdateDialog
},
data() {
const vm = this
return {
permType: 'applications',
isInit: true,
clickedRow: null,
iShowTree: true,
@@ -164,9 +175,31 @@ export default {
}})
},
dropdown: ApplicationTypes
}
},
extraMoreActions: [
{
name: 'actionUpdateSelected',
title: this.$t('common.updateSelected'),
can: ({ selectedRows }) => {
return selectedRows.length > 0 &&
!vm.currentOrgIsRoot &&
vm.$hasPerm('perms.change_applicationpermission')
},
callback: ({ selectedRows }) => {
vm.updateSelectedDialogSetting.selectedRows = selectedRows
vm.updateSelectedDialogSetting.visible = true
}
}
]
},
updateSelectedDialogSetting: {
visible: false,
selectedRows: []
}
}
},
computed: {
...mapGetters(['currentOrgIsRoot'])
}
}
</script>

View File

@@ -1,5 +1,10 @@
<template>
<GenericCreateUpdatePage :fields="fields" :initial="initial" :fields-meta="fieldsMeta" :url="url" />
<GenericCreateUpdatePage
:fields="fields"
:initial="initial"
:fields-meta="fieldsMeta"
:url="url"
/>
</template>
<script>
@@ -37,6 +42,7 @@ export default {
[this.$t('common.Other'), ['is_active', 'date_start', 'date_expired', 'comment']]
],
url: '/api/v1/perms/asset-permissions/',
createSuccessNextRoute: { name: 'AssetPermissionDetail' },
fieldsMeta: {
users: {
el: {

View File

@@ -1,16 +1,26 @@
<template>
<GenericTreeListPage :table-config="tableConfig" :header-actions="headerActions" :tree-setting="treeSetting" />
<div>
<GenericTreeListPage :table-config="tableConfig" :header-actions="headerActions" :tree-setting="treeSetting" />
<PermBulkUpdateDialog
:visible.sync="updateSelectedDialogSetting.visible"
v-bind="updateSelectedDialogSetting"
/>
</div>
</template>
<script>
import GenericTreeListPage from '@/layout/components/GenericTreeListPage'
import { DetailFormatter } from '@/components/TableFormatters'
import PermBulkUpdateDialog from '@/views/perms/components/PermBulkUpdateDialog'
import { mapGetters } from 'vuex'
export default {
components: {
GenericTreeListPage
GenericTreeListPage,
PermBulkUpdateDialog
},
data() {
const vm = this
return {
treeSetting: {
showMenu: false,
@@ -186,10 +196,32 @@ export default {
}
]
},
hasBulkUpdate: false
hasBulkUpdate: false,
extraMoreActions: [
{
name: 'actionUpdateSelected',
title: this.$t('common.updateSelected'),
can: ({ selectedRows }) => {
return selectedRows.length > 0 &&
!vm.currentOrgIsRoot &&
vm.$hasPerm('perms.change_assetpermission')
},
callback: ({ selectedRows }) => {
vm.updateSelectedDialogSetting.selectedRows = selectedRows
vm.updateSelectedDialogSetting.visible = true
}
}
]
},
updateSelectedDialogSetting: {
visible: false,
selectedRows: []
}
}
},
computed: {
...mapGetters(['currentOrgIsRoot'])
},
methods: {
}
}

View File

@@ -0,0 +1,87 @@
<template>
<GenericUpdateFormDialog
v-if="visible"
:selected-rows="selectedRows"
:form-setting="formSetting"
:visible="visible"
v-on="$listeners"
/>
</template>
<script>
import { GenericUpdateFormDialog } from '@/layout/components'
import getFields from '@/views/perms/fields'
export default {
name: 'PermBulkUpdateDialog',
components: {
GenericUpdateFormDialog
},
props: {
permType: {
type: String,
default: 'asset'
},
visible: {
type: Boolean,
default: false
},
selectedRows: {
type: Array,
default: () => ([])
}
},
data() {
return {
formSetting: {
url: '',
hasSaveContinue: false,
initial: {},
fields: [],
fieldsMeta: {}
}
}
},
created() {
this.init()
},
methods: {
init() {
let url
const fieldsManager = getFields.bind(this)()
const fields = [
'users', 'user_groups', 'system_users',
'actions', 'is_active', 'date_start', 'date_expired'
]
const fieldsMeta = {
users: fieldsManager.users,
user_groups: fieldsManager.user_groups,
system_users: fieldsManager.system_users,
actions: fieldsManager.actions,
date_start: fieldsManager.date_start,
date_expired: fieldsManager.date_expired,
is_active: fieldsManager.is_active
}
if (this.permType !== 'asset') {
url = '/api/v1/perms/application-permissions/'
fields.splice(2, 0, 'applications')
Object.assign(fieldsMeta, { applications: fieldsManager.applications })
} else {
url = '/api/v1/perms/asset-permissions/'
fields.splice(2, 0, ...['assets', 'nodes'])
Object.assign(fieldsMeta, {
assets: fieldsManager.assets,
nodes: fieldsManager.nodes
})
}
this.$data.formSetting.url = url
this.$data.formSetting.fields = fields
this.$data.formSetting.fieldsMeta = fieldsMeta
}
}
}
</script>
<style scoped>
</style>

120
src/views/perms/fields.js Normal file
View File

@@ -0,0 +1,120 @@
import AssetSelect from '@/components/AssetSelect'
import PermissionFormActionField from '@/views/perms/components/PermissionFormActionField'
function getFields() {
const users = {
label: this.$t('users.Users'),
hidden: () => false,
el: {
value: [],
ajax: {
url: '/api/v1/users/users/?fields_size=mini',
transformOption: (item) => {
return { label: item.name + '(' + item.username + ')', value: item.id }
}
}
}
}
const user_groups = {
label: this.$t('users.UserGroups'),
hidden: () => false,
el: {
value: [],
url: '/api/v1/users/groups/'
}
}
const assets = {
type: 'assetSelect',
hidden: () => false,
component: AssetSelect,
label: this.$t('perms.Asset'),
rules: [{
required: false
}],
el: {
value: []
}
}
const nodes = {
label: this.$t('perms.Node'),
hidden: () => false,
el: {
value: [],
ajax: {
url: '/api/v1/assets/nodes/',
transformOption: (item) => {
return { label: item.full_value, value: item.id }
}
}
}
}
const system_users = {
label: this.$t('perms.SystemUser'),
hidden: () => false,
el: {
value: [],
ajax: {
url: '/api/v1/assets/system-users/?protocol__in=rdp,ssh,vnc,telnet',
transformOption: (item) => {
const username = item.username || '*'
return { label: item.name + '(' + username + ')', value: item.id }
}
}
}
}
const actions = {
label: this.$t('perms.Actions'),
hidden: () => false,
component: PermissionFormActionField,
helpText: this.$t('common.actionsTips')
}
const date_start = {
label: this.$t('common.dateStart'),
hidden: () => false
}
const date_expired = {
label: this.$t('common.dateExpired'),
hidden: () => false
}
const is_active = {
label: this.$t('assets.IsActive'),
type: 'checkbox'
}
const applications = {
label: this.$t('assets.Applications'),
hidden: () => false,
el: {
value: [],
ajax: {
url: `/api/v1/applications/applications/`,
transformOption: (item) => {
return { label: item.name + ' (' + item.type_display + ')', value: item.id }
}
}
}
}
return {
users: users,
user_groups: user_groups,
assets: assets,
applications: applications,
nodes: nodes,
system_users: system_users,
actions: actions,
is_active: is_active,
date_start: date_start,
date_expired: date_expired
}
}
export default getFields

View File

@@ -33,8 +33,6 @@ export default {
label: 'AccessKeySecret',
formatter: ShowKeyFormatter
},
is_active: {
},
date_created: {
label: this.$t('common.dateCreated'),
showOverflowTooltip: true,
@@ -44,6 +42,7 @@ export default {
prop: '',
formatterArgs: {
hasUpdate: false,
hasClone: false,
onDelete: function({ row }) {
this.$axios.delete(`${ajaxUrl}${row.id}/`).then(res => {
this.getRefsListTable.reloadTable()

View File

@@ -2,10 +2,10 @@
<Page v-bind="$attrs">
<div>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :items="detailCardItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<QuickActions
type="primary"
:title="this.$t('users.AuthSettings')"
@@ -30,14 +30,14 @@
:destroy-on-close="true"
>
<el-row :gutter="20">
<el-col :span="4">
<div style="line-height: 34px;text-align: center">{{ $t('assets.Password') }}</div>
<el-col :md="4" :sm="24">
<div style="line-height: 34px">{{ $t('assets.Password') }}</div>
</el-col>
<el-col :span="14">
<el-col :md="14" :sm="24">
<el-input v-model="passwordInput" type="password" />
<span class="help-tips help-block">{{ $t('common.PasswordRequireForSecurity') }}</span>
</el-col>
<el-col :span="4">
<el-col :md="4" :sm="24">
<el-button size="mini" type="primary" style="line-height:20px " @click="passConfirm">{{ this.$t('common.Confirm') }}</el-button>
</el-col>
</el-row>

View File

@@ -0,0 +1,98 @@
<template>
<GenericListPage
ref="GenericListTable"
:table-config="tableConfig"
:header-actions="headerActions"
:help-message="helpMessage"
/>
</template>
<script>
import { GenericListPage } from '@/layout/components'
export default {
components: {
GenericListPage
},
data() {
const ajaxUrl = '/api/v1/authentication/temp-tokens/'
return {
helpMessage: this.$t('setting.helpText.TempPassword'),
tableConfig: {
hasSelection: true,
url: ajaxUrl,
columns: [
'username', 'secret', 'date_expired', 'date_verified', 'is_valid', 'actions'
],
columnsMeta: {
secret: {
label: this.$t('common.nav.TempPassword')
},
expire: {
label: this.$t('setting.Expired') + '( s )'
},
actions: {
prop: '',
formatterArgs: {
hasUpdate: false,
hasClone: false,
hasDelete: false,
extraActions: [
{
name: 'Expired',
title: this.$t('setting.Expire'),
type: 'info',
callback: function({ row }) {
this.$axios.patch(`${ajaxUrl}${row.id}/expire/`,
).then(res => {
this.getRefsListTable.reloadTable()
this.$message.success(this.$t('common.updateSuccessMsg'))
}).catch(error => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
})
}.bind(this)
}
]
}
}
}
},
headerActions: {
hasSearch: false,
hasRightActions: true,
hasRefresh: true,
hasExport: false,
hasImport: false,
hasBulkDelete: false,
hasCreate: false,
extraActions: [
{
name: this.$t('setting.Create'),
title: this.$t('setting.Create'),
type: 'primary',
can: true,
callback: function() {
this.$axios.post(
`/api/v1/authentication/temp-tokens/`
).then(res => {
this.getRefsListTable.reloadTable()
this.$message.success(this.$t('common.updateSuccessMsg'))
}).catch(error => {
this.$message.error(this.$t('common.updateErrorMsg' + ' ' + error))
})
}.bind(this)
}
]
}
}
},
computed: {
getRefsListTable() {
return this.$refs.GenericListTable.$refs.ListTable.$refs.ListTable || {}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,6 +1,6 @@
<template>
<el-row :gutter="20">
<el-col :span="16">
<el-col :md="16" :sm="24">
<ListTable :table-config="tableConfig" :header-actions="headerActions" />
</el-col>
</el-row>
@@ -66,5 +66,4 @@ export default {
</script>
<style scoped>
</style>

View File

@@ -1,9 +1,9 @@
<template>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard v-if="object" :items="detailItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<QuickActions v-if="object" type="primary" :actions="quickActions" />
</el-col>
</el-row>

View File

@@ -1,5 +1,5 @@
<template>
<pre style="border: none; background: none; white-space: pre-wrap;padding: 10px 20px">
<pre style="border: none; background: none; white-space: pre-wrap; word-wrap: break-word; padding: 10px 20px">
{{ '$ '+ row.input }}
<br>
{{ row.output }}

View File

@@ -18,6 +18,7 @@
<div slot="footer">
<el-button @click="hiddenDialog">{{ $t('common.Cancel') }}</el-button>
<el-button type="primary" :loading="dialogLdapUserImportLoginStatus" @click="importUserClick">{{ $t('common.Import') }}</el-button>
<el-button type="primary" :loading="dialogLdapUserImportAllLoginStatus" @click="importAllUserClick">{{ $t('common.ImportAll') }}</el-button>
</div>
</Dialog>
<Dialog
@@ -41,8 +42,10 @@
import ListTable from '@/components/ListTable'
import { GenericCreateUpdateForm } from '@/layout/components'
import Dialog from '@/components/Dialog'
import Select2 from '@/components/FormFields/Select2'
import { importLdapUser, refreshLdapUserCache, startLdapUserCache } from '@/api/settings'
import { CronTab } from '@/components'
import { Required } from '@/components/DataForm/rules'
export default {
name: 'ImportDialog',
@@ -54,6 +57,7 @@ export default {
data() {
return {
dialogLdapUserImportLoginStatus: false,
dialogLdapUserImportAllLoginStatus: false,
refreshed: false,
headerActions: {
hasCreate: false,
@@ -105,8 +109,24 @@ export default {
settings: {
visible: false,
url: '/api/v1/settings/setting/?category=ldap',
fields: ['AUTH_LDAP_SYNC_IS_PERIODIC', 'AUTH_LDAP_SYNC_CRONTAB', 'AUTH_LDAP_SYNC_INTERVAL'],
fields: ['AUTH_LDAP_SYNC_ORG_ID', 'AUTH_LDAP_SYNC_IS_PERIODIC', 'AUTH_LDAP_SYNC_CRONTAB', 'AUTH_LDAP_SYNC_INTERVAL'],
fieldsMeta: {
AUTH_LDAP_SYNC_ORG_ID: {
component: Select2,
rules: [Required],
el: {
multiple: false,
ajax: {
url: '/api/v1/orgs/orgs/',
transformOption: (item) => {
return { label: item.name, value: item.id }
}
}
},
hidden: (formValue) => {
return !this.$hasLicense()
}
},
AUTH_LDAP_SYNC_IS_PERIODIC: {
type: 'switch'
},
@@ -142,6 +162,16 @@ export default {
}).finally(() => this.dialogLdapUserImportLoginStatus = false)
}
},
importAllUserClick() {
this.dialogLdapUserImportAllLoginStatus = true
const data = {
username_list: ['*']
}
importLdapUser(data).then(res => {
this.$message.success(res.msg)
// eslint-disable-next-line no-return-assign
}).finally(() => this.dialogLdapUserImportAllLoginStatus = false)
},
handlerListTableXHRError(errMsg) {
if (this.dialogLdapUserImport) {
setTimeout(this.$refs.listTable.reloadTable, 30000)

View File

@@ -5,10 +5,10 @@
{{ this.$t('setting.ImportLicenseTip') }}
</el-alert>
<el-row :gutter="20">
<el-col :span="14">
<el-col :md="14" :sm="24">
<DetailCard :title="cardTitle" :items="detailItems" />
</el-col>
<el-col :span="10">
<el-col :md="10" :sm="24">
<QuickActions type="primary" :actions="quickActions" />
</el-col>
</el-row>

View File

@@ -14,7 +14,7 @@
<span>{{ scope.row.value }}</span>
</template>
</el-table-column>
<el-table-column v-for="header in receiveBackends" :key="header.id" :label="header.name_display" width="120">
<el-table-column v-for="header in receiveBackends" :key="header.id" :label="getNameDisplay(header)" width="80">
<template v-slot="scope">
<span v-if="!scope.row.children">
<el-checkbox
@@ -86,7 +86,7 @@ export default {
this.$axios.patch(
`/api/v1/notifications/system-msg-subscription/${sub.id}/`,
{ receiveBackends: backends }
{ receive_backends: backends }
).catch(err => {
this.$log.error(err)
})
@@ -105,6 +105,13 @@ export default {
console.log(err)
})
},
getNameDisplay(header) {
const displayName = header['name_display']
if (displayName === 'Site message') {
return 'Inbox'
}
return displayName
},
onOpenDialog(sub) {
this.currentEditSub = sub
this.dialogSelectedUsers = sub.receivers

View File

@@ -14,20 +14,27 @@ export default {
GenericCreateUpdateForm
},
data() {
const comp = this.$t('common.Component')
return {
fields: [
[
'KoKo',
`SSH ${comp}(KoKo)`,
[
'TERMINAL_PASSWORD_AUTH', 'TERMINAL_PUBLIC_KEY_AUTH',
'TERMINAL_ASSET_LIST_SORT_BY', 'TERMINAL_ASSET_LIST_PAGE_SIZE',
'TERMINAL_TELNET_REGEX'
'TERMINAL_ASSET_LIST_SORT_BY',
'TERMINAL_ASSET_LIST_PAGE_SIZE', 'TERMINAL_TELNET_REGEX'
]
],
[
'XRDP',
`RDP ${comp}(XRDP)`,
[
'XRDP_ENABLED', 'TERMINAL_RDP_ADDR'
'XRDP_ENABLED'
]
],
[
`DB ${comp}(Magnus)`,
[
'TERMINAL_MAGNUS_ENABLED'
]
]
],
@@ -35,11 +42,6 @@ export default {
TERMINAL_TELNET_REGEX: {
type: 'input'
},
TERMINAL_RDP_ADDR: {
hidden: () => {
return !this.$store.getters.hasValidLicense
}
},
XRDP_ENABLED: {
hidden: () => {
return !this.$store.getters.hasValidLicense

View File

@@ -0,0 +1,42 @@
<template>
<GenericCreateUpdatePage
v-bind="$data"
:create-success-next-route="successUrl"
:update-success-next-route="successUrl"
/>
</template>
<script>
import GenericCreateUpdatePage from '@/layout/components/GenericCreateUpdatePage'
export default {
name: 'EndpointCreateUpdate',
components: {
GenericCreateUpdatePage
},
data() {
return {
url: '/api/v1/terminal/endpoints/',
successUrl: { name: 'TerminalSetting', params: { activeMenu: 'EndpointList' }},
fields: [
[this.$t('common.Basic'), ['name', 'host']],
[
this.$t('applications.port'),
['http_port', 'https_port', 'ssh_port', 'rdp_port', 'mysql_port', 'mariadb_port', 'postgresql_port']
],
[this.$t('common.Other'), ['comment']]
],
fieldsMeta: {
},
hasDetailInMsg: false
}
},
computed: {
},
methods: {
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,55 @@
<template>
<ListTable :table-config="tableConfig" :header-actions="headerActions" />
</template>
<script>
import ListTable from '@/components/ListTable'
export default {
name: 'EndpointList',
components: {
ListTable
},
data() {
return {
tableConfig: {
url: '/api/v1/terminal/endpoints/',
columns: [
'name', 'host',
'http_port', 'https_port', 'ssh_port',
'rdp_port', 'mysql_port', 'mariadb_port',
'postgresql_port',
'date_created', 'comment', 'actions'
],
columnsShow: {
min: ['name', 'actions'],
default: [
'name', 'host', 'actions',
'http_port', 'https_port', 'ssh_port', 'rdp_port'
]
},
columnsMeta: {
name: {
formatter: null
},
actions: {
formatterArgs: {
updateRoute: 'EndpointUpdate',
cloneRoute: 'EndpointCreate',
canDelete: ({ row }) => row.id !== '00000000-0000-0000-0000-000000000001'
}
}
}
},
headerActions: {
hasMoreActions: false,
createRoute: 'EndpointCreate'
}
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,57 @@
<template>
<GenericCreateUpdatePage
v-bind="$data"
:create-success-next-route="successUrl"
:update-success-next-route="successUrl"
:after-get-form-value="afterGetFormValue"
/>
</template>
<script>
import { GenericCreateUpdatePage } from '@/layout/components'
export default {
name: 'EndpointRuleCreateUpdate',
components: {
GenericCreateUpdatePage
},
data() {
return {
url: '/api/v1/terminal/endpoint-rules/',
successUrl: { name: 'TerminalSetting', params: { activeMenu: 'EndpointRuleList' }},
fields: [
[this.$t('common.Basic'), ['name', 'ip_group', 'endpoint', 'priority']],
[this.$t('common.Other'), ['comment']]
],
fieldsMeta: {
endpoint: {
el: {
multiple: false,
value: [],
ajax: {
url: '/api/v1/terminal/endpoints/?fields_size=mini',
transformOption: (item) => {
return { label: item.name, value: item.id }
}
}
}
}
},
hasDetailInMsg: false,
cleanFormValue(value) {
if (!Array.isArray(value.ip_group)) {
value.ip_group = value.ip_group ? value.ip_group.split(',') : []
}
return value
},
afterGetFormValue(formValue) {
formValue.ip_group = formValue.ip_group.toString()
return formValue
}
}
}
}
</script>
<style scoped>
</style>

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