gitscope
← marzneshin / v0.3.1 / dashboard/src/features/nodes/services/logs.socket.ts
https://dev.kronoz.win/r/marzneshin.git
dashboard/src/features/nodes/services/logs.socket.ts · TypeScript
Blame Raw
 1import { NodeType } from "@marzneshin/features/nodes";
 2import useWebSocket, { ReadyState } from "react-use-websocket";
 3import { useAuth } from "@marzneshin/features/auth";
 4import { joinPaths } from "@tanstack/react-router";
 5import { useCallback, useEffect, useRef, useState } from "react";
 6
 7export const MAX_NUMBER_OF_LOGS = 500
 8
 9export const getStatus = (status: string) => {
10    return {
11        [ReadyState.CONNECTING]: 'connecting',
12        [ReadyState.OPEN]: 'connected',
13        [ReadyState.CLOSING]: 'closed',
14        [ReadyState.CLOSED]: 'closed',
15        [ReadyState.UNINSTANTIATED]: 'closed',
16    }[status];
17};
18
19export const getWebsocketUrl = (nodeId: number) => {
20    try {
21        const baseURL = new URL(
22            import.meta.env.VITE_BASE_API.startsWith('/')
23                ? window.location.origin + import.meta.env.VITE_BASE_API
24                : import.meta.env.VITE_BASE_API
25        );
26
27        return (
28            (baseURL.protocol === 'https:' ? 'wss://' : 'ws://') +
29            joinPaths([baseURL.host + baseURL.pathname, `/nodes/${nodeId}/logs`]) +
30            '?interval=1&token=' +
31            useAuth.getState().getAuthToken()
32        );
33    } catch (e) {
34        console.error('Unable to generate websocket url');
35        console.error(e);
36        return null;
37    }
38};
39
40
41export const useNodesLog = (node: NodeType) => {
42    const [logs, setLogs] = useState<string[]>([]);
43    const logsDiv = useRef<HTMLDivElement | null>(null);
44    const scrollShouldStayOnEnd = useRef(true);
45
46    const updateLogs = useCallback((callback: (prevLogs: string[]) => string[]) => {
47        setLogs(prevLogs => {
48            const newLogs = callback(prevLogs);
49            return newLogs.length > MAX_NUMBER_OF_LOGS ? newLogs.slice(-MAX_NUMBER_OF_LOGS) : newLogs;
50        });
51    }, []);
52
53    const { readyState } = useWebSocket(node?.id ? getWebsocketUrl(node.id) : '', {
54        onMessage: (e: any) => {
55            updateLogs(prevLogs => [...prevLogs, e.data]);
56        },
57        shouldReconnect: () => true,
58        reconnectAttempts: 10,
59        reconnectInterval: 1000,
60    });
61
62    useEffect(() => {
63        if (logsDiv.current && scrollShouldStayOnEnd.current) {
64            logsDiv.current.scrollTop = logsDiv.current.scrollHeight;
65        }
66    }, [logs]);
67
68    useEffect(() => {
69        return () => {
70            // Cleanup
71            setLogs([]);
72        };
73    }, []);
74
75    const status = getStatus(readyState.toString());
76
77    return { status, readyState, logs, logsDiv };
78};