feat: 修改主机额外配置.
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@cancel="handleClose">
|
@cancel="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class HostExtraController {
|
|||||||
return hostExtraService.getHostExtraList(request);
|
return hostExtraService.getHostExtraList(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/update")
|
@PutMapping("/update")
|
||||||
@Operation(summary = "修改主机拓展信息")
|
@Operation(summary = "修改主机拓展信息")
|
||||||
public Integer updateHostExtra(@Validated @RequestBody HostExtraUpdateRequest request) {
|
public Integer updateHostExtra(@Validated @RequestBody HostExtraUpdateRequest request) {
|
||||||
return hostExtraService.updateHostExtra(request);
|
return hostExtraService.updateHostExtra(request);
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ public enum HostExtraSshAuthTypeEnum {
|
|||||||
DEFAULT,
|
DEFAULT,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 秘钥验证
|
* 自定义秘钥验证
|
||||||
*/
|
*/
|
||||||
KEY,
|
CUSTOM_KEY,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 身份验证
|
* 自定义身份验证
|
||||||
*/
|
*/
|
||||||
IDENTITY,
|
CUSTOM_IDENTITY,
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ public class HostSshExtraStrategy implements MapDataStrategy<HostSshExtraModel>
|
|||||||
Long keyId = model.getKeyId();
|
Long keyId = model.getKeyId();
|
||||||
Long identityId = model.getIdentityId();
|
Long identityId = model.getIdentityId();
|
||||||
// 必填验证
|
// 必填验证
|
||||||
if (HostExtraSshAuthTypeEnum.KEY.equals(authType)) {
|
if (HostExtraSshAuthTypeEnum.CUSTOM_KEY.equals(authType)) {
|
||||||
Valid.notNull(keyId);
|
Valid.notNull(keyId);
|
||||||
} else if (HostExtraSshAuthTypeEnum.IDENTITY.equals(authType)) {
|
} else if (HostExtraSshAuthTypeEnum.CUSTOM_IDENTITY.equals(authType)) {
|
||||||
Valid.notNull(identityId);
|
Valid.notNull(identityId);
|
||||||
}
|
}
|
||||||
// 验证主机秘钥是否存在
|
// 验证主机秘钥是否存在
|
||||||
|
|||||||
@@ -243,7 +243,6 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
List<Long> authorizedGroupIdList) {
|
List<Long> authorizedGroupIdList) {
|
||||||
// 查询主机列表
|
// 查询主机列表
|
||||||
List<HostVO> hosts = hostService.getHostListByCache();
|
List<HostVO> hosts = hostService.getHostListByCache();
|
||||||
|
|
||||||
// 全部数据直接返回
|
// 全部数据直接返回
|
||||||
if (allData) {
|
if (allData) {
|
||||||
return hosts;
|
return hosts;
|
||||||
@@ -258,6 +257,7 @@ public class AssetAuthorizedDataServiceImpl implements AssetAuthorizedDataServic
|
|||||||
.map(dataGroupRel::get)
|
.map(dataGroupRel::get)
|
||||||
.filter(Lists::isNoneEmpty)
|
.filter(Lists::isNoneEmpty)
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
|
.distinct()
|
||||||
.map(hostMap::get)
|
.map(hostMap::get)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|||||||
@@ -149,14 +149,17 @@ public class DataPermissionServiceImpl implements DataPermissionService {
|
|||||||
List<Long> list = RedisLists.range(cacheKey, Long::valueOf);
|
List<Long> list = RedisLists.range(cacheKey, Long::valueOf);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
LambdaQueryWrapper<DataPermissionDO> wrapper = dataPermissionDAO.lambda()
|
LambdaQueryWrapper<DataPermissionDO> wrapper = dataPermissionDAO.lambda()
|
||||||
.eq(DataPermissionDO::getType, type)
|
.eq(DataPermissionDO::getType, type);
|
||||||
.eq(DataPermissionDO::getUserId, userId);
|
|
||||||
// 查询用户角色
|
|
||||||
if (dataType.isToRole()) {
|
if (dataType.isToRole()) {
|
||||||
|
// 查询用户角色
|
||||||
List<Long> roleIdList = systemUserRoleDAO.selectRoleIdByUserId(userId);
|
List<Long> roleIdList = systemUserRoleDAO.selectRoleIdByUserId(userId);
|
||||||
if (!roleIdList.isEmpty()) {
|
wrapper.and(s -> s.eq(DataPermissionDO::getUserId, userId)
|
||||||
wrapper.or().in(DataPermissionDO::getRoleId, roleIdList);
|
.or()
|
||||||
}
|
.in(!roleIdList.isEmpty(), DataPermissionDO::getRoleId, roleIdList)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 单用户
|
||||||
|
wrapper.eq(DataPermissionDO::getUserId, userId);
|
||||||
}
|
}
|
||||||
// 查询数据库
|
// 查询数据库
|
||||||
list = dataPermissionDAO.of()
|
list = dataPermissionDAO.of()
|
||||||
|
|||||||
@@ -26,7 +26,11 @@
|
|||||||
UPDATE data_extra
|
UPDATE data_extra
|
||||||
SET value = JSON_REPLACE(value,
|
SET value = JSON_REPLACE(value,
|
||||||
"$.keyId", NULL,
|
"$.keyId", NULL,
|
||||||
"$.authType", IF(JSON_EXTRACT(value, "$.authType") = 'KEY', 'DEFAULT', JSON_EXTRACT(value, "$.authType")))
|
"$.authType", IF(
|
||||||
|
JSON_EXTRACT(value, "$.authType") = 'CUSTOM_KEY',
|
||||||
|
'DEFAULT',
|
||||||
|
JSON_EXTRACT(value, "$.authType")
|
||||||
|
))
|
||||||
WHERE deleted = 0
|
WHERE deleted = 0
|
||||||
AND type = 'HOST'
|
AND type = 'HOST'
|
||||||
AND item = 'ssh'
|
AND item = 'ssh'
|
||||||
@@ -37,7 +41,11 @@
|
|||||||
UPDATE data_extra
|
UPDATE data_extra
|
||||||
SET value = JSON_REPLACE(value,
|
SET value = JSON_REPLACE(value,
|
||||||
"$.identityId", NULL,
|
"$.identityId", NULL,
|
||||||
"$.authType", IF(JSON_EXTRACT(value, "$.authType") = 'IDENTITY', 'DEFAULT', JSON_EXTRACT(value, "$.authType")))
|
"$.authType", IF(
|
||||||
|
JSON_EXTRACT(value, "$.authType") = 'CUSTOM_IDENTITY',
|
||||||
|
'DEFAULT',
|
||||||
|
JSON_EXTRACT(value, "$.authType")
|
||||||
|
))
|
||||||
WHERE deleted = 0
|
WHERE deleted = 0
|
||||||
AND type = 'HOST'
|
AND type = 'HOST'
|
||||||
AND item = 'ssh'
|
AND item = 'ssh'
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ export function getCurrentAuthorizedHost() {
|
|||||||
* 查询当前用户已授权的主机秘钥
|
* 查询当前用户已授权的主机秘钥
|
||||||
*/
|
*/
|
||||||
export function getCurrentAuthorizedHostKey() {
|
export function getCurrentAuthorizedHostKey() {
|
||||||
return axios.get<HostKeyQueryResponse>('/asset/authorized-data/current-host-key');
|
return axios.get<Array<HostKeyQueryResponse>>('/asset/authorized-data/current-host-key');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询当前用户已授权的主机身份
|
* 查询当前用户已授权的主机身份
|
||||||
*/
|
*/
|
||||||
export function getCurrentAuthorizedHostIdentity() {
|
export function getCurrentAuthorizedHostIdentity() {
|
||||||
return axios.get<HostIdentityQueryResponse>('/asset/authorized-data/current-host-identity');
|
return axios.get<Array<HostIdentityQueryResponse>>('/asset/authorized-data/current-host-identity');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,48 @@ export interface HostAliasUpdateRequest {
|
|||||||
name?: string;
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主机拓展信息查询请求
|
||||||
|
*/
|
||||||
|
export interface HostExtraQueryRequest {
|
||||||
|
hostId?: number;
|
||||||
|
item: string;
|
||||||
|
items?: Array<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主机拓展信息更新请求
|
||||||
|
*/
|
||||||
|
export interface HostExtraUpdateRequest {
|
||||||
|
hostId?: number;
|
||||||
|
item: string;
|
||||||
|
extra: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改主机别名
|
* 修改主机别名
|
||||||
*/
|
*/
|
||||||
export function updateHostAlias(request: HostAliasUpdateRequest) {
|
export function updateHostAlias(request: HostAliasUpdateRequest) {
|
||||||
return axios.put('/asset/host-extra/update-alias', request);
|
return axios.put('/asset/host-extra/update-alias', request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取主机拓展信息
|
||||||
|
*/
|
||||||
|
export function getHostExtraItem<T>(params: HostExtraQueryRequest) {
|
||||||
|
return axios.get<T>('/asset/host-extra/get', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取多个主机拓展信息
|
||||||
|
*/
|
||||||
|
export function getHostExtraItemList(request: HostExtraQueryRequest) {
|
||||||
|
return axios.post<Record<string, Record<string, any>>>('/asset/host-extra/list', request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改主机拓展信息
|
||||||
|
*/
|
||||||
|
export function updateHostExtra(request: HostExtraUpdateRequest) {
|
||||||
|
return axios.put('/asset/host-extra/update', request);
|
||||||
|
}
|
||||||
|
|||||||
@@ -94,6 +94,11 @@ body {
|
|||||||
border-color: rgb(var(--gray-2));
|
border-color: rgb(var(--gray-2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.full {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.disabled {
|
.disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,10 +256,10 @@
|
|||||||
const localeRef = ref();
|
const localeRef = ref();
|
||||||
|
|
||||||
// 打开应用设置
|
// 打开应用设置
|
||||||
const openAppSetting = inject(openAppSettingKey) as () => void;
|
const openAppSetting = inject<() => void>(openAppSettingKey);
|
||||||
|
|
||||||
// 注入收缩菜单
|
// 注入收缩菜单
|
||||||
const toggleDrawerMenu = inject(toggleDrawerMenuKey) as () => void;
|
const toggleDrawerMenu = inject<() => void>(toggleDrawerMenuKey);
|
||||||
|
|
||||||
// 切换主题
|
// 切换主题
|
||||||
const handleToggleTheme = () => {
|
const handleToggleTheme = () => {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: Number,
|
modelValue: Number,
|
||||||
|
authorized: Boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['update:modelValue']);
|
const emits = defineEmits(['update:modelValue']);
|
||||||
@@ -45,7 +46,9 @@
|
|||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const hostIdentities = await cacheStore.loadHostIdentities();
|
const hostIdentities = props.authorized
|
||||||
|
? await cacheStore.loadAuthorizedHostIdentities()
|
||||||
|
: await cacheStore.loadHostIdentities();
|
||||||
optionData.value = hostIdentities.map(s => {
|
optionData.value = hostIdentities.map(s => {
|
||||||
return {
|
return {
|
||||||
label: `${s.name} (${s.username})`,
|
label: `${s.name} (${s.username})`,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: Number,
|
modelValue: Number,
|
||||||
|
authorized: Boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['update:modelValue']);
|
const emits = defineEmits(['update:modelValue']);
|
||||||
@@ -45,7 +46,9 @@
|
|||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const hostKeys = await cacheStore.loadHostKeys();
|
const hostKeys = props.authorized
|
||||||
|
? await cacheStore.loadAuthorizedHostKeys()
|
||||||
|
: await cacheStore.loadHostKeys();
|
||||||
optionData.value = hostKeys.map(s => {
|
optionData.value = hostKeys.map(s => {
|
||||||
return {
|
return {
|
||||||
label: s.name,
|
label: s.name,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
15
orion-ops-ui/src/store/modules/cache/index.ts
vendored
15
orion-ops-ui/src/store/modules/cache/index.ts
vendored
@@ -11,10 +11,13 @@ import { getHostIdentityList } from '@/api/asset/host-identity';
|
|||||||
import { getHostList } from '@/api/asset/host';
|
import { getHostList } from '@/api/asset/host';
|
||||||
import { getHostGroupTree } from '@/api/asset/host-group';
|
import { getHostGroupTree } from '@/api/asset/host-group';
|
||||||
import { getMenuList } from '@/api/system/menu';
|
import { getMenuList } from '@/api/system/menu';
|
||||||
|
import { getCurrentAuthorizedHostIdentity, getCurrentAuthorizedHostKey } from '@/api/asset/asset-authorized-data';
|
||||||
|
|
||||||
export type CacheType = 'users' | 'menus' | 'roles'
|
export type CacheType = 'users' | 'menus' | 'roles'
|
||||||
| 'host' | 'hostGroups' | 'hostKeys' | 'hostIdentities'
|
| 'host' | 'hostGroups' | 'hostKeys' | 'hostIdentities'
|
||||||
| 'dictKeys' | string
|
| 'dictKeys'
|
||||||
|
| 'authorizedHostKeys' | 'authorizedHostIdentities'
|
||||||
|
| string
|
||||||
|
|
||||||
export default defineStore('cache', {
|
export default defineStore('cache', {
|
||||||
state: (): CacheState => ({}),
|
state: (): CacheState => ({}),
|
||||||
@@ -98,5 +101,15 @@ export default defineStore('cache', {
|
|||||||
return await this.load(`${type}_Tags`, () => getTagList(type), force);
|
return await this.load(`${type}_Tags`, () => getTagList(type), force);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 获取已授权的主机秘钥列表
|
||||||
|
async loadAuthorizedHostKeys(force = false) {
|
||||||
|
return await this.load('authorizedHostKeys', getCurrentAuthorizedHostKey, force);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取已授权的主机身份列表
|
||||||
|
async loadAuthorizedHostIdentities(force = false) {
|
||||||
|
return await this.load('authorizedHostIdentities', getCurrentAuthorizedHostIdentity, force);
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ export interface CacheState {
|
|||||||
hostKeys?: HostKeyQueryResponse[];
|
hostKeys?: HostKeyQueryResponse[];
|
||||||
hostIdentities?: HostIdentityQueryResponse[];
|
hostIdentities?: HostIdentityQueryResponse[];
|
||||||
dictKeys?: DictKeyQueryResponse[];
|
dictKeys?: DictKeyQueryResponse[];
|
||||||
|
authorizedHostKeys?: HostKeyQueryResponse[];
|
||||||
|
authorizedHostIdentities?: HostIdentityQueryResponse[];
|
||||||
|
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -26,9 +26,9 @@
|
|||||||
label="用户名"
|
label="用户名"
|
||||||
:rules="usernameRules"
|
:rules="usernameRules"
|
||||||
label-col-flex="60px"
|
label-col-flex="60px"
|
||||||
:help="AuthType.IDENTITY === formModel.authType ? '将使用主机身份的用户名' : undefined">
|
:help="SshAuthType.IDENTITY === formModel.authType ? '将使用主机身份的用户名' : undefined">
|
||||||
<a-input v-model="formModel.username"
|
<a-input v-model="formModel.username"
|
||||||
:disabled="AuthType.IDENTITY === formModel.authType"
|
:disabled="SshAuthType.IDENTITY === formModel.authType"
|
||||||
placeholder="请输入用户名" />
|
placeholder="请输入用户名" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- SSH 端口 -->
|
<!-- SSH 端口 -->
|
||||||
@@ -48,10 +48,10 @@
|
|||||||
<a-radio-group type="button"
|
<a-radio-group type="button"
|
||||||
class="auth-type-group usn"
|
class="auth-type-group usn"
|
||||||
v-model="formModel.authType"
|
v-model="formModel.authType"
|
||||||
:options="toOptions(authTypeKey)" />
|
:options="toOptions(sshAuthTypeKey)" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 主机密码 -->
|
<!-- 主机密码 -->
|
||||||
<a-form-item v-if="AuthType.PASSWORD === formModel.authType"
|
<a-form-item v-if="SshAuthType.PASSWORD === formModel.authType"
|
||||||
field="password"
|
field="password"
|
||||||
label="主机密码"
|
label="主机密码"
|
||||||
:rules="passwordRules"
|
:rules="passwordRules"
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
unchecked-text="使用原密码" />
|
unchecked-text="使用原密码" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 主机秘钥 -->
|
<!-- 主机秘钥 -->
|
||||||
<a-form-item v-if="AuthType.KEY === formModel.authType"
|
<a-form-item v-if="SshAuthType.KEY === formModel.authType"
|
||||||
field="keyId"
|
field="keyId"
|
||||||
label="主机秘钥"
|
label="主机秘钥"
|
||||||
:hide-asterisk="true"
|
:hide-asterisk="true"
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
<host-key-selector v-model="formModel.keyId" />
|
<host-key-selector v-model="formModel.keyId" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 主机身份 -->
|
<!-- 主机身份 -->
|
||||||
<a-form-item v-if="AuthType.IDENTITY === formModel.authType"
|
<a-form-item v-if="SshAuthType.IDENTITY === formModel.authType"
|
||||||
field="identityId"
|
field="identityId"
|
||||||
label="主机身份"
|
label="主机身份"
|
||||||
:hide-asterisk="true"
|
:hide-asterisk="true"
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
import type { HostSshConfig } from './types/const';
|
import type { HostSshConfig } from './types/const';
|
||||||
import { reactive, ref, watch } from 'vue';
|
import { reactive, ref, watch } from 'vue';
|
||||||
import { updateHostConfigStatus, updateHostConfig } from '@/api/asset/host-config';
|
import { updateHostConfigStatus, updateHostConfig } from '@/api/asset/host-config';
|
||||||
import { authTypeKey, AuthType } from './types/const';
|
import { sshAuthTypeKey, SshAuthType } from './types/const';
|
||||||
import rules from './types/form.rules';
|
import rules from './types/form.rules';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import useLoading from '@/hooks/loading';
|
import useLoading from '@/hooks/loading';
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
username: undefined,
|
username: undefined,
|
||||||
port: undefined,
|
port: undefined,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
authType: AuthType.PASSWORD,
|
authType: SshAuthType.PASSWORD,
|
||||||
keyId: undefined,
|
keyId: undefined,
|
||||||
identityId: undefined,
|
identityId: undefined,
|
||||||
connectTimeout: undefined,
|
connectTimeout: undefined,
|
||||||
@@ -195,7 +195,7 @@
|
|||||||
cb('用户名长度不能大于128位');
|
cb('用户名长度不能大于128位');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (formModel.value.authType !== AuthType.IDENTITY && !value) {
|
if (formModel.value.authType !== SshAuthType.IDENTITY && !value) {
|
||||||
cb('请输入用户名');
|
cb('请输入用户名');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ export interface HostSshConfig {
|
|||||||
hasPassword?: boolean;
|
hasPassword?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 主机验证方式
|
// 主机 ssh 验证方式
|
||||||
export const AuthType = {
|
export const SshAuthType = {
|
||||||
// 密码验证
|
// 密码验证
|
||||||
PASSWORD: 'PASSWORD',
|
PASSWORD: 'PASSWORD',
|
||||||
// 秘钥验证
|
// 秘钥验证
|
||||||
@@ -25,7 +25,7 @@ export const AuthType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 主机验证方式 字典项
|
// 主机验证方式 字典项
|
||||||
export const authTypeKey = 'hostAuthTypeType';
|
export const sshAuthTypeKey = 'hostSshAuthType';
|
||||||
|
|
||||||
// 加载的字典值
|
// 加载的字典值
|
||||||
export const dictKeys = [authTypeKey];
|
export const dictKeys = [sshAuthTypeKey];
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
:max-length="32"
|
:max-length="32"
|
||||||
:disabled="item.loading"
|
:disabled="item.loading"
|
||||||
size="mini"
|
size="mini"
|
||||||
placeholder="主机别名"
|
:placeholder="`${item.name} (${item.code})`"
|
||||||
@blur="saveAlias(item)"
|
@blur="saveAlias(item)"
|
||||||
@pressEnter="saveAlias(item)"
|
@pressEnter="saveAlias(item)"
|
||||||
@change="saveAlias(item)"
|
@change="saveAlias(item)"
|
||||||
@@ -164,11 +164,12 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { HostQueryResponse } from '@/api/asset/host';
|
import type { HostQueryResponse } from '@/api/asset/host';
|
||||||
|
import { ref, nextTick, inject } from 'vue';
|
||||||
import useFavorite from '@/hooks/favorite';
|
import useFavorite from '@/hooks/favorite';
|
||||||
import { dataColor } from '@/utils';
|
import { dataColor } from '@/utils';
|
||||||
import { tagColor } from '@/views/asset/host-list/types/const';
|
import { tagColor } from '@/views/asset/host-list/types/const';
|
||||||
import { ref, nextTick } from 'vue';
|
|
||||||
import { updateHostAlias } from '@/api/asset/host-extra';
|
import { updateHostAlias } from '@/api/asset/host-extra';
|
||||||
|
import { sshModalKey } from '../../types/terminal.const';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
hostList: Array<HostQueryResponse>,
|
hostList: Array<HostQueryResponse>,
|
||||||
@@ -183,7 +184,7 @@
|
|||||||
const clickEditAlias = (item: HostQueryResponse) => {
|
const clickEditAlias = (item: HostQueryResponse) => {
|
||||||
item.editable = true;
|
item.editable = true;
|
||||||
if (!item.alias) {
|
if (!item.alias) {
|
||||||
item.alias = `${item.name} (${item.code})`;
|
item.alias = '';
|
||||||
}
|
}
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
aliasNameInput.value?.focus();
|
aliasNameInput.value?.focus();
|
||||||
@@ -217,9 +218,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 打开配置
|
// 打开配置
|
||||||
const openSetting = (item: HostQueryResponse) => {
|
const openSetting = inject<(record: HostQueryResponse) => void>(sshModalKey);
|
||||||
console.log('set', item);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 设置收藏
|
// 设置收藏
|
||||||
const setFavorite = async (item: HostQueryResponse) => {
|
const setFavorite = async (item: HostQueryResponse) => {
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
class="list-view-container"
|
class="list-view-container"
|
||||||
:hostList="hostList"
|
:hostList="hostList"
|
||||||
empty-value="暂无连接记录, 快去体验吧!" />
|
empty-value="暂无连接记录, 快去体验吧!" />
|
||||||
|
<!-- 修改主机设置模态框 -->
|
||||||
|
<ssh-extra-modal ref="sshModal" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -31,12 +33,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, provide, ref, watch } from 'vue';
|
||||||
import { NewConnectionType } from '../../types/terminal.const';
|
import { NewConnectionType, sshModalKey } from '../../types/terminal.const';
|
||||||
import { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
import { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||||
import { HostQueryResponse } from '@/api/asset/host';
|
import { HostQueryResponse } from '@/api/asset/host';
|
||||||
import HostGroupView from './host-group-view.vue';
|
import HostGroupView from './host-group-view.vue';
|
||||||
import HostListView from './host-list-view.vue';
|
import HostListView from './host-list-view.vue';
|
||||||
|
import SshExtraModal from './ssh-extra-modal.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
hosts: AuthorizedHostQueryResponse,
|
hosts: AuthorizedHostQueryResponse,
|
||||||
@@ -51,6 +54,12 @@
|
|||||||
? props.hosts.groupTree[0].key
|
? props.hosts.groupTree[0].key
|
||||||
: 0
|
: 0
|
||||||
);
|
);
|
||||||
|
const sshModal = ref();
|
||||||
|
|
||||||
|
// 暴露打开 ssh 配置模态框
|
||||||
|
provide(sshModalKey, (record: any) => {
|
||||||
|
sshModal.value?.open(record);
|
||||||
|
});
|
||||||
|
|
||||||
// 主机数据处理
|
// 主机数据处理
|
||||||
const shuffleHosts = () => {
|
const shuffleHosts = () => {
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal v-model:visible="visible"
|
||||||
|
body-class="modal-form"
|
||||||
|
title-align="start"
|
||||||
|
:title="title"
|
||||||
|
:top="80"
|
||||||
|
:align-center="false"
|
||||||
|
:draggable="true"
|
||||||
|
:mask-closable="false"
|
||||||
|
:unmount-on-close="true"
|
||||||
|
ok-text="保存"
|
||||||
|
:ok-button-props="{ disabled: loading }"
|
||||||
|
:cancel-button-props="{ disabled: loading }"
|
||||||
|
:on-before-ok="handlerOk"
|
||||||
|
@close="handleClose">
|
||||||
|
<a-spin class="full" :loading="loading">
|
||||||
|
<a-form :model="formModel"
|
||||||
|
ref="formRef"
|
||||||
|
label-align="right"
|
||||||
|
:style="{ width: '460px' }"
|
||||||
|
:label-col-props="{ span: 6 }"
|
||||||
|
:wrapper-col-props="{ span: 18 }"
|
||||||
|
:rules="{}">
|
||||||
|
<!-- 验证方式 -->
|
||||||
|
<a-form-item field="authType" label="验证方式">
|
||||||
|
<a-radio-group type="button"
|
||||||
|
v-model="formModel.authType"
|
||||||
|
:options="toOptions(extraSshAuthTypeKey)" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 用户名 -->
|
||||||
|
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
|
||||||
|
field="username"
|
||||||
|
label="用户名">
|
||||||
|
<a-input v-model="formModel.username" placeholder="请输入用户名" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 主机秘钥 -->
|
||||||
|
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_KEY"
|
||||||
|
field="keyId"
|
||||||
|
label="主机秘钥"
|
||||||
|
:rules="{ required: true, message: '请选择主机秘钥' }">
|
||||||
|
<host-key-selector v-model="formModel.keyId"
|
||||||
|
:authorized="true" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 主机身份 -->
|
||||||
|
<a-form-item v-if="formModel.authType === ExtraSshAuthType.CUSTOM_IDENTITY"
|
||||||
|
field="identityId"
|
||||||
|
label="主机身份"
|
||||||
|
:rules="{ required: true, message: '请选择主机身份' }">
|
||||||
|
<host-identity-selector v-model="formModel.identityId"
|
||||||
|
:authorized="true" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'ssh-extra-modal'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { HostQueryResponse } from '@/api/asset/host';
|
||||||
|
import type { SshExtraModel } from '../../types/terminal.const';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import useVisible from '@/hooks/visible';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import { getHostExtraItem, updateHostExtra } from '@/api/asset/host-extra';
|
||||||
|
import { ExtraSshAuthType, extraSshAuthTypeKey } from '../../types/terminal.const';
|
||||||
|
import { useDictStore } from '@/store';
|
||||||
|
import HostIdentitySelector from '@/components/asset/host-identity/host-identity-selector.vue';
|
||||||
|
import HostKeySelector from '@/components/asset/host-key/host-key-selector.vue';
|
||||||
|
|
||||||
|
const { toOptions } = useDictStore();
|
||||||
|
const { visible, setVisible } = useVisible();
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
|
||||||
|
const title = ref<string>();
|
||||||
|
const hostId = ref<number>();
|
||||||
|
const formRef = ref();
|
||||||
|
const formModel = ref<SshExtraModel>({});
|
||||||
|
|
||||||
|
// 打开配置
|
||||||
|
const open = (record: HostQueryResponse) => {
|
||||||
|
hostId.value = record.id;
|
||||||
|
title.value = record.alias || `${record.name} (${record.code})`;
|
||||||
|
renderForm();
|
||||||
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ open });
|
||||||
|
|
||||||
|
// 渲染表单
|
||||||
|
const renderForm = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const { data } = await getHostExtraItem<SshExtraModel>({ hostId: hostId.value, item: 'ssh' });
|
||||||
|
formModel.value = data;
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确定
|
||||||
|
const handlerOk = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// 验证参数
|
||||||
|
const error = await formRef.value.validate();
|
||||||
|
if (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 修改
|
||||||
|
await updateHostExtra({
|
||||||
|
hostId: hostId.value,
|
||||||
|
item: 'ssh',
|
||||||
|
extra: JSON.stringify(formModel.value)
|
||||||
|
});
|
||||||
|
Message.success('保存成功');
|
||||||
|
// 清空
|
||||||
|
handlerClear();
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
const handleClose = () => {
|
||||||
|
handlerClear();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空
|
||||||
|
const handlerClear = () => {
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -34,9 +34,9 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { TabItem } from './types/terminal.const';
|
import type { TabItem } from './types/terminal.const';
|
||||||
import { ref, onBeforeMount } from 'vue';
|
import { ref, onBeforeMount, onUnmounted } from 'vue';
|
||||||
import { TabType, InnerTabs, dictKeys } from './types/terminal.const';
|
import { TabType, InnerTabs, dictKeys } from './types/terminal.const';
|
||||||
import { useDictStore, useTerminalStore } from '@/store';
|
import { useCacheStore, useDictStore, useTerminalStore } from '@/store';
|
||||||
import TerminalHeader from './components/layout/terminal-header.vue';
|
import TerminalHeader from './components/layout/terminal-header.vue';
|
||||||
import TerminalLeftSidebar from './components/layout/terminal-left-sidebar.vue';
|
import TerminalLeftSidebar from './components/layout/terminal-left-sidebar.vue';
|
||||||
import TerminalRightSidebar from './components/layout/terminal-right-sidebar.vue';
|
import TerminalRightSidebar from './components/layout/terminal-right-sidebar.vue';
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
|
|
||||||
const terminalStore = useTerminalStore();
|
const terminalStore = useTerminalStore();
|
||||||
const dictStore = useDictStore();
|
const dictStore = useDictStore();
|
||||||
|
const cacheStore = useCacheStore();
|
||||||
|
|
||||||
const render = ref(false);
|
const render = ref(false);
|
||||||
const activeKey = ref(InnerTabs.NEW_CONNECTION.key);
|
const activeKey = ref(InnerTabs.NEW_CONNECTION.key);
|
||||||
@@ -93,6 +94,11 @@
|
|||||||
await dictStore.loadKeys([...dictKeys]);
|
await dictStore.loadKeys([...dictKeys]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 卸载时清除 cache
|
||||||
|
onUnmounted(() => {
|
||||||
|
cacheStore.reset('authorizedHostKeys', 'authorizedHostIdentities');
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -51,6 +51,27 @@ export const NewConnectionType = {
|
|||||||
LATEST: 'latest'
|
LATEST: 'latest'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ssh 额外配置
|
||||||
|
export interface SshExtraModel {
|
||||||
|
authType?: string;
|
||||||
|
username?: string;
|
||||||
|
keyId?: number;
|
||||||
|
identityId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主机额外配置 ssh 认证方式
|
||||||
|
export const ExtraSshAuthType = {
|
||||||
|
// 使用默认认证方式
|
||||||
|
DEFAULT: 'DEFAULT',
|
||||||
|
// 自定义秘钥
|
||||||
|
CUSTOM_KEY: 'CUSTOM_KEY',
|
||||||
|
// 自定义身份
|
||||||
|
CUSTOM_IDENTITY: 'CUSTOM_IDENTITY',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开 sshModal key
|
||||||
|
export const sshModalKey = Symbol();
|
||||||
|
|
||||||
// 字体后缀 兜底
|
// 字体后缀 兜底
|
||||||
export const fontFamilySuffix = ',courier-new, courier, monospace';
|
export const fontFamilySuffix = ',courier-new, courier, monospace';
|
||||||
|
|
||||||
@@ -72,9 +93,13 @@ export const cursorStyleKey = 'terminalCursorStyle';
|
|||||||
// 终端新建连接类型
|
// 终端新建连接类型
|
||||||
export const NewConnectionTypeKey = 'terminalNewConnectionType';
|
export const NewConnectionTypeKey = 'terminalNewConnectionType';
|
||||||
|
|
||||||
|
// 终端新建连接类型
|
||||||
|
export const extraSshAuthTypeKey = 'hostExtraSshAuthType';
|
||||||
|
|
||||||
// 加载的字典值
|
// 加载的字典值
|
||||||
export const dictKeys = [
|
export const dictKeys = [
|
||||||
darkThemeKey, fontFamilyKey,
|
darkThemeKey, fontFamilyKey,
|
||||||
fontSizeKey, fontWeightKey,
|
fontSizeKey, fontWeightKey,
|
||||||
cursorStyleKey, NewConnectionTypeKey
|
cursorStyleKey, NewConnectionTypeKey,
|
||||||
|
extraSshAuthTypeKey
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
:cancel-button-props="{ disabled: loading }"
|
:cancel-button-props="{ disabled: loading }"
|
||||||
:on-before-ok="handlerOk"
|
:on-before-ok="handlerOk"
|
||||||
@close="handleClose">
|
@close="handleClose">
|
||||||
<a-spin :loading="loading">
|
<a-spin class="full" :loading="loading">
|
||||||
<a-form :model="formModel"
|
<a-form :model="formModel"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-align="right"
|
label-align="right"
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
<!-- 分配角色 -->
|
<!-- 分配角色 -->
|
||||||
<a-button type="text"
|
<a-button type="text"
|
||||||
size="mini"
|
size="mini"
|
||||||
|
:disabled="record.id === userStore.id"
|
||||||
v-permission="['infra:system-user:grant-role']"
|
v-permission="['infra:system-user:grant-role']"
|
||||||
@click="emits('openGrantRole', record)">
|
@click="emits('openGrantRole', record)">
|
||||||
分配角色
|
分配角色
|
||||||
|
|||||||
Reference in New Issue
Block a user