🔨 添加 sftp 会话类型.
This commit is contained in:
@@ -8,7 +8,7 @@ import type {
|
|||||||
TerminalShortcutSetting,
|
TerminalShortcutSetting,
|
||||||
TerminalState
|
TerminalState
|
||||||
} from './types';
|
} from './types';
|
||||||
import type { PanelSessionTab, TerminalPanelTabItem } from '@/views/host/terminal/types/terminal.type';
|
import type { ISshSession, PanelSessionTab, TerminalPanelTabItem } from '@/views/host/terminal/types/terminal.type';
|
||||||
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||||
import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
|
import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
|
||||||
import type { HostQueryResponse } from '@/api/asset/host';
|
import type { HostQueryResponse } from '@/api/asset/host';
|
||||||
@@ -64,7 +64,8 @@ export default defineStore('terminal', {
|
|||||||
} as TerminalShortcutSetting,
|
} as TerminalShortcutSetting,
|
||||||
},
|
},
|
||||||
hosts: {} as AuthorizedHostQueryResponse,
|
hosts: {} as AuthorizedHostQueryResponse,
|
||||||
tabManager: new TerminalTabManager(TerminalTabs.NEW_CONNECTION),
|
// fixme
|
||||||
|
tabManager: new TerminalTabManager(TerminalTabs.TERMINAL_PANEL),
|
||||||
panelManager: new TerminalPanelManager(),
|
panelManager: new TerminalPanelManager(),
|
||||||
sessionManager: new TerminalSessionManager()
|
sessionManager: new TerminalSessionManager()
|
||||||
}),
|
}),
|
||||||
@@ -173,8 +174,8 @@ export default defineStore('terminal', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 检查当前是否为终端页面 并且获取当前终端会话
|
// 检查当前是否为终端页面 并且获取当前 ssh 会话
|
||||||
getAndCheckCurrentTerminalSession(tips: boolean = true) {
|
getAndCheckCurrentSshSession(tips: boolean = true) {
|
||||||
// 获取当前 activeTab
|
// 获取当前 activeTab
|
||||||
const activeTab = this.tabManager.active;
|
const activeTab = this.tabManager.active;
|
||||||
if (activeTab !== TerminalTabs.TERMINAL_PANEL.key) {
|
if (activeTab !== TerminalTabs.TERMINAL_PANEL.key) {
|
||||||
@@ -184,15 +185,15 @@ export default defineStore('terminal', {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 获取当前会话
|
// 获取当前会话
|
||||||
const session = this.getCurrentTerminalSession();
|
const session = this.getCurrentSshSession();
|
||||||
if (!session && tips) {
|
if (!session && tips) {
|
||||||
Message.warning('请打开终端');
|
Message.warning('请打开终端');
|
||||||
}
|
}
|
||||||
return session;
|
return session;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取当前终端会话
|
// 获取当前 ssh 会话
|
||||||
getCurrentTerminalSession() {
|
getCurrentSshSession() {
|
||||||
// 获取面板会话
|
// 获取面板会话
|
||||||
const sessionTab = this.panelManager
|
const sessionTab = this.panelManager
|
||||||
.getCurrentPanel()
|
.getCurrentPanel()
|
||||||
@@ -201,7 +202,7 @@ export default defineStore('terminal', {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 获取会话
|
// 获取会话
|
||||||
return this.sessionManager.getSession(sessionTab.key);
|
return this.sessionManager.getSession<ISshSession>(sessionTab.key);
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
|
|
||||||
const { loading, setLoading } = useLoading();
|
const { loading, setLoading } = useLoading();
|
||||||
const { visible, setVisible } = useVisible();
|
const { visible, setVisible } = useVisible();
|
||||||
const { getCurrentTerminalSession } = useTerminalStore();
|
const { getCurrentSshSession } = useTerminalStore();
|
||||||
const cacheStore = useCacheStore();
|
const cacheStore = useCacheStore();
|
||||||
|
|
||||||
const formDrawer = ref();
|
const formDrawer = ref();
|
||||||
@@ -267,7 +267,7 @@
|
|||||||
// 关闭回调
|
// 关闭回调
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
// 聚焦终端
|
// 聚焦终端
|
||||||
getCurrentTerminalSession()?.focus();
|
getCurrentSshSession()?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { copy } = useCopy();
|
const { copy } = useCopy();
|
||||||
const { getAndCheckCurrentTerminalSession } = useTerminalStore();
|
const { getAndCheckCurrentSshSession } = useTerminalStore();
|
||||||
|
|
||||||
let clickCount = 0;
|
let clickCount = 0;
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@
|
|||||||
|
|
||||||
// 写入命令
|
// 写入命令
|
||||||
const write = (command: string) => {
|
const write = (command: string) => {
|
||||||
const handler = getAndCheckCurrentTerminalSession()?.handler;
|
const handler = getAndCheckCurrentSshSession()?.handler;
|
||||||
if (handler && handler.enabledStatus('checkAppendMissing')) {
|
if (handler && handler.enabledStatus('checkAppendMissing')) {
|
||||||
handler.checkAppendMissing(command);
|
handler.checkAppendMissing(command);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,17 +45,17 @@
|
|||||||
import TerminalShortcutSetting from '../setting/shortcut/terminal-shortcut-setting.vue';
|
import TerminalShortcutSetting from '../setting/shortcut/terminal-shortcut-setting.vue';
|
||||||
import TerminalPanelsView from '@/views/host/terminal/components/layout/terminal-panels-view.vue';
|
import TerminalPanelsView from '@/views/host/terminal/components/layout/terminal-panels-view.vue';
|
||||||
|
|
||||||
const { preference, tabManager, getCurrentTerminalSession } = useTerminalStore();
|
const { preference, tabManager, getCurrentSshSession } = useTerminalStore();
|
||||||
|
|
||||||
// 监听 tab 切换
|
// 监听 tab 切换
|
||||||
watch(() => tabManager.active, (active, before) => {
|
watch(() => tabManager.active, (active, before) => {
|
||||||
// 失焦 tab
|
// 失焦 tab
|
||||||
if (before === TerminalTabs.TERMINAL_PANEL.key) {
|
if (before === TerminalTabs.TERMINAL_PANEL.key) {
|
||||||
getCurrentTerminalSession()?.blur();
|
getCurrentSshSession()?.blur();
|
||||||
}
|
}
|
||||||
// 聚焦 tab
|
// 聚焦 tab
|
||||||
if (active === TerminalTabs.TERMINAL_PANEL.key) {
|
if (active === TerminalTabs.TERMINAL_PANEL.key) {
|
||||||
getCurrentTerminalSession()?.focus();
|
getCurrentSshSession()?.focus();
|
||||||
}
|
}
|
||||||
// 修改标题
|
// 修改标题
|
||||||
document.title = Object.values(TerminalTabs)
|
document.title = Object.values(TerminalTabs)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
const emits = defineEmits(['openTransfer']);
|
const emits = defineEmits(['openTransfer']);
|
||||||
|
|
||||||
const { getAndCheckCurrentTerminalSession } = useTerminalStore();
|
const { getAndCheckCurrentSshSession } = useTerminalStore();
|
||||||
|
|
||||||
const snippetRef = ref();
|
const snippetRef = ref();
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
// 终端截屏
|
// 终端截屏
|
||||||
const screenshot = () => {
|
const screenshot = () => {
|
||||||
const handler = getAndCheckCurrentTerminalSession()?.handler;
|
const handler = getAndCheckCurrentSshSession()?.handler;
|
||||||
if (handler && handler.enabledStatus('screenshot')) {
|
if (handler && handler.enabledStatus('screenshot')) {
|
||||||
handler.screenshot();
|
handler.screenshot();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ITerminalTabManager, TerminalPanelTabItem } from '../../types/terminal.type';
|
import type { ISshSession, ITerminalTabManager, TerminalPanelTabItem } from '../../types/terminal.type';
|
||||||
import { watch } from 'vue';
|
import { watch } from 'vue';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { PanelSessionType } from '../../types/terminal.const';
|
import { PanelSessionType } from '../../types/terminal.const';
|
||||||
@@ -66,14 +66,14 @@
|
|||||||
if (before) {
|
if (before) {
|
||||||
const beforeTab = props.panel.items.find(s => s.key === before);
|
const beforeTab = props.panel.items.find(s => s.key === before);
|
||||||
if (beforeTab && beforeTab?.type === PanelSessionType.SSH.type) {
|
if (beforeTab && beforeTab?.type === PanelSessionType.SSH.type) {
|
||||||
sessionManager.getSession(before)?.blur();
|
sessionManager.getSession<ISshSession>(before)?.blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 终端自动聚焦
|
// 终端自动聚焦
|
||||||
if (active) {
|
if (active) {
|
||||||
const activeTab = props.panel.items.find(s => s.key === active);
|
const activeTab = props.panel.items.find(s => s.key === active);
|
||||||
if (activeTab && activeTab?.type === PanelSessionType.SSH.type) {
|
if (activeTab && activeTab?.type === PanelSessionType.SSH.type) {
|
||||||
sessionManager.getSession(active)?.focus();
|
sessionManager.getSession<ISshSession>(active)?.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 无终端自动关闭
|
// 无终端自动关闭
|
||||||
@@ -214,12 +214,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.arco-tabs-tab-title {
|
.arco-tabs-tab-title {
|
||||||
padding: 11px 18px 11px 14px;
|
padding: 11px 18px 7px 14px;
|
||||||
background: var(--color-bg-panel-tabs);
|
background: var(--color-bg-panel-tabs);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
height: var(--panel-nav-height);
|
height: var(--panel-nav-height);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
border-bottom: 4px transparent solid;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -31,12 +31,12 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ContextMenuItem, ITerminalSession } from '../../types/terminal.type';
|
import type { ContextMenuItem, ISshSession } from '../../types/terminal.type';
|
||||||
import { ActionBarItems } from '../../types/terminal.const';
|
import { ActionBarItems } from '../../types/terminal.const';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
session: ITerminalSession | undefined
|
session: ISshSession | undefined
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emits = defineEmits(['click']);
|
const emits = defineEmits(['click']);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ITerminalSession, TerminalTabItem, SidebarAction } from '../../types/terminal.type';
|
import type { ISshSession, TerminalTabItem, SidebarAction } from '../../types/terminal.type';
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { useDictStore, useTerminalStore } from '@/store';
|
import { useDictStore, useTerminalStore } from '@/store';
|
||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
const searchModal = ref();
|
const searchModal = ref();
|
||||||
const commandInput = ref();
|
const commandInput = ref();
|
||||||
const terminalRef = ref();
|
const terminalRef = ref();
|
||||||
const session = ref<ITerminalSession>();
|
const session = ref<ISshSession>();
|
||||||
|
|
||||||
// 发送命令
|
// 发送命令
|
||||||
const writeCommandInput = async (e: KeyboardEvent) => {
|
const writeCommandInput = async (e: KeyboardEvent) => {
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
// 初始化会话
|
// 初始化会话
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 创建终端处理器
|
// 创建终端处理器
|
||||||
session.value = await sessionManager.openSession(props.tab, {
|
session.value = await sessionManager.openSsh(props.tab, {
|
||||||
el: terminalRef.value,
|
el: terminalRef.value,
|
||||||
editorModal: editorModal.value,
|
editorModal: editorModal.value,
|
||||||
searchModal: searchModal.value
|
searchModal: searchModal.value
|
||||||
|
|||||||
41
orion-ops-ui/src/views/host/terminal/handler/sftp-session.ts
Normal file
41
orion-ops-ui/src/views/host/terminal/handler/sftp-session.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import type { ISftpSession, ITerminalChannel } from '../types/terminal.type';
|
||||||
|
import { InputProtocol } from '../types/terminal.protocol';
|
||||||
|
|
||||||
|
// sftp 会话实现
|
||||||
|
export default class SftpSession implements ISftpSession {
|
||||||
|
|
||||||
|
public readonly hostId: number;
|
||||||
|
|
||||||
|
public sessionId: string;
|
||||||
|
|
||||||
|
public connected: boolean;
|
||||||
|
|
||||||
|
private readonly channel: ITerminalChannel;
|
||||||
|
|
||||||
|
constructor(hostId: number,
|
||||||
|
sessionId: string,
|
||||||
|
channel: ITerminalChannel) {
|
||||||
|
this.hostId = hostId;
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
this.channel = channel;
|
||||||
|
this.connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置已连接
|
||||||
|
connect(): void {
|
||||||
|
this.connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 断开连接
|
||||||
|
disconnect(): void {
|
||||||
|
// 发送关闭消息
|
||||||
|
this.channel.send(InputProtocol.CLOSE, {
|
||||||
|
sessionId: this.sessionId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
close(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ShortcutKey, TerminalInteractSetting, TerminalShortcutKey } from '@/store/modules/terminal/types';
|
import type { ShortcutKey, TerminalInteractSetting, TerminalShortcutKey } from '@/store/modules/terminal/types';
|
||||||
import type { ITerminalSession, ITerminalSessionHandler, TerminalDomRef } from '../types/terminal.type';
|
import type { ISshSession, ISshSessionHandler, XtermDomRef } from '../types/terminal.type';
|
||||||
import type { Terminal } from 'xterm';
|
import type { Terminal } from 'xterm';
|
||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
import html2canvas from 'html2canvas';
|
import html2canvas from 'html2canvas';
|
||||||
@@ -31,21 +31,21 @@ const preventKeys: Array<ShortcutKey> = [
|
|||||||
|
|
||||||
const { copy: copyValue, readText } = useCopy();
|
const { copy: copyValue, readText } = useCopy();
|
||||||
|
|
||||||
// 终端会话处理器实现
|
// ssh 会话处理器实现
|
||||||
export default class TerminalSessionHandler implements ITerminalSessionHandler {
|
export default class SshSessionHandler implements ISshSessionHandler {
|
||||||
|
|
||||||
private readonly domRef: TerminalDomRef;
|
private readonly domRef: XtermDomRef;
|
||||||
|
|
||||||
private readonly inst: Terminal;
|
private readonly inst: Terminal;
|
||||||
|
|
||||||
private readonly session: ITerminalSession;
|
private readonly session: ISshSession;
|
||||||
|
|
||||||
private readonly interactSetting: TerminalInteractSetting;
|
private readonly interactSetting: TerminalInteractSetting;
|
||||||
|
|
||||||
private readonly shortcutKeys: Array<TerminalShortcutKey>;
|
private readonly shortcutKeys: Array<TerminalShortcutKey>;
|
||||||
|
|
||||||
constructor(session: ITerminalSession,
|
constructor(session: ISshSession,
|
||||||
domRef: TerminalDomRef) {
|
domRef: XtermDomRef) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.inst = session.inst;
|
this.inst = session.inst;
|
||||||
this.domRef = domRef;
|
this.domRef = domRef;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { UnwrapRef } from 'vue';
|
import type { UnwrapRef } from 'vue';
|
||||||
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
||||||
import type { ITerminalChannel, ITerminalSession, ITerminalSessionHandler, TerminalAddons, TerminalDomRef } from '../types/terminal.type';
|
import type { ISshSession, ISshSessionHandler, ITerminalChannel, XtermAddons, XtermDomRef } from '../types/terminal.type';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { InputProtocol } from '../types/terminal.protocol';
|
import { InputProtocol } from '../types/terminal.protocol';
|
||||||
import { fontFamilySuffix, TerminalShortcutType, TerminalStatus } from '../types/terminal.const';
|
import { fontFamilySuffix, TerminalShortcutType, TerminalStatus } from '../types/terminal.const';
|
||||||
@@ -13,10 +13,10 @@ import { CanvasAddon } from 'xterm-addon-canvas';
|
|||||||
import { WebglAddon } from 'xterm-addon-webgl';
|
import { WebglAddon } from 'xterm-addon-webgl';
|
||||||
import { playBell } from '@/utils/bell';
|
import { playBell } from '@/utils/bell';
|
||||||
import { addEventListen } from '@/utils/event';
|
import { addEventListen } from '@/utils/event';
|
||||||
import TerminalSessionHandler from './terminal-session-handler';
|
import SshSessionHandler from './ssh-session-handler';
|
||||||
|
|
||||||
// 终端会话实现
|
// ssh 会话实现
|
||||||
export default class TerminalSession implements ITerminalSession {
|
export default class SshSession implements ISshSession {
|
||||||
|
|
||||||
public readonly hostId: number;
|
public readonly hostId: number;
|
||||||
|
|
||||||
@@ -30,11 +30,11 @@ export default class TerminalSession implements ITerminalSession {
|
|||||||
|
|
||||||
public status: number;
|
public status: number;
|
||||||
|
|
||||||
public handler: ITerminalSessionHandler;
|
public handler: ISshSessionHandler;
|
||||||
|
|
||||||
private readonly channel: ITerminalChannel;
|
private readonly channel: ITerminalChannel;
|
||||||
|
|
||||||
private readonly addons: TerminalAddons;
|
private readonly addons: XtermAddons;
|
||||||
|
|
||||||
constructor(hostId: number,
|
constructor(hostId: number,
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
@@ -46,12 +46,12 @@ export default class TerminalSession implements ITerminalSession {
|
|||||||
this.canWrite = false;
|
this.canWrite = false;
|
||||||
this.status = TerminalStatus.CONNECTING;
|
this.status = TerminalStatus.CONNECTING;
|
||||||
this.inst = undefined as unknown as Terminal;
|
this.inst = undefined as unknown as Terminal;
|
||||||
this.handler = undefined as unknown as ITerminalSessionHandler;
|
this.handler = undefined as unknown as ISshSessionHandler;
|
||||||
this.addons = {} as TerminalAddons;
|
this.addons = {} as XtermAddons;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
init(domRef: TerminalDomRef): void {
|
init(domRef: XtermDomRef): void {
|
||||||
const { preference } = useTerminalStore();
|
const { preference } = useTerminalStore();
|
||||||
// 初始化实例
|
// 初始化实例
|
||||||
this.inst = new Terminal({
|
this.inst = new Terminal({
|
||||||
@@ -65,7 +65,7 @@ export default class TerminalSession implements ITerminalSession {
|
|||||||
scrollback: preference.sessionSetting.scrollBackLine,
|
scrollback: preference.sessionSetting.scrollBackLine,
|
||||||
});
|
});
|
||||||
// 处理器
|
// 处理器
|
||||||
this.handler = new TerminalSessionHandler(this, domRef);
|
this.handler = new SshSessionHandler(this, domRef);
|
||||||
// 注册快捷键
|
// 注册快捷键
|
||||||
this.registerShortcut(preference);
|
this.registerShortcut(preference);
|
||||||
// 注册事件
|
// 注册事件
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
import { ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager, OutputPayload } from '../types/terminal.type';
|
import { ISshSession, ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager, OutputPayload } from '../types/terminal.type';
|
||||||
import { InputProtocol } from '../types/terminal.protocol';
|
import { InputProtocol } from '../types/terminal.protocol';
|
||||||
import { TerminalStatus } from '../types/terminal.const';
|
import { TerminalStatus } from '../types/terminal.const';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import SshSession from './ssh-session';
|
||||||
|
import SftpSession from './sftp-session';
|
||||||
|
|
||||||
// 终端输出消息体处理器实现
|
// 终端输出消息体处理器实现
|
||||||
export default class TerminalOutputProcessor implements ITerminalOutputProcessor {
|
export default class TerminalOutputProcessor implements ITerminalOutputProcessor {
|
||||||
@@ -19,50 +22,81 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
|||||||
processCheck({ sessionId, result, msg }: OutputPayload): void {
|
processCheck({ sessionId, result, msg }: OutputPayload): void {
|
||||||
const success = !!Number.parseInt(result);
|
const success = !!Number.parseInt(result);
|
||||||
const session = this.sessionManager.getSession(sessionId);
|
const session = this.sessionManager.getSession(sessionId);
|
||||||
// 未成功展示错误信息
|
if (session instanceof SshSession) {
|
||||||
if (!success) {
|
// ssh 会话
|
||||||
session.write(`[91m${msg || ''}[0m`);
|
if (success) {
|
||||||
session.status = TerminalStatus.CLOSED;
|
// 检查成功发送 connect 命令
|
||||||
return;
|
const { preference } = useTerminalStore();
|
||||||
|
this.channel.send(InputProtocol.CONNECT, {
|
||||||
|
sessionId,
|
||||||
|
terminalType: preference.sessionSetting.terminalEmulationType || 'xterm',
|
||||||
|
cols: session.inst.cols,
|
||||||
|
rows: session.inst.rows
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 未成功展示错误信息
|
||||||
|
session.write(`[91m${msg || ''}[0m`);
|
||||||
|
session.status = TerminalStatus.CLOSED;
|
||||||
|
}
|
||||||
|
} else if (session instanceof SftpSession) {
|
||||||
|
// sftp 会话
|
||||||
|
if (success) {
|
||||||
|
// 检查成功发送 connect 命令
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 未成功提示错误信息
|
||||||
|
Message.error(msg || '建立 SFTP 失败');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const { preference } = useTerminalStore();
|
|
||||||
// 发送 connect 命令
|
|
||||||
this.channel.send(InputProtocol.CONNECT, {
|
|
||||||
sessionId,
|
|
||||||
terminalType: preference.sessionSetting.terminalEmulationType || 'xterm',
|
|
||||||
cols: session.inst.cols,
|
|
||||||
rows: session.inst.rows
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理连接消息
|
// 处理连接消息
|
||||||
processConnect({ sessionId, result, msg }: OutputPayload): void {
|
processConnect({ sessionId, result, msg }: OutputPayload): void {
|
||||||
const success = !!Number.parseInt(result);
|
const success = !!Number.parseInt(result);
|
||||||
const session = this.sessionManager.getSession(sessionId);
|
const session = this.sessionManager.getSession(sessionId);
|
||||||
// 未成功展示错误信息
|
if (session instanceof SshSession) {
|
||||||
if (!success) {
|
// ssh 会话
|
||||||
session.write(`[91m${msg || ''}[0m`);
|
if (success) {
|
||||||
session.status = TerminalStatus.CLOSED;
|
// 设置可写
|
||||||
return;
|
session.setCanWrite(true);
|
||||||
|
// 执行连接逻辑
|
||||||
|
session.connect();
|
||||||
|
} else {
|
||||||
|
// 未成功展示错误信息
|
||||||
|
session.write(`[91m${msg || ''}[0m`);
|
||||||
|
session.status = TerminalStatus.CLOSED;
|
||||||
|
}
|
||||||
|
} else if (session instanceof SftpSession) {
|
||||||
|
// sftp 会话
|
||||||
|
if (success) {
|
||||||
|
// 执行连接逻辑
|
||||||
|
session.connect();
|
||||||
|
} else {
|
||||||
|
// 未成功提示错误信息
|
||||||
|
Message.error(msg || '打开 SFTP 失败');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 设置可写
|
|
||||||
session.setCanWrite(true);
|
|
||||||
// 执行连接逻辑
|
|
||||||
session.connect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理关闭消息
|
// 处理关闭消息
|
||||||
processClose({ sessionId, msg }: OutputPayload): void {
|
processClose({ sessionId, msg }: OutputPayload): void {
|
||||||
const session = this.sessionManager.getSession(sessionId);
|
const session = this.sessionManager.getSession(sessionId);
|
||||||
// 关闭 tab 则无需处理
|
// 无需处理 (直接关闭 tab )
|
||||||
if (session) {
|
if (!session) {
|
||||||
// 提示消息
|
return;
|
||||||
|
}
|
||||||
|
if (session instanceof SshSession) {
|
||||||
|
// ssh 拼接关闭消息
|
||||||
session.write(`\r\n[91m${msg || ''}[0m`);
|
session.write(`\r\n[91m${msg || ''}[0m`);
|
||||||
// 设置状态
|
// 设置状态
|
||||||
session.status = TerminalStatus.CLOSED;
|
session.status = TerminalStatus.CLOSED;
|
||||||
session.connected = false;
|
session.connected = false;
|
||||||
// 设置不可写
|
// 设置不可写
|
||||||
session.setCanWrite(false);
|
session.setCanWrite(false);
|
||||||
|
} else if (session instanceof SftpSession) {
|
||||||
|
// sftp 设置状态
|
||||||
|
session.connected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +107,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
|||||||
|
|
||||||
// 处理输出消息
|
// 处理输出消息
|
||||||
processOutput({ sessionId, body }: OutputPayload): void {
|
processOutput({ sessionId, body }: OutputPayload): void {
|
||||||
const session = this.sessionManager.getSession(sessionId);
|
const session = this.sessionManager.getSession<ISshSession>(sessionId);
|
||||||
session && session.write(body);
|
session && session.write(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import type { ITerminalChannel, ITerminalSession, ITerminalSessionManager, TerminalDomRef, TerminalTabItem } from '../types/terminal.type';
|
import type { ISftpSession, ITerminalChannel, ITerminalSession, ITerminalSessionManager, TerminalTabItem, XtermDomRef } from '../types/terminal.type';
|
||||||
import { sleep } from '@/utils';
|
import { sleep } from '@/utils';
|
||||||
import { InputProtocol } from '../types/terminal.protocol';
|
import { InputProtocol } from '../types/terminal.protocol';
|
||||||
|
import { PanelSessionType } from '../types/terminal.const';
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
import { addEventListen, removeEventListen } from '@/utils/event';
|
import { addEventListen, removeEventListen } from '@/utils/event';
|
||||||
import TerminalSession from './terminal-session';
|
|
||||||
import TerminalChannel from './terminal-channel';
|
import TerminalChannel from './terminal-channel';
|
||||||
|
import SshSession from './ssh-session';
|
||||||
|
import SftpSession from './sftp-session';
|
||||||
|
|
||||||
// 终端会话管理器实现
|
// 终端会话管理器实现
|
||||||
export default class TerminalSessionManager implements ITerminalSessionManager {
|
export default class TerminalSessionManager implements ITerminalSessionManager {
|
||||||
@@ -23,15 +25,15 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
|||||||
this.dispatchResizeFn = useDebounceFn(this.dispatchResize).bind(this);
|
this.dispatchResizeFn = useDebounceFn(this.dispatchResize).bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开终端会话
|
// 打开 ssh 会话
|
||||||
async openSession(tab: TerminalTabItem,
|
async openSsh(tab: TerminalTabItem,
|
||||||
domRef: TerminalDomRef) {
|
domRef: XtermDomRef) {
|
||||||
const sessionId = tab.key;
|
const sessionId = tab.key;
|
||||||
const hostId = tab.hostId as number;
|
const hostId = tab.hostId as number;
|
||||||
// 初始化客户端
|
// 初始化客户端
|
||||||
await this.initChannel();
|
await this.initChannel();
|
||||||
// 新建会话
|
// 新建会话
|
||||||
const session = new TerminalSession(
|
const session = new SshSession(
|
||||||
hostId,
|
hostId,
|
||||||
sessionId,
|
sessionId,
|
||||||
this.channel
|
this.channel
|
||||||
@@ -46,14 +48,37 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
|||||||
this.channel.send(InputProtocol.CHECK, {
|
this.channel.send(InputProtocol.CHECK, {
|
||||||
sessionId,
|
sessionId,
|
||||||
hostId,
|
hostId,
|
||||||
connectType: 'SSH'
|
connectType: PanelSessionType.SSH.type
|
||||||
|
});
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开 sftp 会话
|
||||||
|
async openSftp(tab: TerminalTabItem): Promise<ISftpSession> {
|
||||||
|
const sessionId = tab.key;
|
||||||
|
const hostId = tab.hostId as number;
|
||||||
|
// 初始化客户端
|
||||||
|
await this.initChannel();
|
||||||
|
// 新建会话
|
||||||
|
const session = new SftpSession(
|
||||||
|
hostId,
|
||||||
|
sessionId,
|
||||||
|
this.channel
|
||||||
|
);
|
||||||
|
// 添加会话
|
||||||
|
this.sessions[sessionId] = session;
|
||||||
|
// 发送会话初始化请求
|
||||||
|
this.channel.send(InputProtocol.CHECK, {
|
||||||
|
sessionId,
|
||||||
|
hostId,
|
||||||
|
connectType: PanelSessionType.SFTP.type
|
||||||
});
|
});
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取终端会话
|
// 获取终端会话
|
||||||
getSession(sessionId: string): ITerminalSession {
|
getSession<T>(sessionId: string): T {
|
||||||
return this.sessions[sessionId];
|
return this.sessions[sessionId] as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭终端会话
|
// 关闭终端会话
|
||||||
@@ -64,7 +89,7 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
|||||||
}
|
}
|
||||||
// 关闭连接
|
// 关闭连接
|
||||||
session.disconnect();
|
session.disconnect();
|
||||||
// 关闭 session
|
// 关闭会话
|
||||||
session.close();
|
session.close();
|
||||||
// 移除 session
|
// 移除 session
|
||||||
this.sessions[sessionId] = undefined as unknown as ITerminalSession;
|
this.sessions[sessionId] = undefined as unknown as ITerminalSession;
|
||||||
@@ -94,6 +119,8 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
|||||||
private dispatchResize() {
|
private dispatchResize() {
|
||||||
// 对所有已连接的会话重置大小
|
// 对所有已连接的会话重置大小
|
||||||
Object.values(this.sessions)
|
Object.values(this.sessions)
|
||||||
|
.filter(s => s instanceof SshSession)
|
||||||
|
.map(s => s as SshSession)
|
||||||
.filter(h => h.connected)
|
.filter(h => h.connected)
|
||||||
.forEach(h => h.fit());
|
.forEach(h => h.fit());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,13 +96,6 @@ export interface OutputPayload {
|
|||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 终端 dom 元素引用
|
|
||||||
export interface TerminalDomRef {
|
|
||||||
el: HTMLElement;
|
|
||||||
searchModal: any;
|
|
||||||
editorModal: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 终端 tab 管理器定义
|
// 终端 tab 管理器定义
|
||||||
export interface ITerminalTabManager<T extends TerminalTabItem = TerminalTabItem> {
|
export interface ITerminalTabManager<T extends TerminalTabItem = TerminalTabItem> {
|
||||||
// 当前 tab
|
// 当前 tab
|
||||||
@@ -149,10 +142,12 @@ export interface ITerminalPanelManager<T extends TerminalPanelTabItem = Terminal
|
|||||||
|
|
||||||
// 终端会话管理器定义
|
// 终端会话管理器定义
|
||||||
export interface ITerminalSessionManager {
|
export interface ITerminalSessionManager {
|
||||||
// 打开终端会话
|
// 打开 ssh 会话
|
||||||
openSession: (tab: TerminalTabItem, domRef: TerminalDomRef) => Promise<ITerminalSession>;
|
openSsh: (tab: TerminalTabItem, domRef: XtermDomRef) => Promise<ISshSession>;
|
||||||
|
// 打开 sftp 会话
|
||||||
|
openSftp: (tab: TerminalTabItem) => Promise<ISftpSession>;
|
||||||
// 获取终端会话
|
// 获取终端会话
|
||||||
getSession: (sessionId: string) => ITerminalSession;
|
getSession: <T extends ITerminalSession>(sessionId: string) => T;
|
||||||
// 关闭终端会话
|
// 关闭终端会话
|
||||||
closeSession: (sessionId: string) => void;
|
closeSession: (sessionId: string) => void;
|
||||||
// 重置
|
// 重置
|
||||||
@@ -185,8 +180,15 @@ export interface ITerminalOutputProcessor {
|
|||||||
processOutput: (payload: OutputPayload) => void;
|
processOutput: (payload: OutputPayload) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 终端 dom 元素引用
|
||||||
|
export interface XtermDomRef {
|
||||||
|
el: HTMLElement;
|
||||||
|
searchModal: any;
|
||||||
|
editorModal: any;
|
||||||
|
}
|
||||||
|
|
||||||
// 终端插件
|
// 终端插件
|
||||||
export interface TerminalAddons {
|
export interface XtermAddons {
|
||||||
fit: FitAddon;
|
fit: FitAddon;
|
||||||
webgl: WebglAddon;
|
webgl: WebglAddon;
|
||||||
canvas: CanvasAddon;
|
canvas: CanvasAddon;
|
||||||
@@ -199,21 +201,30 @@ export interface TerminalAddons {
|
|||||||
export interface ITerminalSession {
|
export interface ITerminalSession {
|
||||||
hostId: number;
|
hostId: number;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
// terminal 实例
|
|
||||||
inst: Terminal;
|
|
||||||
// 是否已连接
|
// 是否已连接
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
|
|
||||||
|
// 连接
|
||||||
|
connect: () => void;
|
||||||
|
// 断开连接
|
||||||
|
disconnect: () => void;
|
||||||
|
// 关闭
|
||||||
|
close: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ssh 会话定义
|
||||||
|
export interface ISshSession extends ITerminalSession {
|
||||||
|
// terminal 实例
|
||||||
|
inst: Terminal;
|
||||||
// 是否可写
|
// 是否可写
|
||||||
canWrite: boolean;
|
canWrite: boolean;
|
||||||
// 状态
|
// 状态
|
||||||
status: number;
|
status: number;
|
||||||
// 处理器
|
// 处理器
|
||||||
handler: ITerminalSessionHandler;
|
handler: ISshSessionHandler;
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
init: (domRef: TerminalDomRef) => void;
|
init: (domRef: XtermDomRef) => void;
|
||||||
// 连接
|
|
||||||
connect: () => void;
|
|
||||||
// 设置是否可写
|
// 设置是否可写
|
||||||
setCanWrite: (canWrite: boolean) => void;
|
setCanWrite: (canWrite: boolean) => void;
|
||||||
// 写入数据
|
// 写入数据
|
||||||
@@ -226,14 +237,10 @@ export interface ITerminalSession {
|
|||||||
fit: () => void;
|
fit: () => void;
|
||||||
// 查找
|
// 查找
|
||||||
find: (word: string, next: boolean, options: ISearchOptions) => void;
|
find: (word: string, next: boolean, options: ISearchOptions) => void;
|
||||||
// 断开连接
|
|
||||||
disconnect: () => void;
|
|
||||||
// 关闭
|
|
||||||
close: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 终端会话处理器定义
|
// ssh 会话处理器定义
|
||||||
export interface ITerminalSessionHandler {
|
export interface ISshSessionHandler {
|
||||||
// 检测是否忽略默认行为
|
// 检测是否忽略默认行为
|
||||||
checkPreventDefault: (e: KeyboardEvent) => boolean;
|
checkPreventDefault: (e: KeyboardEvent) => boolean;
|
||||||
// 启用状态
|
// 启用状态
|
||||||
@@ -278,3 +285,8 @@ export interface ITerminalSessionHandler {
|
|||||||
// 检查追加缺失的部分
|
// 检查追加缺失的部分
|
||||||
checkAppendMissing: (value: string) => void;
|
checkAppendMissing: (value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sftp 会话定义
|
||||||
|
export interface ISftpSession extends ITerminalSession {
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user