Browse Source

working services

Leo dev 1 week ago
parent
commit
2151228b85
3 changed files with 240 additions and 8 deletions
  1. 8 6
      src/app/api/server/route.ts
  2. 13 2
      src/app/servers/[server]/page.tsx
  3. 219 0
      src/app/servers/[server]/service.tsx

+ 8 - 6
src/app/api/server/route.ts

@@ -1,12 +1,9 @@
-import { HttpStatusCode } from "axios";
+import axios, { HttpStatusCode } from "axios";
 import { NextResponse } from "next/server";
 import { servers } from "../servers/servers";
 import validate from "../auth/auth";
 
-/*
-  Vision: This will be 
-*/
-export function GET(request: Request) {
+export async function GET(request: Request) {
   const { searchParams } = new URL(request.url);
 
   if (!validate(searchParams.get("sessionId")))
@@ -17,7 +14,7 @@ export function GET(request: Request) {
 
   const index = searchParams.get("index");
 
-  if (!index)
+  if (!index || !servers.data[parseInt(index)])
     return NextResponse.json(
       {
         message: "The index is invalid",
@@ -27,7 +24,12 @@ export function GET(request: Request) {
       }
     );
 
+  const server = servers.data[parseInt(index)];
+
+  const apps = await axios.get(`http://${server.ip}:8080/apps`, {params: {auth: 'my-key'}});
+
   return NextResponse.json({
     message: "ok",
+    apps: apps.data,
   });
 }

+ 13 - 2
src/app/servers/[server]/page.tsx

@@ -2,10 +2,15 @@
 import useSession from "@/hooks/useSession";
 import Server from "@/types/server";
 import { useEffect, useState } from "react";
+import ServiceComponent from "./service";
+
+interface Server2 extends Server {
+  apps: { name?: string }[]
+}
 
 export default function ServerPage() {
   const session = useSession();
-  const [server, setServer] = useState<Server | undefined>();
+  const [server, setServer] = useState<Server2 | undefined>();
 
   useEffect(() => {
     session?.getServerIndex(0).then((s) => {
@@ -13,5 +18,11 @@ export default function ServerPage() {
     });
   }, [session]);
 
-  return <h1>{JSON.stringify(server)}</h1>;
+  return server && (
+    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-5">
+      {server.apps.map((a, i) => (
+        <ServiceComponent key={i} server={a} index={i} session={session} / >
+    ))}
+    </div>
+  );
 }

+ 219 - 0
src/app/servers/[server]/service.tsx

@@ -0,0 +1,219 @@
+import {
+  AlertDialog,
+  AlertDialogAction,
+  AlertDialogCancel,
+  AlertDialogContent,
+  AlertDialogDescription,
+  AlertDialogFooter,
+  AlertDialogHeader,
+  AlertDialogTitle,
+} from "@/components/ui/alert-dialog";
+import {
+  Card,
+  CardContent,
+  CardDescription,
+  CardHeader,
+  CardTitle,
+} from "@/components/ui/card";
+import {
+  ContextMenu,
+  ContextMenuContent,
+  ContextMenuItem,
+  ContextMenuTrigger,
+} from "@/components/ui/context-menu";
+import { Label } from "@/components/ui/label";
+import { Progress } from "@/components/ui/progress";
+import Session from "@/lib/session";
+import Server from "@/types/server";
+import {
+  AppWindow,
+  AppWindowMac,
+  Cpu,
+  HardDrive,
+  MapPin,
+  MemoryStick,
+  Network,
+  Pencil,
+  ServerIcon,
+  Trash,
+  Wifi,
+} from "lucide-react";
+import { redirect } from "next/navigation";
+import { useState } from "react";
+import { toast } from "sonner";
+
+export function UsageBar({
+  value,
+  Icon,
+  text,
+  color,
+}: {
+  value: number;
+  Icon: React.ElementType;
+  text: string;
+  color: string;
+}) {
+  return (
+    <div className="flex flex-col gap-2 w-full">
+      <div className="justify-between flex">
+        <Label className="text-md flex justify-center">
+          <Icon size={16} color={color} /> {text}
+        </Label>
+        <Label className="text-md">{value}%</Label>
+      </div>
+      <Progress value={value} />
+    </div>
+  );
+}
+
+function formatBytes(bytes: number, decimals = 2): string {
+  if (bytes === 0) return "0 Bytes";
+
+  const k = 1024;
+  const dm = decimals < 0 ? 0 : decimals;
+
+  const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
+  const i = Math.floor(Math.log(bytes) / Math.log(k));
+
+  const size = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
+
+  return `${size} ${sizes[i]}`;
+}
+
+export function ByteUnit({
+  value,
+  Icon,
+  text,
+  color,
+}: {
+  value: number;
+  Icon: React.ElementType;
+  text: string;
+  color: string;
+}) {
+  return (
+    <div className="flex flex-col gap-2 w-full">
+      <div className="justify-between flex">
+        <Label className="text-md flex justify-center">
+          <Icon size={16} color={color} /> {text}
+        </Label>
+        <Label className="text-md">{formatBytes(value)}</Label>
+      </div>
+    </div>
+  )
+}
+
+export default function ServiceComponent({
+  server: service,
+  index,
+  session,
+}: {
+  server: { name?: string, description?: string };
+  index: number;
+  session: Session | null;
+}) {
+  const [deleteAlert, setDeleteAlert] = useState(false);
+
+  return (
+    <>
+      <ContextMenu>
+        <ContextMenuTrigger>
+          <Card
+            className="w-full transition-transform duration-500 hover:scale-102 hover:cursor-pointer !select-none"
+            onClick={() => {
+              redirect(`/servers/${index}`);
+            }}
+          >
+            <CardHeader>
+              <div className="flex items-center gap-2 overflow-clip">
+                <div className="bg-muted text-muted-foreground p-1.5 rounded-md">
+                  <AppWindowMac className="w-4.5 h-4.5 text-current my-auto" />
+                </div>
+                <div>
+                  <CardTitle>{service.name}</CardTitle>
+
+                  {(service.description && <CardDescription className="flex justify-between">
+                    {service.description} {(service as any).cpu}
+                  </CardDescription>)}
+                </div>
+              </div>
+            </CardHeader>
+            <CardContent className="w-full flex flex-col gap-3.5">
+              {/* <UsageBar
+                value={service.cpu}
+                Icon={Cpu}
+                text="CPU"
+                color="var(--chart-2)"
+              />
+              <UsageBar
+                value={service.memory}
+                Icon={MemoryStick}
+                text="Memory"
+                color="var(--chart-1)"
+              />
+              <UsageBar
+                value={service.storage}
+                Icon={HardDrive}
+                text="Storage"
+                color="var(--chart-5)"
+              /> */}
+              {/* <div className="flex flex-col gap-2 w-full">
+                <div className="justify-between flex">
+                  <Label className="text-md flex justify-center">
+                    <Network size={16} color="var(--chart-3)" /> Network
+                  </Label>
+                  <Label className="text-md">{formatBytes(service.network)}</Label>
+                </div>
+              </div> */}
+            </CardContent>
+          </Card>
+        </ContextMenuTrigger>
+        <ContextMenuContent>
+          <ContextMenuItem>
+            <Pencil className="text-current" />
+            Edit
+          </ContextMenuItem>
+          <ContextMenuItem
+            className="text-destructive"
+            onClick={() => setDeleteAlert(true)}
+          >
+            <Trash className="text-current" />
+            Delete
+          </ContextMenuItem>
+        </ContextMenuContent>
+      </ContextMenu>
+
+      <AlertDialog open={deleteAlert} onOpenChange={setDeleteAlert}>
+        <AlertDialogContent>
+          <AlertDialogHeader>
+            <AlertDialogTitle>Are you sure?</AlertDialogTitle>
+            <AlertDialogDescription>
+              This action will permanently remove your server from the server
+              list
+            </AlertDialogDescription>
+          </AlertDialogHeader>
+          <AlertDialogFooter>
+            <AlertDialogCancel>Cancel</AlertDialogCancel>
+            <AlertDialogAction
+              className="text-destructive-foreground bg-destructive"
+              onClick={() =>
+                session
+                  ?.removeServer(index)
+                  .then(() => {
+                    toast.info("Server successfully deleted");
+                  })
+                  .catch((e) => {
+                    toast.error(
+                      `Error deleting server: ` + e.response.data.message
+                    );
+                  })
+              }
+            >
+              Delete
+            </AlertDialogAction>
+          </AlertDialogFooter>
+        </AlertDialogContent>
+      </AlertDialog>
+    </>
+  );
+}