Compare commits

..

2 Commits

Author SHA1 Message Date
李如威 5f138ec9df feat: 状态等待时间 2025-09-25 19:23:37 +08:00
李如威 9584f370ad feat: 耗时 2025-09-25 09:06:05 +08:00
6 changed files with 50 additions and 5 deletions

View File

@ -11,7 +11,7 @@ import nodes as F
from tool import format_content_uuid from tool import format_content_uuid
from tortoise.transactions import in_transaction from tortoise.transactions import in_transaction
from tortoise.functions import Max from tortoise.functions import Max
from fastapi import FastAPI, Depends, HTTPException from fastapi import FastAPI, Depends, HTTPException, Query
from models.schemas.system import InitStatusResponse, InitConfigRequest, BaseResponse, TableResponse from models.schemas.system import InitStatusResponse, InitConfigRequest, BaseResponse, TableResponse
from models.schemas.user import LoginRequest, LoginResponse from models.schemas.user import LoginRequest, LoginResponse
from models.schemas.client import ClientRequest from models.schemas.client import ClientRequest
@ -74,6 +74,10 @@ def get_current_user(token: str = Depends(oauth2_scheme)) -> DB.User | None:
return user return user
def pagination_query(page:int = Query(1), page_size:int = Query(20)):
return {"page": page, "page_size": page_size}
async def is_init(): async def is_init():
init_config = await load_init_config() init_config = await load_init_config()
init_status = init_config.get("is_init") init_status = init_config.get("is_init")
@ -207,6 +211,7 @@ def create_app():
) )
await work_log.save() await work_log.save()
try: try:
start_time = int(datetime.datetime.now().timestamp())
work_content = await DB.WorkContent.filter(work_id=work.id).first() work_content = await DB.WorkContent.filter(work_id=work.id).first()
if not work_content: if not work_content:
logger.warning(f"没有找到对应的内容: {work.uuid}") logger.warning(f"没有找到对应的内容: {work.uuid}")
@ -251,7 +256,7 @@ def create_app():
await flow.run_async(shared) await flow.run_async(shared)
app.state.tasks.pop(task_id, None) app.state.tasks.pop(task_id, None)
app.state.task_nodes.pop(task_id, None) app.state.task_nodes.pop(task_id, None)
end_time = int(datetime.datetime.now().timestamp())
# 更新日志 # 更新日志
work_log_result = [] work_log_result = []
for x in nodes: for x in nodes:
@ -269,7 +274,7 @@ def create_app():
"result": shared.get(id, {}), "result": shared.get(id, {}),
"status": status "status": status
}) })
work_log.result = work_log_result work_log.result = {"time": end_time - start_time, "logs": work_log_result}
if next((x for x in shared.values() if x.get("err")), None): if next((x for x in shared.values() if x.get("err")), None):
work_log.status = DB.WorkLogsStatus.FAILED work_log.status = DB.WorkLogsStatus.FAILED
else: else:
@ -322,6 +327,7 @@ def create_app():
log: DB.WorkLogs = next((x for x in work_logs_list if x.work_id == work.id), DB.WorkLogs(user_id=-1)) log: DB.WorkLogs = next((x for x in work_logs_list if x.work_id == work.id), DB.WorkLogs(user_id=-1))
log_user: DB.User = next((u for u in user_list if u.id == log.user_id), DB.User()) log_user: DB.User = next((u for u in user_list if u.id == log.user_id), DB.User())
obj["last_user"] = log_user.username or "" obj["last_user"] = log_user.username or ""
obj["last_use_time"] = log.result.get("time", 0)
obj["last_run_time"] = log.create_time.timestamp() if log.create_time else 0 obj["last_run_time"] = log.create_time.timestamp() if log.create_time else 0
obj["last_status"] = log.status or "" obj["last_status"] = log.status or ""
obj["is_running"] = work.uuid in global_tasks obj["is_running"] = work.uuid in global_tasks
@ -451,6 +457,12 @@ def create_app():
await DB.Client.filter(id=id).delete() await DB.Client.filter(id=id).delete()
return BaseResponse() return BaseResponse()
@app.get("/api/logs", tags=["Log"], description="日志列表", response_model=TableResponse)
async def log_list(p:dict = Depends(pagination_query), user: DB.User = Depends(get_current_user)):
page, page_size = p["page"], p["page_size"]
res = await DB.WorkLogs.all().order_by('-id').limit(page_size).offset((page - 1) * page_size)
return TableResponse(data=[x.json() for x in res], total=len(res))
return app return app

