Skip to content

Commit

Permalink
2.8.0 workflow触发 实时上传速度
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaobaidadada committed Feb 13, 2025
1 parent 2a77076 commit 9da58a1
Show file tree
Hide file tree
Showing 14 changed files with 205 additions and 69 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "filecat",
"version": "2.7.7",
"version": "2.8.0",
"description": "filecat 文件管理器",
"author": "xiaobaidadada",
"scripts": {
Expand Down
6 changes: 5 additions & 1 deletion src/common/req/http_controller_router.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

export enum Http_controller_router {
setting_sys_option_status_save = "sys_option/status/save"
setting_sys_option_status_save = "sys_option/status/save",
setting_customer_router = "customer_router",
setting_customer_router_save = "customer_router/save",
setting_customer_workflow_router = "customer_router/workflow",
setting_customer_workflow_router_save = "customer_router/workflow/save",
}
1 change: 1 addition & 0 deletions src/common/req/user.req.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export enum UserAuth {
rtsp_proxy, // rtsp 功能
workflow_exe, // workflow 执行 功能
workflow_exe_user, // 能被设置执行的 workflow 用户
workflow_api, // workflow 自定义触发api
}

export class UserData extends UserLogin {
Expand Down
1 change: 1 addition & 0 deletions src/main/domain/data/data_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export enum data_common_key {
navindex_net_key_list = "navindex_net_key_list",
navindex_wol_key = "navindex_wol_key",
customer_router_key = "customer_router_key",
customer_workflow_router_key = "customer_workflow_router_key",
customer_api_router_key = "customer_api_router_key",
token_setting = "token_setting",
files_pre_mulu_key = "files_pre_mulu_key", // 废弃 文件目录
Expand Down
54 changes: 29 additions & 25 deletions src/main/domain/file/workflow/workflow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,31 +633,7 @@ export class WorkflowService {
const file_path = path.join(root_path, decodeURIComponent(pojo.path));
const user_info = userService.get_user_info_by_token(token);
if (pojo.run_type === WorkRunType.start) {
if (work_exec_map.get(file_path))
throw "Workflow exec task already exists";
const worker = new work_children(file_path);
work_exec_map.set(file_path, worker);
try {
await worker.init({
filecat_user_id:userService.get_user_id(user_info.username),
filecat_user_name:user_info.username,
filecat_user_note:user_info.note
});
} catch (e){
work_exec_map.delete(file_path);
this.online_change_push();
throw e;
}
this.online_change_push(); // 在init后不然没有 filename
worker.run_jobs().then(e=>{
work_exec_map.delete(file_path);
this.online_change_push();
}).catch(e=>{
console.log('任务执行失败',e)
work_exec_map.delete(file_path);
worker.close();
this.online_change_push();
});
await this.exec_file(file_path,user_info);
} else if (pojo.run_type === WorkRunType.stop) {
const worker = work_exec_map.get(file_path);
if (!worker)
Expand All @@ -668,6 +644,34 @@ export class WorkflowService {
}
}

public async exec_file(file_path,user_info) {
if (work_exec_map.get(file_path))
throw "Workflow exec task already exists";
const worker = new work_children(file_path);
work_exec_map.set(file_path, worker);
try {
await worker.init({
filecat_user_id:userService.get_user_id(user_info.username),
filecat_user_name:user_info.username,
filecat_user_note:user_info.note
});
} catch (e){
work_exec_map.delete(file_path);
this.online_change_push();
throw e;
}
this.online_change_push(); // 在init后不然没有 filename
worker.run_jobs().then(e=>{
work_exec_map.delete(file_path);
this.online_change_push();
}).catch(e=>{
console.log('任务执行失败',e)
work_exec_map.delete(file_path);
worker.close();
this.online_change_push();
});
}

public async workflow_realtime_one(data: WsData<WorkFlowRealTimeOneReq>) {

const token: string = (data.wss as Wss).token;
Expand Down
4 changes: 2 additions & 2 deletions src/main/domain/net/net.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export class NetService {
console.error('Error:', err);
resolve(res.status(500).send(err.code));
} else {
resolve(res.header('filecat_remote_raw_headers',needle_res.rawHeaders).status(needle_res.statusCode).send(needle_res.raw));
resolve(res.header('filecat_remote_raw_headers',needle_res.rawHeaders).header("filecat_remote_code",needle_res.statusCode).status(200).send(needle_res.raw));
}
if(pojo.form_data_list) {
for(const item of pojo.form_data_list as HttpFormData[]) {
Expand All @@ -206,7 +206,7 @@ export class NetService {
}
switch (pojo.method) {
case 'get':
needle(pojo.method,pojo.url,option, call);
needle.get(pojo.url,option, call); // needle("get"...) 就会报错 。。。
break;
case 'put':
case 'post': {
Expand Down
17 changes: 15 additions & 2 deletions src/main/domain/setting/setting.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,32 @@ export class SettingController {


// 获取页面路由
@Get("/customer_router")
@Get(`/${Http_controller_router.setting_customer_router}`)
getRouter() {
return Sucess(settingService.getCustomerRouter());
}

// 设置页面路由
@Post('/customer_router/save')
@Post(`/${Http_controller_router.setting_customer_router_save}`)
saveRouter(@Body() req: any, @Req() r) {
userService.check_user_auth(r.headers.authorization, UserAuth.code_resource);
settingService.setCustomerRouter(req);
return Sucess("1");
}

@Get(`/${Http_controller_router.setting_customer_workflow_router}`)
get_workflow_router() {
return Sucess(settingService.get_workflow_router());
}

// 设置workflow路由
@Post(`/${Http_controller_router.setting_customer_workflow_router_save}`)
save_workflow_router(@Body() req: any, @Req() r) {
userService.check_user_auth(r.headers.authorization, UserAuth.workflow_api);
settingService.save_workflow_router(req);
return Sucess("1");
}

// 获取api路由
@Get("/api/customer_router")
getApiRouter() {
Expand Down
50 changes: 50 additions & 0 deletions src/main/domain/setting/setting.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import * as vm from "node:vm";
import {userController} from "../user/user.controller";
import {UserService, userService} from "../user/user.service";
import {shellServiceImpl, sysType} from "../shell/shell.service";
import {workflowService} from "../file/workflow/workflow.service";
import {Wss} from "../../../common/frame/ws.server";
import {UserAuth} from "../../../common/req/user.req";

const needle = require('needle');

Expand Down Expand Up @@ -48,11 +51,58 @@ export class SettingService {
DataUtil.set(customer_router_key, req);
}

get_workflow_router() {
const list = DataUtil.get(data_common_key.customer_workflow_router_key);
return list ?? [];
}

save_workflow_router(req: any) {
DataUtil.set(data_common_key.customer_workflow_router_key, req);
}

public async intercept(ctx: Request) {
let c_url = ctx.originalUrl;
if (ctx.originalUrl.includes("?")) {
c_url = ctx.originalUrl.split("?")[0];
}
const workflow_list_router = this.get_workflow_router() as [][];
if (!!workflow_list_router && workflow_list_router.length > 0) {
for (let item of workflow_list_router) {
// @ts-ignore
const router = item[0];
if (router === c_url) {
// @ts-ignore
const location = item[1];
if (location && fs.existsSync(location)) {
// @ts-ignore
const token = item[2];
if(token) {
// token验证
if(token !== ctx.headers.authorization) {
ctx.res.status(500).send("token is invalid");
return true;
}
}
// workflow文件存在
try {
// @ts-ignore
const user_info = userService.get_user_info_by_user_id(item[3]);
// @ts-ignore
userService.check_user_auth_by_user_id(item[3], UserAuth.workflow_exe);
workflowService.exec_file(location,user_info).catch((e)=>{
console.log("workflow触发失败",e)
ctx.res.status(500).send(JSON.stringify(e));
}).then(()=>{
ctx.res.status(200).send("ok");
});
} catch (e){
ctx.res.status(500).send(JSON.stringify(e));
}
return true;
}
}
}
}
const list_router = DataUtil.get<[][]>(customer_router_key);
if (!!list_router && list_router.length > 0) {
for (let item of list_router) {
Expand Down
27 changes: 24 additions & 3 deletions src/web/project/component/prompts/FilesUpload.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useEffect} from 'react';
import React, {useEffect, useRef, useState} from 'react';
import {ActionButton} from "../../../meta/component/Button";
import {useRecoilState} from "recoil";
import {$stroe} from "../../util/store";
Expand All @@ -20,24 +20,43 @@ export function FilesUpload() {
const [open, setOpen] = React.useState(false);
const [uploadFiles, setUploadFiles] = useRecoilState($stroe.uploadFiles);
const [nowProgress, setNowProgress] = useRecoilState($stroe.nowProgress);
const [speed, setSpeed] = useState(0); // 上传速度 MB/s
const timeoutRef = useRef<NodeJS.Timeout | null>(null); // 用于存储 `setTimeout` 渲染期间内保持 且不会被渲染

function click() {
setOpen(!open);
}


useEffect(() => {
(async () => {
const newList: any = Array.from(uploadFiles);
for (let index = 0; index < newList.length; ) {
let value: any = newList[index];
try {
// console.log(`${getRouterAfter('file',location.pathname)}${value.fullPath}`)
const startTime = Date.now();
const rsp = await fileHttp.put(`${encodeURIComponent(`${getRouterAfter('file',getRouterPath())}${value.fullPath}`)}?dir=${value.isDir?1:0}`, value, (progressEvent) => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
setNowProgress({
name: value.name,
value: percentCompleted,
index: index
})
const elapsedTime = (Date.now() - startTime) / 1000; // seconds

// 计算上传速度(MB/s)
if (elapsedTime > 0) {
const uploadSpeed = (progressEvent.loaded / (1024 * 1024) / elapsedTime).toFixed(2);
setSpeed(parseFloat(uploadSpeed));
}

// **清除旧的 `setTimeout` 并重置**
if (timeoutRef.current) clearTimeout(timeoutRef.current);
// **如果 1 秒后没有新的 `onUploadProgress` 触发,则设为 0**
timeoutRef.current = setTimeout(() => {
setSpeed(0);
}, 1000);
})
if (rsp.code === 0) {
// @ts-ignore
Expand Down Expand Up @@ -65,10 +84,12 @@ export function FilesUpload() {
<div className="card floating">
<div className="card-title">
<h2>{uploadFiles.length}{t("个文件正在上传")}</h2>
<ActionButton icon={open ? "keyboard_arrow_down" : "keyboard_arrow_up"} title={"Toggle file upload list"} onClick={click}/>
<ActionButton icon={open ? "keyboard_arrow_down" : "keyboard_arrow_up"}
title={"Toggle file upload list"} onClick={click}/>
<div className="upload-speed">{speed} MB/s</div>
</div>
{open && <div className="card-content file-icons">
{
{
uploadFiles.map((v: any, index) => (
<div className="file" key={index}>
<div className="file-name">
Expand Down
50 changes: 47 additions & 3 deletions src/web/project/component/setting/CustomerRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ import {Table} from "../../../meta/component/Table";
import {TableListRender} from "./component/TableListRend";
import {useTranslation} from "react-i18next";
import {CustomerApiRouter} from "./CustomerApiRouter";
import {Http_controller_router} from "../../../../common/req/http_controller_router";
import {useRecoilState} from "recoil";
import {$stroe} from "../../util/store";


export function CustomerRouter() {
const {t} = useTranslation();
const [prompt_card, set_prompt_card] = useRecoilState($stroe.prompt_card);

const headers = [t("路由"), `${t("文件")}|http${t("路径")}`, t("备注"),];
const headers_workflow = [t("路由"), t("文件路径"),"token","user id", t("备注"),];

const save = async (req: [[]]) => {
const result = await settingHttp.post("customer_router/save", req);
const result = await settingHttp.post(Http_controller_router.setting_customer_router_save, req);
if (result.code === RCode.Sucess) {
new Noty({
type: 'success',
Expand All @@ -29,15 +35,53 @@ export function CustomerRouter() {
}
}
const getItems = async () => {
const result = await settingHttp.get("customer_router");
const result = await settingHttp.get(Http_controller_router.setting_customer_router);
if (result.code === RCode.Sucess) {
return result.data;
}
return [];
}

const soft_ware_info_click = ()=>{
let context = <div>
需要以 "/api" 开头的路由,会自动识别文件地址和http地址,将页面转到对应结果。
</div>;
set_prompt_card({open:true,title:"信息",context_div : (
<div >
{context}
</div>
)})
}
const worlfow_api_info_click = ()=>{
let context = <div>
需要以 "/api" 开头的路由,文件路径是有效的workflow路径。
</div>;
set_prompt_card({open:true,title:"信息",context_div : (
<div >
{context}
</div>
)})
}
return <Row>
<Column>
<TableListRender headers={headers} getItems={getItems} save={save}/>
<TableListRender title={t("页面资源路由")} headers={headers} getItems={getItems} save={save} info_click={soft_ware_info_click}/>
<TableListRender title={t("workflow触发路由")} headers={headers_workflow} getItems={async ()=>{
const result = await settingHttp.get(Http_controller_router.setting_customer_workflow_router);
if (result.code === RCode.Sucess) {
return result.data;
}
return [];
}} save={async (req: [[]]) => {
const result = await settingHttp.post(Http_controller_router.setting_customer_workflow_router_save, req);
if (result.code === RCode.Sucess) {
new Noty({
type: 'success',
text: '保存成功',
timeout: 1000, // 设置通知消失的时间(单位:毫秒)
layout: "bottomLeft"
}).show();
}
}} info_click={worlfow_api_info_click}/>
</Column>
<Column>
<CustomerApiRouter />
Expand Down
8 changes: 8 additions & 0 deletions src/web/project/component/setting/component/Permission.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,14 @@ export function Permission(props:{
}}/>
{t("workflow 执行用户")}
</div>
<div>
<input type="checkbox"
checked={props.is_selected(UserAuth.workflow_api,true)}
onChange={() => {
props.select_auth(UserAuth.workflow_api)
}}/>
{t("workflow触发api 修改")}
</div>
</div>
<h3>{t("标签编辑权限")}</h3>
<div className={"checkbox_container"}>
Expand Down
Loading

0 comments on commit 9da58a1

Please sign in to comment.