From d60c08a281cd63f2183381a1a19c5e196b41fbc5 Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Thu, 30 Oct 2025 14:21:00 -0500 Subject: [PATCH] feat(start of server): added the start of server data --- app/main.ts | 1 + app/src/internal/system/routes/stats.ts | 1 + frontend/src/lib/querys/admin/getServers.ts | 17 + frontend/src/lib/querys/serverStats.ts | 17 + .../_app/_adminLayout/admin/servers.tsx | 300 +++++++++++++++++- .../routes/_old/old/-components/ocp/Lots.tsx | 2 +- 6 files changed, 332 insertions(+), 6 deletions(-) create mode 100644 frontend/src/lib/querys/admin/getServers.ts create mode 100644 frontend/src/lib/querys/serverStats.ts diff --git a/app/main.ts b/app/main.ts index 46b11a9..93e7129 100644 --- a/app/main.ts +++ b/app/main.ts @@ -118,6 +118,7 @@ const main = async () => { "http://localhost:3001", "http://localhost:4000", "http://localhost:4001", + "http://localhost:5500", env.BETTER_AUTH_URL, // prod ]; diff --git a/app/src/internal/system/routes/stats.ts b/app/src/internal/system/routes/stats.ts index c36a388..7c20218 100644 --- a/app/src/internal/system/routes/stats.ts +++ b/app/src/internal/system/routes/stats.ts @@ -23,6 +23,7 @@ router.get("/", async (req, res) => { const statData = data as ServerStats[]; const used = process.memoryUsage(); + res.json({ status: "ok", uptime: process.uptime(), diff --git a/frontend/src/lib/querys/admin/getServers.ts b/frontend/src/lib/querys/admin/getServers.ts new file mode 100644 index 0000000..635a9df --- /dev/null +++ b/frontend/src/lib/querys/admin/getServers.ts @@ -0,0 +1,17 @@ +import { queryOptions } from "@tanstack/react-query"; +import axios from "axios"; + +export function getServers() { + return queryOptions({ + queryKey: ["getModules"], + queryFn: () => fetchSession(), + staleTime: 5000, + refetchOnWindowFocus: true, + }); +} + +const fetchSession = async () => { + const { data } = await axios.get("/lst/api/admin/server"); + + return data.data; +}; diff --git a/frontend/src/lib/querys/serverStats.ts b/frontend/src/lib/querys/serverStats.ts new file mode 100644 index 0000000..ca4695b --- /dev/null +++ b/frontend/src/lib/querys/serverStats.ts @@ -0,0 +1,17 @@ +import { queryOptions } from "@tanstack/react-query"; +import axios from "axios"; + +export function getServerStats() { + return queryOptions({ + queryKey: ["getServerStats"], + queryFn: () => fetchSession(), + staleTime: 5000, + refetchOnWindowFocus: true, + }); +} + +const fetchSession = async () => { + const { data } = await axios.get(`/lst/api/system/stats`); + + return data; +}; diff --git a/frontend/src/routes/_app/_adminLayout/admin/servers.tsx b/frontend/src/routes/_app/_adminLayout/admin/servers.tsx index ed0c268..a324cd0 100644 --- a/frontend/src/routes/_app/_adminLayout/admin/servers.tsx +++ b/frontend/src/routes/_app/_adminLayout/admin/servers.tsx @@ -1,9 +1,299 @@ -import { createFileRoute } from '@tanstack/react-router' +import { useMutation, useQuery } from "@tanstack/react-query"; +import { createFileRoute } from "@tanstack/react-router"; +import { + createColumnHelper, + flexRender, + getCoreRowModel, + getPaginationRowModel, + getSortedRowModel, + type SortingState, + useReactTable, +} from "@tanstack/react-table"; +import axios from "axios"; +import { ArrowDown, ArrowUp } from "lucide-react"; +import React, { useEffect, useState } from "react"; +import { toast } from "sonner"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { getServers } from "@/lib/querys/admin/getServers"; -export const Route = createFileRoute('/_app/_adminLayout/admin/servers')({ - component: RouteComponent, -}) +type ServerData = { + name: string; + serverDNS: string; + plantToken: string; + ipAddress: string; + uptime: string; + build: string; + pendingUpdateFile: string; + lastUpdate: Date; +}; + +const updateServerItem = async ( + token: string, + data: Record, +) => { + try { + const res = axios.patch(`/lst/api/admin/server/${token}`, data, { + withCredentials: true, + }); + + toast.success(`Updated: ${data.field} set to ${data.value}`); + return res; + } catch (err) { + toast.error(`Error updating: ${data.field} being set to ${data.value}`); + return err; + } +}; +export const Route = createFileRoute("/_app/_adminLayout/admin/servers")({ + component: RouteComponent, +}); function RouteComponent() { - return
Hello "/_adminLayout/admin/servers"!
+ const { data, isLoading, refetch } = useQuery(getServers()); + // const { + // data: stats, + // isLoading: loadingStats, + // refetch: statsRefetch, + // } = useQuery(getServerStats()); + const [sorting, setSorting] = useState([]); + const columnHelper = createColumnHelper(); + + const updateServer = useMutation({ + mutationFn: ({ + token, + field, + value, + }: { + token: string; + field: string; + value: string | number | boolean | null; + }) => updateServerItem(token, { [field]: value }), + + onSuccess: () => { + // refetch or update cache + + refetch(); + }, + }); + + const columns = [ + columnHelper.accessor("name", { + cell: (i) => i.getValue(), + header: ({ column }) => { + return ( + + ); + }, + }), + + columnHelper.accessor("serverDNS", { + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row, getValue }) => { + const initialValue = String(getValue() ?? ""); + const [localValue, setLocalValue] = React.useState(initialValue); + const token = row.original.plantToken; + const field = "serverDNS"; + + useEffect(() => setLocalValue(initialValue), [initialValue]); + + const handleSubmit = (newValue: string) => { + if (newValue !== initialValue) { + setLocalValue(newValue); // keep new value locally immediately + updateServer.mutate({ token, field, value: newValue }); + } + }; + return ( + setLocalValue(e.currentTarget.value)} + onBlur={(e) => handleSubmit(e.currentTarget.value.trim())} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + handleSubmit(e.currentTarget.value.trim()); + e.currentTarget.blur(); // exit edit mode + } + }} + /> + ); + }, + }), + columnHelper.accessor("ipAddress", { + header: ({ column }) => { + return ( + + ); + }, + cell: ({ row, getValue }) => { + const initialValue = String(getValue() ?? ""); + const [localValue, setLocalValue] = React.useState(initialValue); + const token = row.original.plantToken; + const field = "ipAddress"; + + useEffect(() => setLocalValue(initialValue), [initialValue]); + + const handleSubmit = (newValue: string) => { + if (newValue !== initialValue) { + setLocalValue(newValue); // keep new value locally immediately + updateServer.mutate({ token, field, value: newValue }); + } + }; + return ( + setLocalValue(e.currentTarget.value)} + onBlur={(e) => handleSubmit(e.currentTarget.value.trim())} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + handleSubmit(e.currentTarget.value.trim()); + e.currentTarget.blur(); // exit edit mode + } + }} + /> + ); + }, + }), + + // columnHelper.accessor("uptime", { + // header: () => { + // return UpTime; + // }, + // cell: ({ row }) => { + // //console.log(data); + // return {data?.uptime ?? "--"}; + // }, + // }), + ]; + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onSortingChange: setSorting, + getSortedRowModel: getSortedRowModel(), + //renderSubComponent: ({ row }: { row: any }) => , + //getRowCanExpand: () => true, + state: { + sorting, + }, + }); + + if (isLoading) { + return
Loading user data
; + } + return ( +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + + {/* {row.getIsExpanded() && ( + + + {renderSubComponent({ row })} + + + )} */} + + ))} + +
+
+ + +
+
+
+ ); } diff --git a/frontend/src/routes/_old/old/-components/ocp/Lots.tsx b/frontend/src/routes/_old/old/-components/ocp/Lots.tsx index 2bc201a..fa972b5 100644 --- a/frontend/src/routes/_old/old/-components/ocp/Lots.tsx +++ b/frontend/src/routes/_old/old/-components/ocp/Lots.tsx @@ -1,5 +1,4 @@ import { useQuery } from "@tanstack/react-query"; - import { ScrollArea } from "@/components/ui/scroll-area"; import { Skeleton } from "@/components/ui/skeleton"; import { @@ -66,6 +65,7 @@ let lotColumns = [ ]; export default function Lots() { const { data, isError, isLoading } = useQuery(getlots()); + const { session } = useAuth(); const { settings } = useSettingStore();