View File

@ -8,6 +8,7 @@ async def async_run_cmd(
password: str = None, password: str = None,
port: int = None, port: int = None,
sudo: bool = False, sudo: bool = False,
wait: int = 0,
): ):
""" """
异步执行命令 异步执行命令
@ -15,6 +16,9 @@ async def async_run_cmd(
- 如果 host 不为空则通过 SSH 执行远程命令 - 如果 host 不为空则通过 SSH 执行远程命令
""" """
if wait > 0:
await asyncio.sleep(wait)
async def read_stream(stream, stream_type): async def read_stream(stream, stream_type):
async for line in stream: async for line in stream:
text = line text = line

View File

@ -1,3 +1,4 @@
import asyncio
from fastapi import FastAPI from fastapi import FastAPI
from core.pocket_flow import AsyncNode from core.pocket_flow import AsyncNode
from core.logger import logger from core.logger import logger
@ -36,6 +37,7 @@ class StatusNode(AsyncNode):
"password": self.params[self.id].get("password"), "password": self.params[self.id].get("password"),
"port": self.params[self.id].get("port"), "port": self.params[self.id].get("port"),
"sudo": self.params[self.id].get("sudo", False), "sudo": self.params[self.id].get("sudo", False),
"wait": self.params[self.id].get("wait", 3),
"cmd": cmd_str, "cmd": cmd_str,
} }

6
frontend/src/api/log.js Normal file
View File

@ -0,0 +1,6 @@
import { request } from "./base";
export const tableList = async (data = {}) => {
return request("/api/logs?" + new URLSearchParams(data).toString(), "GET")
}

View File

@ -83,7 +83,12 @@ export default () => {
{ {
dataIndex: 'last_run_time', dataIndex: 'last_run_time',
title: '最后执行时间', title: '最后执行时间',
render: (_, record) => record?.last_run_time ? new Date(record?.last_run_time * 1000).toLocaleString() : '' render: (_, record) => record?.last_run_time ? new Date(record?.last_run_time * 1000).toLocaleString() : '-'
},
{
dataIndex: 'last_use_time',
title: '最后执行耗时',
render: (_, record) => record?.last_use_time ? formatSeconds(record?.last_use_time) : '-',
}, },
{ {
dataIndex: 'last_status', dataIndex: 'last_status',
@ -190,4 +195,11 @@ export default () => {
</Card> </Card>
) )
}
export const formatSeconds = (seconds = 0) => {
const h = String(Math.floor(seconds / 3600)).padStart(2, "0");
const m = String(Math.floor((seconds % 3600) / 60)).padStart(2, "0");
const s = String(seconds % 60).padStart(2, "0");
return h === "00" ? `${m}:${s}` : `${h}:${m}:${s}`;
} }

View File

@ -1,6 +1,8 @@
import { ProTable } from "@ant-design/pro-components"; import { ProTable } from "@ant-design/pro-components";
import { Card } from "antd"; import { Card } from "antd";
import { useRef } from "react"; import { useRef } from "react";
import { formatSeconds } from "../Flow";
import { tableList } from "../../api/log";
const LogPage = () => { const LogPage = () => {
const actionRef = useRef(null); const actionRef = useRef(null);
@ -26,7 +28,12 @@ const LogPage = () => {
{ {
dataIndex: 'last_run_time', dataIndex: 'last_run_time',
title: '执行时间', title: '执行时间',
render: (_, record) => record?.last_run_time ? new Date(record?.last_run_time * 1000).toLocaleString() : '' render: (_, record) => record?.last_run_time ? new Date(record?.last_run_time * 1000).toLocaleString() : '-'
},
{
dataIndex: 'time',
title: '执行耗时',
render: (_, record) => record?.time ? formatSeconds(record?.time) : '-',
}, },
{ {
dataIndex: 'last_status', dataIndex: 'last_status',
@ -51,6 +58,8 @@ const LogPage = () => {
} }
]; ];
const onRequest = async (params) => { const onRequest = async (params) => {
const res = await tableList(params)
console.log(res);
return {}; return {};
} }
return <Card title="操作日志"> return <Card title="操作日志">