删除文件.

This commit is contained in:
lijiahangmax
2024-02-20 00:06:10 +08:00
parent a2ff7b0076
commit bde002ee34
21 changed files with 518 additions and 130 deletions

View File

@@ -79,4 +79,6 @@ public interface ErrorMessage {
String PATH_NOT_NORMALIZE = "路径不合法";
String OPERATE_ERROR = "操作失败";
}

View File

@@ -104,8 +104,8 @@ public enum InputTypeEnum {
*/
SFTP_REMOVE("rm",
SftpRemoveHandler.class,
new String[]{"type", "sessionId", "paths"},
SftpRemoveRequest.class),
new String[]{"type", "sessionId", "path"},
SftpBaseRequest.class),
/**
* SFTP 截断文件

View File

@@ -48,12 +48,12 @@ public enum OutputTypeEnum {
/**
* SFTP 创建文件夹
*/
SFTP_MKDIR("md", "${type}|${sessionId}|${result}|${msg}"),
SFTP_MKDIR("mk", "${type}|${sessionId}|${result}|${msg}"),
/**
* SFTP 创建文件
*/
SFTP_TOUCH("to", "${type}|${sessionId}${result}|${msg}"),
SFTP_TOUCH("to", "${type}|${sessionId}|${result}|${msg}"),
/**
* SFTP 移动文件

View File

@@ -2,7 +2,7 @@ package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.ops.framework.common.enums.BooleanBit;
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.model.request.SftpRemoveRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.request.SftpBaseRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.response.SftpBaseResponse;
import com.orion.ops.module.asset.handler.host.terminal.session.ISftpSession;
import lombok.extern.slf4j.Slf4j;
@@ -18,14 +18,14 @@ import org.springframework.web.socket.WebSocketSession;
*/
@Slf4j
@Component
public class SftpRemoveHandler extends AbstractTerminalHandler<SftpRemoveRequest> {
public class SftpRemoveHandler extends AbstractTerminalHandler<SftpBaseRequest> {
@Override
public void handle(WebSocketSession channel, SftpRemoveRequest payload) {
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
// 获取会话
ISftpSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
String[] paths = payload.getPaths().split("\\|");
log.info("SftpRemoveHandler-handle session: {}, paths: {}", payload.getSessionId(), paths);
String[] paths = payload.getPath().split("\\|");
log.info("SftpRemoveHandler-handle session: {}, path: {}", payload.getSessionId(), paths);
Exception ex = null;
// 删除
try {
@@ -40,7 +40,7 @@ public class SftpRemoveHandler extends AbstractTerminalHandler<SftpRemoveRequest
SftpBaseResponse.builder()
.sessionId(payload.getSessionId())
.result(BooleanBit.of(ex == null).getValue())
.msg(ex == null ? null : ex.getMessage())
.msg(this.getErrorMessage(ex))
.build());
}

View File

@@ -1,32 +0,0 @@
package com.orion.ops.module.asset.handler.host.terminal.model.request;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.List;
/**
* sftp 删除文件 实体对象
* <p>
* i|eff00a1|paths
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/2/6 13:31
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "SftpRemoveRequest", description = "sftp 删除文件 实体对象")
public class SftpRemoveRequest extends SftpBaseRequest {
@Schema(description = "paths 多个用|分割")
private String paths;
}

View File

@@ -17,6 +17,7 @@ import org.springframework.web.socket.WebSocketSession;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -68,6 +69,7 @@ public class SftpSession extends TerminalSession implements ISftpSession {
true);
return files.stream()
.map(SftpSession::fileMapping)
.sorted(Comparator.comparing(SftpFileVO::getName))
.collect(Collectors.toList());
}

View File

@@ -5,11 +5,11 @@
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core';
export {}
export {};
declare module '@vue/runtime-core' {
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
RouterLink: typeof import('vue-router')['RouterLink'];
RouterView: typeof import('vue-router')['RouterView'];
}
}

View File

@@ -0,0 +1,9 @@
import { nextTick } from 'vue';
// 设置 ref 自动聚焦
export const setAutoFocus = (el: HTMLElement) => {
// 自动聚焦
nextTick(() => {
el && el.focus();
});
};

View File

@@ -1,5 +1,5 @@
<template>
<a-dropdown class="host-space-context-menu"
<a-dropdown class="terminal-context-menu"
:popup-max-height="false"
trigger="contextMenu"
position="bl"
@@ -12,35 +12,35 @@
<div class="snippet-item-title">
<!-- 名称 -->
<span class="snippet-item-title-name">
{{ item.name }}
</span>
<!-- 操作 -->
<div class="snippet-item-title-actions">
<a-space>
<!-- 粘贴 -->
<a-tag class="pointer usn"
size="small"
:checkable="true"
:checked="true"
@click.stop.prevent="paste">
<template #icon>
<icon-paste />
</template>
粘贴
</a-tag>
<!-- 执行 -->
<a-tag class="pointer usn"
size="small"
:checkable="true"
:checked="true"
@click.stop="exec">
<template #icon>
<icon-thunderbolt />
</template>
执行
</a-tag>
</a-space>
</div>
{{ item.name }}
</span>
<!-- 操作 -->
<div class="snippet-item-title-actions">
<a-space>
<!-- 粘贴 -->
<a-tag class="pointer usn"
size="small"
:checkable="true"
:checked="true"
@click.stop.prevent="paste">
<template #icon>
<icon-paste />
</template>
粘贴
</a-tag>
<!-- 执行 -->
<a-tag class="pointer usn"
size="small"
:checkable="true"
:checked="true"
@click.stop="exec">
<template #icon>
<icon-thunderbolt />
</template>
执行
</a-tag>
</a-space>
</div>
</div>
<!-- 命令 -->
<span class="snippet-item-command"
@@ -53,35 +53,35 @@
<template #content>
<!-- 复制 -->
<a-doption @click="copyCommand">
<div class="host-space-context-menu-icon">
<div class="terminal-context-menu-icon">
<icon-copy />
</div>
<div>复制</div>
</a-doption>
<!-- 粘贴 -->
<a-doption @click="paste">
<div class="host-space-context-menu-icon">
<div class="terminal-context-menu-icon">
<icon-paste />
</div>
<div>粘贴</div>
</a-doption>
<!-- 执行 -->
<a-doption @click="exec">
<div class="host-space-context-menu-icon">
<div class="terminal-context-menu-icon">
<icon-thunderbolt />
</div>
<div>执行</div>
</a-doption>
<!-- 修改 -->
<a-doption @click="openUpdateSnippet(item)">
<div class="host-space-context-menu-icon">
<div class="terminal-context-menu-icon">
<icon-edit />
</div>
<div>修改</div>
</a-doption>
<!-- 删除 -->
<a-doption @click="removeSnippet(item.id)">
<div class="host-space-context-menu-icon">
<div class="terminal-context-menu-icon">
<icon-delete />
</div>
<div>删除</div>
@@ -89,7 +89,7 @@
<!-- 展开 -->
<a-doption v-if="!item.expand"
@click="() => item.expand = true">
<div class="host-space-context-menu-icon">
<div class="terminal-context-menu-icon">
<icon-expand />
</div>
<div>展开</div>
@@ -97,7 +97,7 @@
<!-- 收起 -->
<a-doption v-else
@click="() => item.expand = false">
<div class="host-space-context-menu-icon">
<div class="terminal-context-menu-icon">
<icon-shrink />
</div>
<div>收起</div>

View File

@@ -185,7 +185,7 @@
import { dataColor } from '@/utils';
import { tagColor } from '@/views/asset/host-list/types/const';
import { updateHostAlias } from '@/api/asset/host-extra';
import { openSshModalKey, PanelSessionType } from '../../types/terminal.const';
import { openSshSettingModalKey, PanelSessionType } from '../../types/terminal.const';
import { useTerminalStore } from '@/store';
const props = defineProps<{
@@ -231,7 +231,7 @@
};
// 打开配置
const openSetting = inject(openSshModalKey) as (record: HostQueryResponse) => void;
const openSetting = inject(openSshSettingModalKey) as (record: HostQueryResponse) => void;
// 设置收藏
const setFavorite = async (item: HostQueryResponse) => {

View File

@@ -34,7 +34,7 @@
<script lang="ts" setup>
import { onMounted, provide, ref, watch } from 'vue';
import { NewConnectionType, openSshModalKey } from '../../types/terminal.const';
import { NewConnectionType, openSshSettingModalKey } from '../../types/terminal.const';
import { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
import { HostQueryResponse } from '@/api/asset/host';
import HostGroupView from './host-group-view.vue';
@@ -57,7 +57,7 @@
const sshModal = ref();
// 暴露打开 ssh 配置模态框
provide(openSshModalKey, (record: any) => {
provide(openSshSettingModalKey, (record: any) => {
sshModal.value?.open(record);
});

View File

@@ -17,7 +17,7 @@
<!-- 启用-修改中 -->
<a-input v-if="item.editable && item.enabled"
v-model="item.shortcutKey"
:ref="setEditRef as unknown as VNodeRef"
:ref="setAutoFocus as unknown as VNodeRef"
class="trigger-input"
size="small"
placeholder="请按下快捷键"
@@ -67,7 +67,7 @@
<script lang="ts" setup>
import type { TerminalShortcutKeyEditable } from '@/store/modules/terminal/types';
import type { VNodeRef } from 'vue';
import { nextTick } from 'vue';
import { setAutoFocus } from '@/utils/dom';
defineProps<{
title: string;
@@ -77,14 +77,6 @@
const emits = defineEmits(['setEditable', 'clearEditable', 'updateEnabled']);
// 设置 ref
const setEditRef = (el: HTMLElement) => {
// 自动聚焦
nextTick(() => {
el && el.focus();
});
};
// 修改启用状态
const updateEnabledStatus = (item: TerminalShortcutKeyEditable, enabled: boolean) => {
emits('updateEnabled', item, enabled);

View File

@@ -1,5 +1,30 @@
<template>
<div></div>
<a-modal v-model:visible="visible"
body-class="modal-form"
title-align="start"
:title="touch ? '创建文件' : '创建文件夹'"
:align-center="false"
:mask-closable="false"
:unmount-on-close="true"
:on-before-ok="handlerOk"
@close="handleClose">
<a-form :model="formModel"
ref="formRef"
label-align="right"
layout="horizontal"
:style="{ width: '460px' }"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }">
<!-- 文件路径 -->
<a-form-item field="path"
label="文件路径"
:rules="[{ required: true, message: '请输入文件路径' }]">
<a-input ref="pathRef"
v-model="formModel.path"
placeholder="请输入文件路径" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts">
@@ -9,6 +34,76 @@
</script>
<script lang="ts" setup>
import useVisible from '@/hooks/visible';
import useLoading from '@/hooks/loading';
import { nextTick, ref } from 'vue';
import { useTerminalStore } from '@/store';
import SftpSession from '../../handler/sftp-session';
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
const { sessionManager } = useTerminalStore();
const sessionId = ref();
const touch = ref(false);
const pathRef = ref();
const formRef = ref();
const formModel = ref({
path: ''
});
// 打开新增
const open = (session: string, path: string, isTouch: boolean) => {
sessionId.value = session;
formModel.value.path = path;
touch.value = isTouch;
setVisible(true);
// 自动聚焦
nextTick(() => {
pathRef.value?.focus();
});
};
defineExpose({ open });
// 确定
const handlerOk = async () => {
setLoading(true);
try {
// 验证参数
const error = await formRef.value.validate();
if (error) {
return false;
}
// 获取会话
const session = sessionManager.getSession(sessionId.value);
if (session instanceof SftpSession) {
if (touch.value) {
// 创建文件
session.touch(formModel.value.path);
} else {
// 创建文件夹
session.mkdir(formModel.value.path);
}
}
// 清空
handlerClear();
} catch (e) {
return false;
} finally {
setLoading(false);
}
};
// 关闭
const handleClose = () => {
handlerClear();
};
// 清空
const handlerClear = () => {
setLoading(false);
};
</script>

View File

@@ -33,7 +33,7 @@
<!-- 根目录 -->
<a-breadcrumb-item class="sftp-path-unit"
@click.stop="loadFileList('/')">
<icon-home />
<icon-storage />
</a-breadcrumb-item>
<!-- 子目录 -->
<a-breadcrumb-item class="sftp-path-unit"
@@ -180,8 +180,9 @@
<script lang="ts" setup>
import type { PathAnalysis } from '@/utils/file';
import type { ISftpSession } from '../../types/terminal.type';
import { nextTick, ref, watch } from 'vue';
import { inject, nextTick, ref, watch } from 'vue';
import { getParentPath, getPathAnalysis } from '@/utils/file';
import { openSftpCreateModalKey } from '../../types/terminal.const';
const props = defineProps<{
currentPath: string;
@@ -197,6 +198,8 @@
const pathInput = ref('');
const pathInputRef = ref();
const openSftpCreateModal = inject(openSftpCreateModalKey) as (sessionId: string, path: string, isTouch: boolean) => void;
// 监听路径变化
watch(() => props.currentPath, (path) => {
if (path) {
@@ -246,20 +249,19 @@
// 创建文件
const createFile = () => {
// TODO openModal(true, "props.currentPath")
console.log(props.currentPath);
openSftpCreateModal(props.session?.sessionId as string, props.currentPath + '/', true);
};
// 创建文件夹
const createDir = () => {
// TODO openModal(false, "props.currentPath")
console.log(props.currentPath);
openSftpCreateModal(props.session?.sessionId as string, props.currentPath + '/', false);
};
// 删除选中文件
const deleteSelectFiles = () => {
// TODO confirm
console.log(props.selectedFiles);
if (props.selectedFiles?.length) {
props.session?.remove(props.selectedFiles);
}
};
// 上传文件
@@ -279,8 +281,8 @@
<style lang="less" scoped>
@action-num: 7;
@action-gap: 8px;
@action-width: 26px;
@actions-width: @action-num * (@action-width + @action-gap);
@action-size: 26px;
@actions-width: @action-num * (@action-size + @action-gap);
.sftp-table-header {
width: 100%;
@@ -312,6 +314,7 @@
:deep(.sftp-path-unit) {
cursor: pointer;
font-size: 12px;
&:hover {
color: rgb(var(--arcoblue-6));
@@ -326,5 +329,7 @@
.header-action-icon {
font-size: 16px;
padding: 4px;
width: @action-size;
height: @action-size;
}
</style>

View File

@@ -1,5 +1,6 @@
<template>
<a-table row-key="path"
ref="tableRef"
class="sftp-table"
label-align="left"
:columns="columns"
@@ -9,16 +10,19 @@
:data="list"
:pagination="false"
:bordered="false"
@cell-mouse-enter="setEditable"
@cell-mouse-leave="unsetEditable">
:loading="loading"
@cell-mouse-enter="setOperable"
@cell-mouse-leave="unsetOperable">
<!-- 文件搜索框 -->
<template #nameFilter="{ filterValue, setFilterValue, handleFilterConfirm, handleFilterReset}">
<div class="name-filter">
<a-space direction="vertical">
<!-- 过滤输入框 -->
<a-input size="small"
:ref="setAutoFocus as unknown as VNodeRef"
:model-value="filterValue[0]"
@input="(value) => setFilterValue([value])" />
@input="(value) => setFilterValue([value])"
@pressEnter="handleFilterConfirm" />
<!-- 按钮 -->
<div class="name-filter-footer">
<a-button size="small" @click="handleFilterConfirm">过滤</a-button>
@@ -59,10 +63,24 @@
arrow-class="terminal-tooltip-content"
content="复制路径">
<span class="click-icon-wrapper row-action-icon"
@click="copy(record.path, false)">
@click="copy(record.path, '已复制')">
<icon-copy />
</span>
</a-tooltip>
<!-- 编辑内容 -->
<a-tooltip v-if="canEditable(record.attr)"
position="top"
:mini="true"
:overlay-inverse="true"
:auto-fix-position="false"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
content="编辑内容">
<span class="click-icon-wrapper row-action-icon"
@click="editFile(record)">
<icon-edit />
</span>
</a-tooltip>
<!-- 删除 -->
<a-tooltip position="top"
:mini="true"
@@ -125,12 +143,14 @@
<script lang="ts" setup>
import type { TableData } from '@arco-design/web-vue/es/table/interface';
import type { SftpFile, ISftpSession } from '../../types/terminal.type';
import { ref, computed } from 'vue';
import type { VNodeRef } from 'vue';
import { ref, computed, watch } from 'vue';
import { useRowSelection } from '@/types/table';
import { dateFormat } from '@/utils';
import columns from './types/table.columns';
import useCopy from '@/hooks/copy';
import columns from './types/table.columns';
import { FILE_TYPE } from '../../types/terminal.const';
import { setAutoFocus } from '@/utils/dom';
const props = defineProps<{
session: ISftpSession | undefined;
@@ -144,6 +164,11 @@
const rowSelection = useRowSelection({ width: 40 });
const { copy } = useCopy();
// 切换页面自动清空过滤
watch(() => props.list, () => {
tableRef.value?.clearFilters();
});
const selectedKeys = computed({
get() {
return props.selectedFiles;
@@ -154,15 +179,16 @@
});
const editRowName = ref<string>('');
const tableRef = ref();
// 设置选中状态
const setEditable = (record: TableData) => {
// 设置可操作状态
const setOperable = (record: TableData) => {
editRowName.value = record.name;
record.hover = true;
};
// 设置未选中状态
const unsetEditable = (record: TableData) => {
// 设置不可操作状态
const unsetOperable = (record: TableData) => {
setTimeout(() => {
// 等待后如果还是当前行 但是未被选中则代表已经被失焦
if (record.name === editRowName.value && !record.hover) {
@@ -172,21 +198,30 @@
record.hover = false;
};
// 是否可编辑
const canEditable = (attr: string) => {
const typeValue = formatFileType(attr).value;
// 非文件夹和链接文件可以编辑
return FILE_TYPE.DIRECTORY.value !== typeValue
&& FILE_TYPE.LINK_FILE.value !== typeValue;
};
// 点击文件名称
const clickFilename = (record: TableData) => {
if (FILE_TYPE.DIRECTORY.value === formatFileType(record.attr).value) {
// 进入文件夹
emits('loadFile', record.path);
} else {
// 点击文件
// TODO VIEW
}
};
// 编辑文件
const editFile = (record: TableData) => {
// TODO
};
// 删除文件
const deleteFile = (path: string) => {
// TODO confirm
console.log(path);
props.session?.remove([path]);
};
// 下载文件

View File

@@ -7,7 +7,8 @@
<!-- 左侧面板表格 -->
<template #first>
<a-spin class="sftp-table-container"
:loading="tableLoading">
:loading="tableLoading"
:hide-icon="true">
<!-- 表头 -->
<sftp-table-header class="sftp-table-header"
:current-path="currentPath"
@@ -27,6 +28,8 @@
<div>editor</div>
</template>
</a-split>
<!-- 创建文件模态框 -->
<sftp-create-modal ref="createModal" />
</div>
</template>
@@ -38,12 +41,14 @@
<script lang="ts" setup>
import type { ISftpSession, SftpFile, TerminalTabItem } from '../../types/terminal.type';
import { onMounted, onUnmounted, ref } from 'vue';
import { onMounted, onUnmounted, provide, ref } from 'vue';
import { useTerminalStore } from '@/store';
import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading';
import { openSftpCreateModalKey } from '../../types/terminal.const';
import SftpTable from './sftp-table.vue';
import SftpTableHeader from './sftp-table-header.vue';
import SftpCreateModal from './sftp-create-modal.vue';
const props = defineProps<{
tab: TerminalTabItem
@@ -53,12 +58,18 @@
const { loading: tableLoading, setLoading: setTableLoading } = useLoading(true);
const session = ref<ISftpSession>();
const createModal = ref();
const currentPath = ref<string>('');
const fileList = ref<Array<SftpFile>>([]);
const selectFiles = ref<Array<string>>([]);
const splitSize = ref(1);
const editView = ref(true);
// 暴露打开创建方法
provide(openSftpCreateModalKey, (sessionId: string, path: string, isTouch: boolean) => {
createModal.value?.open(sessionId, path, isTouch);
});
// 连接成功回调
const connectCallback = () => {
loadFiles(undefined);
@@ -70,16 +81,44 @@
session.value?.list(path);
};
// 检查结果
const checkResult = (result: string, msg: string): boolean => {
const success = !!Number.parseInt(result);
if (!success) {
Message.error(msg);
}
return success;
};
// 接收列表回调
const resolveList = (result: string, path: string, list: Array<SftpFile>) => {
const success = !!Number.parseInt(result);
setTableLoading(false);
if (!success) {
Message.error('查询失败');
if (!checkResult(result, '查询失败')) {
return;
}
currentPath.value = path;
fileList.value = list;
selectFiles.value = [];
};
// 接收文件响应
const resolveFileAction = (result: string, msg: string) => {
setTableLoading(false);
// 检查结果
if (!checkResult(result, msg)) {
return;
}
Message.success('操作成功');
// 刷新列表
loadFiles(currentPath.value);
};
// 接收获取文件内容响应
const resolveSftpGetContent = (path: string, result: string, content: string) => {
};
// 接收修改文件内容响应
const resolveSftpSetContent = (result: string, msg: string) => {
};
// 初始化会话
@@ -87,7 +126,14 @@
// 创建终端处理器
session.value = await sessionManager.openSftp(props.tab, {
connectCallback,
resolveList
resolveList,
resolveSftpMkdir: resolveFileAction,
resolveSftpTouch: resolveFileAction,
resolveSftpMove: resolveFileAction,
resolveSftpRemove: resolveFileAction,
resolveSftpChmod: resolveFileAction,
resolveSftpGetContent,
resolveSftpSetContent,
});
});

View File

@@ -34,7 +34,7 @@ const columns = [
sortable: {
sortDirections: ['ascend', 'descend'],
},
width: 218,
width: 234,
cellClass: 'action-cell',
},
] as TableColumnData[];

View File

@@ -1,5 +1,6 @@
import type { ISftpSession, ISftpSessionResolver, ITerminalChannel } from '../types/terminal.type';
import { InputProtocol } from '../types/terminal.protocol';
import { Modal } from '@arco-design/web-vue';
// sftp 会话实现
export default class SftpSession implements ISftpSession {
@@ -53,6 +54,71 @@ export default class SftpSession implements ISftpSession {
});
};
// 创建文件夹
mkdir(path: string) {
this.channel.send(InputProtocol.SFTP_MKDIR, {
sessionId: this.sessionId,
path
});
};
// 创建文件
touch(path: string) {
this.channel.send(InputProtocol.SFTP_TOUCH, {
sessionId: this.sessionId,
path
});
};
// 移动文件
move(path: string, target: string) {
this.channel.send(InputProtocol.SFTP_MOVE, {
sessionId: this.sessionId,
path,
target
});
};
// 删除文件
remove(path: string[]) {
Modal.confirm({
title: '删除确认',
content: `确定要删除 ${path} 吗? 确定后将立即删除且无法恢复!`,
onOk: () => {
this.channel.send(InputProtocol.SFTP_REMOVE, {
sessionId: this.sessionId,
path: path.join('|')
});
}
});
};
// 修改权限
chmod(path: string, mod: number) {
this.channel.send(InputProtocol.SFTP_CHMOD, {
sessionId: this.sessionId,
path,
mod
});
};
// 获取内容
getContent(path: string) {
this.channel.send(InputProtocol.SFTP_GET_CONTENT, {
sessionId: this.sessionId,
path
});
};
// 修改内容
setContent(path: string, content: string) {
this.channel.send(InputProtocol.SFTP_SET_CONTENT, {
sessionId: this.sessionId,
path,
content
});
};
// 断开连接
disconnect(): void {
// 发送关闭消息

View File

@@ -119,4 +119,53 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
session && session.resolver.resolveList(result, path, JSON.parse(body));
}
// 处理 SFTP 创建文件夹
processSftpMkdir({ sessionId, result, msg }: OutputPayload): void {
// 获取会话
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
session && session.resolver.resolveSftpMkdir(result, msg);
}
// 处理 SFTP 创建文件
processSftpTouch({ sessionId, result, msg }: OutputPayload): void {
// 获取会话
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
session && session.resolver.resolveSftpTouch(result, msg);
}
// 处理 SFTP 移动文件
processSftpMove({ sessionId, result, msg }: OutputPayload): void {
// 获取会话
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
session && session.resolver.resolveSftpMove(result, msg);
}
// 处理 SFTP 删除文件
processSftpRemove({ sessionId, result, msg }: OutputPayload): void {
// 获取会话
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
session && session.resolver.resolveSftpRemove(result, msg);
}
// 处理 SFTP 修改文件权限
processSftpChmod({ sessionId, result, msg }: OutputPayload): void {
// 获取会话
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
session && session.resolver.resolveSftpChmod(result, msg);
}
// 处理 SFTP 获取文件内容
processSftpGetContent({ sessionId, path, result, content }: OutputPayload): void {
// 获取会话
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
session && session.resolver.resolveSftpGetContent(path, result, content);
}
// 处理 SFTP 修改文件内容
processSftpSetContent({ sessionId, result, msg }: OutputPayload) {
// 获取会话
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
session && session.resolver.resolveSftpSetContent(result, msg);
}
}

View File

@@ -35,6 +35,41 @@ export const InputProtocol = {
type: 'ls',
template: ['type', 'sessionId', 'showHiddenFile', 'path']
},
// SFTP 创建文件夹
SFTP_MKDIR: {
type: 'mk',
template: ['type', 'sessionId', 'path']
},
// SFTP 创建文件
SFTP_TOUCH: {
type: 'to',
template: ['type', 'sessionId', 'path']
},
// SFTP 移动文件
SFTP_MOVE: {
type: 'mv',
template: ['type', 'sessionId', 'path', 'target']
},
// SFTP 删除文件
SFTP_REMOVE: {
type: 'rm',
template: ['type', 'sessionId', 'path']
},
// SFTP 修改文件权限
SFTP_CHMOD: {
type: 'cm',
template: ['type', 'sessionId', 'path', 'mod']
},
// SFTP 获取内容
SFTP_GET_CONTENT: {
type: 'gc',
template: ['type', 'sessionId', 'path']
},
// SFTP 修改内容
SFTP_SET_CONTENT: {
type: 'sc',
template: ['type', 'sessionId', 'path', 'content']
},
};
// 输出协议
@@ -72,7 +107,49 @@ export const OutputProtocol = {
// SFTP 文件列表
SFTP_LIST: {
type: 'ls',
template: ['type', 'sessionId', 'result', 'path', 'body'],
template: ['type', 'sessionId', 'path', 'result', 'body'],
processMethod: 'processSftpList'
},
// SFTP 创建文件夹
SFTP_MKDIR: {
type: 'mk',
template: ['type', 'sessionId', 'result', 'msg'],
processMethod: 'processSftpMkdir'
},
// SFTP 创建文件
SFTP_TOUCH: {
type: 'to',
template: ['type', 'sessionId', 'result', 'msg'],
processMethod: 'processSftpTouch'
},
// SFTP 移动文件
SFTP_MOVE: {
type: 'mv',
template: ['type', 'sessionId', 'result', 'msg'],
processMethod: 'processSftpMove'
},
// SFTP 删除文件
SFTP_REMOVE: {
type: 'rm',
template: ['type', 'sessionId', 'result', 'msg'],
processMethod: 'processSftpRemove'
},
// SFTP 修改文件权限
SFTP_CHMOD: {
type: 'cm',
template: ['type', 'sessionId', 'result', 'msg'],
processMethod: 'processSftpChmod'
},
// SFTP 获取文件内容
SFTP_GET_CONTENT: {
type: 'gc',
template: ['type', 'sessionId', 'path', 'result', 'content'],
processMethod: 'processSftpGetContent'
},
// SFTP 修改文件内容
SFTP_SET_CONTENT: {
type: 'sc',
template: ['type', 'sessionId', 'result', 'msg'],
processMethod: 'processSftpSetContent'
},
};

View File

@@ -180,6 +180,20 @@ export interface ITerminalOutputProcessor {
processSshOutput: (payload: OutputPayload) => void;
// 处理 SFTP 文件列表
processSftpList: (payload: OutputPayload) => void;
// 处理 SFTP 创建文件夹
processSftpMkdir: (payload: OutputPayload) => void;
// 处理 SFTP 创建文件
processSftpTouch: (payload: OutputPayload) => void;
// 处理 SFTP 移动文件
processSftpMove: (payload: OutputPayload) => void;
// 处理 SFTP 删除文件
processSftpRemove: (payload: OutputPayload) => void;
// 处理 SFTP 修改文件权限
processSftpChmod: (payload: OutputPayload) => void;
// 处理 SFTP 获取文件内容
processSftpGetContent: (payload: OutputPayload) => void;
// 处理 SFTP 修改文件内容
processSftpSetContent: (payload: OutputPayload) => void;
}
// xterm dom 元素引用
@@ -299,6 +313,20 @@ export interface ISftpSession extends ITerminalSession {
setShowHiddenFile: (show: boolean) => void;
// 查询文件列表
list: (path: string | undefined) => void;
// 创建文件夹
mkdir: (path: string) => void;
// 创建文件
touch: (path: string) => void;
// 移动文件
move: (path: string, target: string) => void;
// 删除文件
remove: (path: string[]) => void;
// 修改权限
chmod: (path: string, mod: number) => void;
// 获取内容
getContent: (path: string) => void;
// 修改内容
setContent: (path: string, content: string) => void;
}
// sftp 会话接收器定义
@@ -307,6 +335,20 @@ export interface ISftpSessionResolver {
connectCallback: () => void;
// 接受文件列表响应
resolveList: (result: string, path: string, list: Array<SftpFile>) => void;
// 接收创建文件夹响应
resolveSftpMkdir: (result: string, msg: string) => void;
// 接收创建文件响应
resolveSftpTouch: (result: string, msg: string) => void;
// 接收移动文件响应
resolveSftpMove: (result: string, msg: string) => void;
// 接收删除文件响应
resolveSftpRemove: (result: string, msg: string) => void;
// 接收修改文件权限响应
resolveSftpChmod: (result: string, msg: string) => void;
// 接收获取文件内容响应
resolveSftpGetContent: (path: string, result: string, content: string) => void;
// 接收修改文件内容响应
resolveSftpSetContent: (result: string, msg: string) => void;
}
// sftp 文件