feat: client

This commit is contained in:
李如威 2025-09-16 22:57:56 +08:00
parent 25fa8a043a
commit 03885d5a52
9 changed files with 157 additions and 2 deletions

View File

@ -1,4 +1,5 @@
import asyncio
import datetime
import json
import uvicorn
import uuid
@ -11,6 +12,7 @@ from tortoise.functions import Max
from fastapi import FastAPI, Depends, HTTPException
from models.schemas.system import InitStatusResponse, InitConfigRequest, BaseResponse, TableResponse
from models.schemas.user import LoginRequest, LoginResponse
from models.schemas.client import ClientRequest
from core.config import load_init_config, save_init_config
from core.db import init_tortoise, close_tortoise
from core.logger import logger
@ -413,6 +415,35 @@ def create_app():
return StreamingResponse(_event(), media_type="text/event-stream")
@app.get("/api/clients", tags=["Client"], description="终端列表", response_model=TableResponse)
async def clients_list(user: DB.User = Depends(get_current_user)):
res = await DB.Client.filter(user_id=user.id).all()
return TableResponse(data=[x.json() for x in res], total=len(res))
@app.post("/api/clients", tags=["Client"], description="终端创建", response_model=BaseResponse)
async def create_client(body: ClientRequest, user: DB.User = Depends(get_current_user)):
await DB.Client(**body, user_id=user.id).save()
return BaseResponse()
@app.post("/api/clients", tags=["Client"], description="终端创建", response_model=BaseResponse)
async def create_client(body: ClientRequest, user: DB.User = Depends(get_current_user)):
await DB.Client(**body, user_id=user.id).save()
return BaseResponse()
@app.put("/api/clients", tags=["Client"], description="终端修改", response_model=BaseResponse)
async def update_client(client_id:int, body: ClientRequest, user: DB.User = Depends(get_current_user)):
obj = await DB.Client.filter(id=client_id).first()
if obj:
obj.update_from_dict(body.model_dump())
obj.update_time = datetime.datetime.now()
await obj.save()
return BaseResponse()
@app.delete("/api/clients", tags=["Client"], description="终端删除", response_model=BaseResponse)
async def del_client(client_id: int, user: DB.User = Depends(get_current_user)):
await DB.Client.filter(id=client_id).delete()
return BaseResponse()
return app

View File

@ -1,3 +1,4 @@
from .system import *
from .user import *
from .work import *
from .work import *
from .client import *

View File

@ -0,0 +1,14 @@
from tortoise import fields
from .system import BaseModel
class Client(BaseModel):
name = fields.CharField(max_length=50, null=True)
host = fields.CharField(max_length=50, null=True)
port = fields.IntField(default=22, null=True)
user = fields.CharField(max_length=50, null=True)
password = fields.CharField(max_length=50, null=True)
user_id = fields.IntField(null=True)
class Meta:
table = "tb_client"

View File

@ -0,0 +1,11 @@
from pydantic import BaseModel
from .system import BaseResponse
import hashlib
class ClientRequest(BaseModel):
port: int
host: str
password: str
user: str
name: str

View File

@ -0,0 +1,21 @@
import { request } from "./base";
export const tableList = async (data = {}) => {
return request("/api/clients?" + new URLSearchParams(data).toString(), "GET")
}
export const create = async (data = {}) => {
return request("/api/clients", "POST", data)
}
export const update = async (id, data = {}) => {
return request("/api/clients?id=" + id, "PUT", data)
}
export const del = async (id) => {
return request("/api/clients?id=" + id, "DELETE")
}

View File

@ -0,0 +1,11 @@
import { ModalForm, ProForm } from "@ant-design/pro-components";
import { Button } from "antd";
import { PlusOutlined } from "@ant-design/icons";
const Create = ({ onFinish }) => {
return <ModalForm
trigger={<Button icon={<PlusOutlined />} type='primary'>创建</Button>}
></ModalForm>
}
export default Create;

View File

@ -0,0 +1,63 @@
import { ProTable } from "@ant-design/pro-components"
import { Card } from "antd"
import { useRef } from "react"
import { useNavigate } from "react-router-dom"
import { tableList } from "../../api/client"
import Create from "./components/Create"
const ClientPage = () => {
const navigate = useNavigate();
const actionRef = useRef(null);
const onRequest = async () => {
const res = await tableList();
return res;
}
const columns = [
{
title: '#ID',
dataIndex: "id",
},
{
title: '名字',
dataIndex: "name",
},
{
title: '地址',
dataIndex: "host",
},
{
title: '创建时间',
dataIndex: "update_time",
},
{
title: '操作',
valueType: 'option',
key: 'option',
with: 100,
fixed: 'right',
render: (_, record) => [
<Button key='2' type="link" style={{ padding: '0 0', border: 'none' }} onClick={() => {
navigate("/flow/detail/" + record.uuid);
}}>编辑</Button>,
<Button key='3' type="link" danger style={{ padding: '0 0', border: 'none' }}>删除</Button>,
]
}
]
return <Card title="节点管理">
<ProTable
toolBarRender={() => <Create />}
actionRef={actionRef}
rowKey='id'
search={false}
scroll={{ x: 'max-content' }}
options={false}
columns={columns}
request={onRequest}
/>
</Card>
}
export default ClientPage

View File

@ -1,7 +1,7 @@
import React, { useState } from "react";
import { Outlet, useNavigate, useLocation } from "react-router-dom";
import { Layout, Menu, Button, Flex, Dropdown, Steps } from "antd";
import { MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, LoginOutlined, ProfileOutlined, NodeExpandOutlined } from "@ant-design/icons";
import { MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, LoginOutlined, ProfileOutlined, NodeExpandOutlined, LaptopOutlined } from "@ant-design/icons";
import { useAuth } from "../context/AuthContext";
import './Layout.css';
@ -15,6 +15,7 @@ export default function LayoutPage() {
const menuItems = [
{ key: "1", icon: <NodeExpandOutlined />, label: "工作台", path: "/flow" },
{ key: "2", icon: <LaptopOutlined />, label: "节点管理", path: "/client" },
];
const selectedKey = menuItems.find(item => location.pathname.startsWith(item.path))?.key || "";

View File

@ -8,6 +8,7 @@ import Profile from "./pages/User/Profile";
import Flow from "./pages/Flow";
import FlowDetail from "./pages/Flow/Detail";
import LayoutPage from "./pages/Layout";
import ClientPage from "./pages/Client";
//
@ -29,6 +30,7 @@ const routes = [
element: <LayoutPage />,
children: [
{ path: "/flow", element: <Flow /> },
{ path: "/client", element: <ClientPage /> },
{ path: "/flow/detail/:work_uuid", element: <FlowDetail /> },
{ path: "/user", element: <User /> },
{ path: "/user/detail", element: <Profile /> }