update: init

This commit is contained in:
hamster1963
2024-11-22 22:20:38 +08:00
commit e5682aacbd
45 changed files with 1834 additions and 0 deletions

30
src/lib/nav-router.ts Normal file
View File

@@ -0,0 +1,30 @@
export const navRouter = [
{
name: "服务器",
path: "/",
},
{
name: "服务(Dev)",
path: "/service",
},
{
name: "任务(Dev)",
path: "/task",
},
{
name: "告警(Dev)",
path: "/alarm",
},
{
name: "内网穿透(Dev)",
path: "/intranet",
},
{
name: "用户",
path: "/user",
},
{
name: "设置(Dev)",
path: "/setting",
},
];

80
src/lib/nezha-api.ts Normal file
View File

@@ -0,0 +1,80 @@
export const fetchUsers = async (token: string) => {
const response = await fetch("http://localhost:8008/api/v1/user", {
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
return data;
};
export const createUser = async (
token: string,
username: string,
password: string,
) => {
const response = await fetch(`http://localhost:8008/api/v1/user`, {
method: "POST",
body: JSON.stringify({ username, password }),
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
return data;
};
export const deleteUser = async (token: string, ids: number[]) => {
const response = await fetch(
`http://localhost:8008/api/v1/batch-delete/user`,
{
method: "POST",
body: JSON.stringify(ids),
headers: {
Authorization: `Bearer ${token}`,
},
},
);
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
return data;
};
export const fetchServers = async (token: string) => {
const response = await fetch("http://localhost:8008/api/v1/server", {
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
return data;
};
export const deleteServer = async (token: string, ids: number[]) => {
const response = await fetch(
`http://localhost:8008/api/v1/batch-delete/server`,
{
method: "POST",
body: JSON.stringify(ids),
headers: {
Authorization: `Bearer ${token}`,
},
},
);
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
return data;
};

79
src/lib/nezha-model.ts Normal file
View File

@@ -0,0 +1,79 @@
/**
* model.Server
*/
export interface ModelServer {
created_at: string;
/**
* DDNS配置
*/
ddns_profiles: number[];
deleted_at: string;
/**
* 展示排序,越大越靠前
*/
display_index: number;
/**
* 启用DDNS
*/
enable_ddns: boolean;
/**
* 对游客隐藏
*/
hide_for_guest?: boolean;
host?: ModelHost;
id: number;
last_active?: string;
name: string;
/**
* 管理员可见备注
*/
note: string;
/**
* 公开备注
*/
public_note: string;
state: ModelHostState;
updated_at: string;
uuid: string;
}
export interface ModelHost {
arch?: string;
boot_time?: number;
country_code?: string;
cpu?: string[];
disk_total?: number;
gpu?: string[];
ip?: string;
mem_total?: number;
platform?: string;
platform_version?: string;
swap_total?: number;
version?: string;
virtualization?: string;
}
export interface ModelHostState {
cpu?: number;
disk_used?: number;
gpu?: number[];
load_1?: number;
load_15?: number;
load_5?: number;
mem_used?: number;
net_in_speed?: number;
net_in_transfer?: number;
net_out_speed?: number;
net_out_transfer?: number;
process_count?: number;
swap_used?: number;
tcp_conn_count?: number;
temperatures?: ModelSensorTemperature[];
udp_conn_count?: number;
uptime?: number;
}
export interface ModelSensorTemperature {
name?: string;
temperature?: number;
}

89
src/lib/useWebsocket.tsx Normal file
View File

@@ -0,0 +1,89 @@
import { useState, useEffect, useRef, useCallback } from 'react'
export interface WebSocketHook {
socket: WebSocket | null
connected: boolean
onlineCount: number
message: string | null
sendMessage: (msg: string) => void
}
export default function useWebSocket(url: string): WebSocketHook {
const [socket, setSocket] = useState<WebSocket | null>(null)
const [message, setMessage] = useState<string | null>(null)
const [connected, setConnected] = useState<boolean>(false)
const [onlineCount, setOnlineCount] = useState<number>(0)
const socketRef = useRef<WebSocket | null>(null)
const reconnectAttempts = useRef<number>(0)
const reconnectTimeout = useRef<NodeJS.Timeout | null>(null)
const isUnmounted = useRef<boolean>(false)
const connect = useCallback(() => {
if (isUnmounted.current) return
const ws = new WebSocket(url)
setSocket(ws)
socketRef.current = ws
ws.onopen = () => {
setConnected(true)
reconnectAttempts.current = 0
}
ws.onmessage = (event: MessageEvent) => {
setMessage(event.data)
const msgJson = JSON.parse(event.data)
if (msgJson.type === 'live') {
setOnlineCount(msgJson.data.count)
}
}
ws.onerror = (error) => {
console.error('WebSocket Error:', error)
}
ws.onclose = () => {
setConnected(false)
if (!isUnmounted.current) {
// Attempt to reconnect
if (reconnectAttempts.current < 5) {
const timeout = Math.pow(2, reconnectAttempts.current) * 1000 // Exponential backoff
reconnectAttempts.current += 1
reconnectTimeout.current = setTimeout(() => {
connect()
}, timeout)
} else {
console.warn('Max reconnect attempts reached.')
}
}
}
}, [url])
useEffect(() => {
connect()
return () => {
isUnmounted.current = true
if (socketRef.current) {
socketRef.current.close()
}
if (reconnectTimeout.current) {
clearTimeout(reconnectTimeout.current)
}
}
}, [connect])
// Function to send messages
const sendMessage = useCallback((msg: string) => {
if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) {
socketRef.current.send(msg)
} else {
console.warn(
'WebSocket is not open. Ready state:',
socketRef.current?.readyState
)
}
}, [])
return { socket, message, sendMessage, connected, onlineCount }
}

6
src/lib/utils.ts Normal file
View File

@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

View File

@@ -0,0 +1,26 @@
import { createContext, useContext, ReactNode } from 'react'
import useWebSocket, { WebSocketHook } from './useWebsocket'
interface WebSocketProviderProps {
children: ReactNode
}
const WebSocketContext = createContext<WebSocketHook | undefined>(undefined)
export const WebSocketProvider = ({ children }: WebSocketProviderProps) => {
const ws = useWebSocket('wss://dev-next.buycoffee.top:4433/api/v1/ws/server')
return (
<WebSocketContext.Provider value={ws}>{children}</WebSocketContext.Provider>
)
}
export const useWebSocketContext = (): WebSocketHook => {
const context = useContext(WebSocketContext)
if (!context) {
throw new Error(
'useWebSocketContext must be used within a WebSocketProvider'
)
}
return context
}