🚧 批量上传.

This commit is contained in:
lijiahangmax
2024-05-09 00:06:08 +08:00
parent 42f1c6f0cb
commit af00e71651
36 changed files with 333 additions and 94 deletions

View File

@@ -1,4 +1,5 @@
import axios from 'axios';
import { createAppWebSocket } from '@/utils/http';
// 终端主题
export interface TerminalTheme {
@@ -49,3 +50,17 @@ export function getTerminalThemes() {
export function getTerminalAccessToken() {
return axios.get<string>('/asset/host-terminal/access');
}
/**
* 打开主机终端 websocket
*/
export const openHostTerminalChannel = (accessToken: string) => {
return createAppWebSocket(`/host/terminal/${accessToken}`);
};
/**
* 打开主机传输 websocket
*/
export const openHostTransferChannel = (accessToken: string) => {
return createAppWebSocket(`/host/transfer/${accessToken}`);
};

View File

@@ -1,5 +1,6 @@
import type { Pagination } from '@/types/global';
import type { TableData } from '@arco-design/web-vue/es/table/interface';
import { createAppWebSocket } from '@/utils/http';
/**
* 执行日志查询请求
@@ -82,3 +83,10 @@ export interface ExecLogInterruptRequest {
logId?: number;
hostLogId?: number;
}
/**
* 打开执行日志 websocket
*/
export const openExecLogChannel = (token: string) => {
return createAppWebSocket(`/exec/log/${token}`);
};

View File

@@ -0,0 +1,146 @@
import type { DataGrid, Pagination } from '@/types/global';
import type { TableData } from '@arco-design/web-vue/es/table/interface';
import axios from 'axios';
import qs from 'query-string';
/**
* 上传任务创建请求
*/
export interface UploadTaskCreateRequest {
userId?: number;
username?: string;
remotePath?: string;
description?: string;
status?: string;
extraInfo?: string;
startTime?: string;
endTime?: string;
}
/**
* 上传任务创建响应
*/
export interface UploadTaskCreateResponse {
id: number;
token: string;
}
/**
* 上传任务请求
*/
export interface UploadTaskRequest {
id?: number;
}
/**
* 上传任务查询请求
*/
export interface UploadTaskQueryRequest extends Pagination {
id?: number;
userId?: number;
remotePath?: string;
description?: string;
status?: string;
createTimeRange?: string[];
}
/**
* 上传任务查询响应
*/
export interface UploadTaskQueryResponse extends TableData {
id: number;
userId: number;
username: string;
remotePath: string;
description: string;
status: string;
extraInfo: string;
startTime: number;
endTime: number;
createTime: number;
files: Array<UploadTaskFileQueryResponse>;
}
/**
* 上传任务文件查询响应
*/
export interface UploadTaskFileQueryResponse {
id: number;
taskId: number;
hostId: number;
fileId: string;
filePath: string;
fileSize: number;
status: string;
startTime: number;
endTime: number;
current: number;
}
/**
* 创建上传任务
*/
export function createUploadTask(request: UploadTaskCreateRequest) {
return axios.post<UploadTaskCreateResponse>('/asset/upload-task/create', request);
}
/**
* 创建上传任务
*/
export function startUploadTask(request: UploadTaskRequest) {
return axios.post('/asset/upload-task/start', request);
}
/**
* 创建上传任务
*/
export function cancelUploadTask(request: UploadTaskRequest) {
return axios.post('/asset/upload-task/cancel', request);
}
/**
* 查询上传任务
*/
export function getUploadTask(id: number) {
return axios.get<UploadTaskQueryResponse>('/asset/upload-task/get', { params: { id } });
}
/**
* 分页查询上传任务
*/
export function getUploadTaskPage(request: UploadTaskQueryRequest) {
return axios.post<DataGrid<UploadTaskQueryResponse>>('/asset/upload-task/query', request);
}
/**
* 删除上传任务
*/
export function deleteUploadTask(id: number) {
return axios.delete('/asset/upload-task/delete', { params: { id } });
}
/**
* 批量删除上传任务
*/
export function batchDeleteUploadTask(idList: Array<number>) {
return axios.delete('/asset/upload-task/batch-delete', {
params: { idList },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });
}
});
}
/**
* 查询主机连接日志数量
*/
export function getUploadTaskCount(request: UploadTaskQueryRequest) {
return axios.post<number>('/asset/upload-task/query-count', request);
}
/**
* 清空主机连接日志
*/
export function clearUploadTask(request: UploadTaskQueryRequest) {
return axios.post<number>('/asset/upload-task/clear', request);
}

View File

@@ -6,7 +6,7 @@ import type {
ExecLogStatusResponse,
ExecLogTailRequest,
ExecLogInterruptRequest
} from './exec-log';
} from '../exec/exec-log';
import axios from 'axios';
import qs from 'query-string';

View File

@@ -0,0 +1,8 @@
import { createAppWebSocket } from '@/utils/http';
/**
* 打开文件上传 websocket
*/
export const openFileUploadChannel = (uploadToken: string) => {
return createAppWebSocket(`"/file/upload/${uploadToken}`);
};

View File

@@ -11,8 +11,8 @@
<a-link target="_blank" :href="`https://github.com/lijiahangmax/orion-ops-pro/releases/tag/v${version}`">v{{ version }} 开源版</a-link>
</a-space>
<span class="copyright">
Copyright<icon-copyright /> 2024 Li Jiahang All rights reserved.
</span>
Copyright<icon-copyright /> {{ new Date().getFullYear() }} Li Jiahang All rights reserved.
</span>
</a-space>
</a-layout-footer>
</template>

View File

@@ -36,7 +36,7 @@
import useLoading from '@/hooks/loading';
import { nextTick, ref } from 'vue';
import { getExecCommandLog } from '@/api/exec/exec-command-log';
import { getExecJobLog } from '@/api/exec/exec-job-log';
import { getExecJobLog } from '@/api/job/exec-job-log';
import ExecLogPanel from '../panel/index.vue';
const props = defineProps<{

View File

@@ -29,7 +29,7 @@
import type { ExecType } from '../const';
import { onUnmounted, ref, nextTick, onMounted } from 'vue';
import { getExecCommandLogStatus } from '@/api/exec/exec-command-log';
import { getExecJobLogStatus } from '@/api/exec/exec-job-log';
import { getExecJobLogStatus } from '@/api/job/exec-job-log';
import { dictKeys, execHostStatus, execStatus } from '../const';
import { useDictStore } from '@/store';
import ExecHost from './exec-host.vue';

View File

@@ -2,11 +2,10 @@ import type { ILogAppender, LogAddons, LogAppenderConf, LogDomRef } from './appe
import { LogAppenderOptions } from './appender-const';
import type { ExecType } from '../const';
import type { ExecLogTailRequest } from '@/api/exec/exec-log';
import { openExecLogChannel } from '@/api/exec/exec-log';
import { getExecCommandLogTailToken } from '@/api/exec/exec-command-log';
import { getExecJobLogTailToken } from '@/api/exec/exec-job-log';
import { webSocketBaseUrl } from '@/utils/env';
import { getExecJobLogTailToken } from '@/api/job/exec-job-log';
import { Message } from '@arco-design/web-vue';
import { createWebSocket } from '@/utils';
import { useDebounceFn } from '@vueuse/core';
import { addEventListen, removeEventListen } from '@/utils/event';
import { copy as copyText } from '@/hooks/copy';
@@ -158,7 +157,7 @@ export default class LogAppender implements ILogAppender {
const { data } = await tokenMaker;
// 打开会话
try {
this.client = await createWebSocket(`${webSocketBaseUrl}/exec/log/${data}`);
this.client = await openExecLogChannel(data);
} catch (e) {
Message.error('连接失败');
console.error('log error', e);

View File

@@ -174,7 +174,7 @@
import { formatDuration } from '@/utils';
import { useDictStore } from '@/store';
import { downloadExecCommandLogFile } from '@/api/exec/exec-command-log';
import { downloadExecJobLogFile } from '@/api/exec/exec-job-log';
import { downloadExecJobLogFile } from '@/api/job/exec-job-log';
import { downloadFile } from '@/utils/file';
import XtermSearchModal from '@/components/xtrem/search-modal/index.vue';
import 'xterm/css/xterm.css';

View File

@@ -17,6 +17,16 @@ const EXEC: AppRouteRecordRaw[] = [
path: '/exec-log',
component: () => import('@/views/exec/exec-command-log/index.vue'),
},
{
name: 'batchUpload',
path: '/batch-upload',
component: () => import('@/views/exec/batch-upload/index.vue'),
},
{
name: 'uploadTaskLog',
path: '/upload-log',
component: () => import('@/views/exec/upload-task-log/index.vue'),
},
{
name: 'execTemplate',
path: '/exec-template',

View File

@@ -13,7 +13,7 @@ import { getHostGroupTree } from '@/api/asset/host-group';
import { getMenuList } from '@/api/system/menu';
import { getCurrentAuthorizedHostIdentity, getCurrentAuthorizedHostKey } from '@/api/asset/asset-authorized-data';
import { getCommandSnippetGroupList } from '@/api/asset/command-snippet-group';
import { getExecJobList } from '@/api/exec/exec-job';
import { getExecJobList } from '@/api/job/exec-job';
import { getPathBookmarkGroupList } from '@/api/asset/path-bookmark-group';
export type CacheType = 'users' | 'menus' | 'roles'

View File

@@ -2,7 +2,7 @@
export const AdminRoleCode = 'admin';
// 表格视图分页数配置
export const TablePageSizeOptions = [10, 20, 30, 50, 100];
export const TablePageSizeOptions = [10, 15, 20, 30, 50, 100];
// 卡片视图分页数配置
export const CardPageSizeOptions = [12, 18, 36, 48, 96];

View File

@@ -0,0 +1,25 @@
import { webSocketBaseUrl } from '@/utils/env';
/**
* 创建应用 websocket
*/
export const createAppWebSocket = (url: string): Promise<WebSocket> => {
return createWebSocket(webSocketBaseUrl + url);
};
/**
* 创建 websocket
*/
export const createWebSocket = (url: string): Promise<WebSocket> => {
return new Promise<WebSocket>((resolve, reject) => {
const socket = new WebSocket(url);
socket.onopen = () => {
resolve(socket);
};
socket.onerror = e => {
reject(e);
};
});
};

View File

@@ -197,23 +197,6 @@ export const objectTruthKeyCount = (obj: any, ignore: string[] = []) => {
}, 0);
};
/**
* 创建 websocket
*/
export const createWebSocket = async (url: string) => {
return new Promise<WebSocket>((resolve, reject) => {
const socket = new WebSocket(url);
socket.onopen = () => {
resolve(socket);
};
socket.onerror = e => {
reject(e);
};
});
};
/**
* 休眠
*/

View File

@@ -0,0 +1,17 @@
<template>
<div>批量上传</div>
</template>
<script lang="ts">
export default {
name: 'index'
};
</script>
<script lang="ts" setup>
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,17 @@
<template>
<div>upload-task-log</div>
</template>
<script lang="ts">
export default {
name: 'index'
};
</script>
<script lang="ts" setup>
</script>
<style lang="less" scoped>
</style>

View File

@@ -2,9 +2,8 @@ import type { ISftpTransferManager, ISftpTransferUploader, SftpTransferItem } fr
import { ISftpTransferDownloader, SftpFile, TransferOperatorResponse } from '../types/terminal.type';
import { TransferReceiverType, TransferStatus, TransferType } from '../types/terminal.const';
import { Message } from '@arco-design/web-vue';
import { getTerminalAccessToken } from '@/api/asset/host-terminal';
import { createWebSocket, nextId } from '@/utils';
import { webSocketBaseUrl } from '@/utils/env';
import { getTerminalAccessToken, openHostTransferChannel } from '@/api/asset/host-terminal';
import { nextId } from '@/utils';
import SftpTransferUploader from './sftp-transfer-uploader';
import SftpTransferDownloader from './sftp-transfer-downloader';
@@ -113,7 +112,7 @@ export default class SftpTransferManager implements ISftpTransferManager {
const { data: accessToken } = await getTerminalAccessToken();
// 打开会话
try {
this.client = await createWebSocket(`${webSocketBaseUrl}/host/transfer/${accessToken}`);
this.client = await openHostTransferChannel(accessToken);
} catch (e) {
// 打开失败将传输列表置为失效
Message.error('会话打开失败');

View File

@@ -1,9 +1,7 @@
import type { InputPayload, ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager, OutputPayload, Protocol, } from '../types/terminal.type';
import { OutputProtocol } from '../types/terminal.protocol';
import { getTerminalAccessToken } from '@/api/asset/host-terminal';
import { getTerminalAccessToken, openHostTerminalChannel } from '@/api/asset/host-terminal';
import { Message } from '@arco-design/web-vue';
import { createWebSocket } from '@/utils';
import { webSocketBaseUrl } from '@/utils/env';
import TerminalOutputProcessor from './terminal-output-processor';
// 终端通信处理器 实现
@@ -23,7 +21,7 @@ export default class TerminalChannel implements ITerminalChannel {
const { data: accessToken } = await getTerminalAccessToken();
// 打开会话
try {
this.client = await createWebSocket(`${webSocketBaseUrl}/host/terminal/${accessToken}`);
this.client = await openHostTerminalChannel(accessToken);
} catch (e) {
Message.error('无法连接至服务器');
console.error('terminal error', e);

View File

@@ -17,7 +17,7 @@
<script lang="ts" setup>
import { onMounted, ref, nextTick } from 'vue';
import { useRoute } from 'vue-router';
import { getExecJobLog } from '@/api/exec/exec-job-log';
import { getExecJobLog } from '@/api/job/exec-job-log';
import ExecLogPanel from '@/components/exec/log/panel/index.vue';
const route = useRoute();

View File

@@ -102,15 +102,14 @@
<script lang="ts" setup>
import type { ExecLogQueryResponse, ExecHostLogQueryResponse } from '@/api/exec/exec-log';
import { deleteExecJobHostLog } from '@/api/exec/exec-job-log';
import { interruptHostExecJob } from '@/api/exec/exec-job-log';
import { deleteExecJobHostLog, interruptHostExecJob } from '@/api/job/exec-job-log';
import { execHostStatusKey, execHostStatus } from '@/components/exec/log/const';
import { useDictStore } from '@/store';
import useLoading from '@/hooks/loading';
import columns from '@/views/exec/exec-command-log/types/host-table.columns';
import { useExpandable } from '@/types/table';
import { dateFormat, formatDuration } from '@/utils';
import { downloadExecJobLogFile } from '@/api/exec/exec-job-log';
import { downloadExecJobLogFile } from '@/api/job/exec-job-log';
import { copy } from '@/hooks/copy';
import { downloadFile } from '@/utils/file';
import { Message } from '@arco-design/web-vue';

View File

@@ -60,7 +60,7 @@
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import { execStatusKey } from '@/components/exec/log/const';
import { getExecJobLogCount, clearExecJobLog } from '@/api/exec/exec-job-log';
import { getExecJobLogCount, clearExecJobLog } from '@/api/job/exec-job-log';
import { Message, Modal } from '@arco-design/web-vue';
import { useDictStore } from '@/store';
import ExecJobSelector from '@/components/exec/job/selector/index.vue';

View File

@@ -198,7 +198,7 @@
getExecJobHostLogList,
getExecJobLogPage,
getExecJobLogStatus
} from '@/api/exec/exec-job-log';
} from '@/api/job/exec-job-log';
import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading';
import columns from '../types/table.columns';
@@ -206,7 +206,7 @@
import { useExpandable, usePagination, useRowSelection } from '@/types/table';
import { useDictStore } from '@/store';
import { dateFormat, formatDuration } from '@/utils';
import { interruptExecJob } from '@/api/exec/exec-job-log';
import { interruptExecJob } from '@/api/job/exec-job-log';
import ExecJobHostLogTable from './exec-job-host-log-table.vue';
const emits = defineEmits(['viewCommand', 'viewParams', 'viewLog', 'openClear']);

View File

@@ -86,14 +86,14 @@
</script>
<script lang="ts" setup>
import type { ExecJobQueryResponse } from '@/api/exec/exec-job';
import type { ExecJobQueryResponse } from '@/api/job/exec-job';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import { useDictStore } from '@/store';
import { dateFormat } from '@/utils';
import { copy } from '@/hooks/copy';
import { getExecJob } from '@/api/exec/exec-job';
import { getExecJob } from '@/api/job/exec-job';
import { EnabledStatus } from '@/types/const';
import { execJobStatusKey } from '../types/const';

View File

@@ -128,14 +128,14 @@
</script>
<script lang="ts" setup>
import type { ExecJobUpdateRequest } from '@/api/exec/exec-job';
import type { ExecJobUpdateRequest } from '@/api/job/exec-job';
import type { ExecTemplateQueryResponse } from '@/api/exec/exec-template';
import { onUnmounted, ref } from 'vue';
import useLoading from '@/hooks/loading';
import useVisible from '@/hooks/visible';
import formRules from '../types/form.rules';
import { jobBuiltinsParams } from '../types/const';
import { createExecJob, getExecJob, updateExecJob } from '@/api/exec/exec-job';
import { createExecJob, getExecJob, updateExecJob } from '@/api/job/exec-job';
import { getExecTemplateWithAuthorized } from '@/api/exec/exec-template';
import { Message } from '@arco-design/web-vue';
import { EnabledStatus } from '@/types/const';

View File

@@ -172,9 +172,9 @@
</script>
<script lang="ts" setup>
import type { ExecJobQueryRequest, ExecJobQueryResponse } from '@/api/exec/exec-job';
import type { ExecJobQueryRequest, ExecJobQueryResponse } from '@/api/job/exec-job';
import { reactive, ref, onMounted } from 'vue';
import { deleteExecJob, getExecJobPage, triggerExecJob, updateExecJobStatus } from '@/api/exec/exec-job';
import { deleteExecJob, getExecJobPage, triggerExecJob, updateExecJobStatus } from '@/api/job/exec-job';
import { Message } from '@arco-design/web-vue';
import usePermission from '@/hooks/permission';
import useLoading from '@/hooks/loading';

View File

@@ -53,15 +53,6 @@
placeholder="菜单权限 infra:system-menu:query"
allow-clear />
</a-form-item>
<!-- 外链地址 -->
<a-form-item v-if="formModel.type !== MenuType.FUNCTION"
field="path"
label="外链地址"
tooltip="输入组件名称后则不会生效">
<a-input v-model="formModel.path"
placeholder="外链地址与组件名称二选一"
allow-clear />
</a-form-item>
<!-- 组件名称 -->
<a-form-item v-if="formModel.type !== MenuType.FUNCTION"
field="component"
@@ -70,6 +61,15 @@
placeholder="路由组件名称"
allow-clear />
</a-form-item>
<!-- 外链地址 -->
<a-form-item v-if="formModel.type !== MenuType.FUNCTION"
field="path"
label="外链地址"
tooltip="输入组件名称后则不会生效">
<a-input v-model="formModel.path"
placeholder="组件名称与外链地址二选一"
allow-clear />
</a-form-item>
<!-- 菜单排序 -->
<a-form-item field="sort" label="菜单排序">
<a-input-number v-model="formModel.sort"
@@ -212,6 +212,7 @@
if (error) {
return false;
}
// 验证父菜单
if (formModel.value.parentId === 0
&& (formModel.value.type === MenuType.SUB_MENU || formModel.value.type === MenuType.FUNCTION)) {
formRef.value.setFields({
@@ -222,6 +223,19 @@
});
return false;
}
// 验证组件名称
if ((formModel.value.type === MenuType.PARENT_MENU || formModel.value.type === MenuType.SUB_MENU)
&& !formModel.value.component
&& !formModel.value.path) {
formRef.value.setFields({
component: {
status: 'error',
message: '组件名称与外链地址二选一'
}
});
return false;
}
if (isAddHandle.value) {
// 新增
await createMenu(formModel.value);