feat: client
This commit is contained in:
parent
25fa8a043a
commit
03885d5a52
|
|
@ -1,4 +1,5 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import datetime
|
||||||
import json
|
import json
|
||||||
import uvicorn
|
import uvicorn
|
||||||
import uuid
|
import uuid
|
||||||
|
|
@ -11,6 +12,7 @@ from tortoise.functions import Max
|
||||||
from fastapi import FastAPI, Depends, HTTPException
|
from fastapi import FastAPI, Depends, HTTPException
|
||||||
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 core.config import load_init_config, save_init_config
|
from core.config import load_init_config, save_init_config
|
||||||
from core.db import init_tortoise, close_tortoise
|
from core.db import init_tortoise, close_tortoise
|
||||||
from core.logger import logger
|
from core.logger import logger
|
||||||
|
|
@ -413,6 +415,35 @@ def create_app():
|
||||||
|
|
||||||
return StreamingResponse(_event(), media_type="text/event-stream")
|
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
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
from .system import *
|
from .system import *
|
||||||
from .user import *
|
from .user import *
|
||||||
from .work import *
|
from .work import *
|
||||||
|
from .client import *
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Outlet, useNavigate, useLocation } from "react-router-dom";
|
import { Outlet, useNavigate, useLocation } from "react-router-dom";
|
||||||
import { Layout, Menu, Button, Flex, Dropdown, Steps } from "antd";
|
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 { useAuth } from "../context/AuthContext";
|
||||||
import './Layout.css';
|
import './Layout.css';
|
||||||
|
|
||||||
|
|
@ -15,6 +15,7 @@ export default function LayoutPage() {
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ key: "1", icon: <NodeExpandOutlined />, label: "工作台", path: "/flow" },
|
{ 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 || "";
|
const selectedKey = menuItems.find(item => location.pathname.startsWith(item.path))?.key || "";
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import Profile from "./pages/User/Profile";
|
||||||
import Flow from "./pages/Flow";
|
import Flow from "./pages/Flow";
|
||||||
import FlowDetail from "./pages/Flow/Detail";
|
import FlowDetail from "./pages/Flow/Detail";
|
||||||
import LayoutPage from "./pages/Layout";
|
import LayoutPage from "./pages/Layout";
|
||||||
|
import ClientPage from "./pages/Client";
|
||||||
|
|
||||||
|
|
||||||
// 受保护的路由:没登录就跳转到登录页
|
// 受保护的路由:没登录就跳转到登录页
|
||||||
|
|
@ -29,6 +30,7 @@ const routes = [
|
||||||
element: <LayoutPage />,
|
element: <LayoutPage />,
|
||||||
children: [
|
children: [
|
||||||
{ path: "/flow", element: <Flow /> },
|
{ path: "/flow", element: <Flow /> },
|
||||||
|
{ path: "/client", element: <ClientPage /> },
|
||||||
{ path: "/flow/detail/:work_uuid", element: <FlowDetail /> },
|
{ path: "/flow/detail/:work_uuid", element: <FlowDetail /> },
|
||||||
{ path: "/user", element: <User /> },
|
{ path: "/user", element: <User /> },
|
||||||
{ path: "/user/detail", element: <Profile /> }
|
{ path: "/user/detail", element: <Profile /> }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue