From d5d56e79b1c552d528fccac81417e1c9037fe0c2 Mon Sep 17 00:00:00 2001 From: tylen Date: Fri, 16 May 2025 23:48:42 +0300 Subject: [PATCH 1/3] app: make RandomModal a separate component This allows to have smaller context within this feature. The RandomModal handles only the scope that is passed to it. --- app/dash/Dashboard.tsx | 72 +++------------------ app/dash/components/RandomModal.tsx | 98 +++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 62 deletions(-) create mode 100644 app/dash/components/RandomModal.tsx diff --git a/app/dash/Dashboard.tsx b/app/dash/Dashboard.tsx index 93e8b85..11782bd 100644 --- a/app/dash/Dashboard.tsx +++ b/app/dash/Dashboard.tsx @@ -1,12 +1,13 @@ "use client"; import Navbar from "@/components/Navbar"; import ErrorToast from "@/components/Error"; -import { Edit, Plus, Trash, Dice5, Copy, ScanSearch, ChevronRight} from "lucide-react"; +import { Edit, Plus, Trash, Dice5, ScanSearch, ChevronRight} from "lucide-react"; import { useEffect, useState, useMemo } from "react"; import axios from "axios"; import Fuse from "fuse.js"; import Footer from "@/components/Footer" import Cookies from "js-cookie"; +import RandomModal from "./components/RandomModal"; import { SortType, Server, Port } from "@/app/types"; import { compareIp } from "@/app/utils"; @@ -41,7 +42,7 @@ export default function Dashboard() { const [portPort, setPortPort] = useState(null); const [editItem, setEditItem] = useState(null); - const [randomPort, setRandomPort] = useState(null); + const [showRandomModal, setShowRandomModal] = useState(false); const [isScanning, setIsScanning] = useState(false); @@ -251,29 +252,6 @@ const usedPorts = useMemo(() => { return ports; }, [servers]); -const generateRandomPort = () => { - let port; - let attempts = 0; - - do { - port = Math.floor(Math.random() * (65535 - 1024) + 1024); - attempts++; - } while (usedPorts.has(port) && attempts < 1000); - - if (attempts >= 1000) { - handleError("Could not find free port after 1000 attempts"); - return; - } - - setRandomPort(port); - setShowRandomModal(true); -}; - - const copyToClipboard = () => { - if (randomPort !== null) { - navigator.clipboard.writeText(randomPort.toString()); - } -}; const sortedPorts = (ports: Port[]) => [...ports].sort((a, b) => a.port - b.port); @@ -377,48 +355,18 @@ const generateRandomPort = () => { - {showRandomModal && randomPort !== null && ( - -
-
-

Random Port Generator

-

Your allocated port number

-
- -
- - {randomPort} - -
- -
- - - -
-
-
+ {showRandomModal && ( + )} + + + + + + + ); +}; + +export default RandomModal; From 66eea8d003c6a4552c204055ce952d24b8e8ee3b Mon Sep 17 00:00:00 2001 From: tylen Date: Sat, 17 May 2025 01:12:20 +0300 Subject: [PATCH 2/3] RandomModal: make possible to choose server context This change introduces a possibility to set a server context for port generation. --- app/dash/Dashboard.tsx | 1 + app/dash/components/RandomModal.tsx | 99 ++++++++++++++++++++--------- app/utils.ts | 32 +++++++++- 3 files changed, 101 insertions(+), 31 deletions(-) diff --git a/app/dash/Dashboard.tsx b/app/dash/Dashboard.tsx index 11782bd..16499d7 100644 --- a/app/dash/Dashboard.tsx +++ b/app/dash/Dashboard.tsx @@ -365,6 +365,7 @@ const usedPorts = useMemo(() => { )} diff --git a/app/dash/components/RandomModal.tsx b/app/dash/components/RandomModal.tsx index 1a1c8f3..0880f16 100644 --- a/app/dash/components/RandomModal.tsx +++ b/app/dash/components/RandomModal.tsx @@ -1,52 +1,68 @@ "use client"; +import { Server } from "@/app/types"; +import { + generateRandomPortWithServerContext, + generateRandomPortWithUsedPortsContext, +} from "@/app/utils"; import { Copy } from "lucide-react"; +import { useEffect, useState } from "react"; type RandomModalProps = { setShowRandomModal: (show: boolean) => void; globalErrorHandler: (message: string) => void; + availableServers: Server[]; usedPorts: Set; }; const copyPortToClipboard = (randomPort: number | null) => { if (randomPort !== null) { navigator.clipboard.writeText(randomPort.toString()); - } else { } }; -const generateRandomPort = ( - usedPorts: Set, - maxRetries: number -): number | null => { - let port; - let attempts = 0; - - do { - port = Math.floor(Math.random() * (65535 - 1024) + 1024); - attempts++; - } while (usedPorts.has(port) && attempts < maxRetries); - - if (attempts >= maxRetries) { - return null; - } - - return port; -}; - const RandomModal = (props: RandomModalProps) => { - const { setShowRandomModal, usedPorts, globalErrorHandler } = props; - const generationRetryAttempts = 1000; - const randomPort: number | null = generateRandomPort( + const { + setShowRandomModal, + availableServers, usedPorts, - generationRetryAttempts + globalErrorHandler, + } = props; + const generationRetryAttempts = 1000; + const [randomPort, setRandomPort] = useState( + generateRandomPortWithUsedPortsContext(usedPorts, generationRetryAttempts) + ); + const [selectedServerId, setselectedServerId] = useState( + undefined ); - if (randomPort === null) { - setShowRandomModal(false); - globalErrorHandler( - "Could not find free port after " + generationRetryAttempts + " attempts" + const handleServerContextChange = (serverId: number) => { + if (!serverId) { + setRandomPort( + generateRandomPortWithUsedPortsContext(usedPorts, generationRetryAttempts) + ); + setselectedServerId(undefined); + return + } + setselectedServerId(serverId); + const selectedServer = availableServers.find( + (server) => server.id === serverId ); - } + setRandomPort( + generateRandomPortWithServerContext( + selectedServer, + generationRetryAttempts + ) + ); + }; + + useEffect(() => { + if (!randomPort) { + globalErrorHandler( + "Could not find free port after " + generationRetryAttempts + " attempts" + ); + setShowRandomModal(false); + } + }, [randomPort, globalErrorHandler]); return ( <> @@ -60,13 +76,36 @@ const RandomModal = (props: RandomModalProps) => {

Random Port Generator

+ + {availableServers.length != 0 && ( + + )} +

Your allocated port number

-
{randomPort} + {!selectedServerId && availableServers.length != 0 && ( +

+ Port generated without specific server context in mind +

+ )}
diff --git a/app/utils.ts b/app/utils.ts index 896116d..3923357 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -1,3 +1,5 @@ +import { Server } from "@/app/types"; + export const compareIp = (a: string, b: string) => { const pa = a.split('.').map(n => parseInt(n, 10)); const pb = b.split('.').map(n => parseInt(n, 10)); @@ -6,4 +8,32 @@ export const compareIp = (a: string, b: string) => { if (diff !== 0) return diff; } return 0; - }; \ No newline at end of file + }; + +const generateRandomPort = (): number => { + return Math.floor(Math.random() * (65535 - 1024) + 1024); +}; + +export const findAvailablePort = (usedPorts: Set, maxRetries: number): number | null => { + let port; + let attempts = 0; + + do { + port = generateRandomPort(); + attempts++; + } while (usedPorts.has(port) && attempts < maxRetries); + + return attempts < maxRetries ? port : null; +}; + +export const generateRandomPortWithUsedPortsContext = (usedPorts: Set, maxRetries: number): number | null => { + return findAvailablePort(usedPorts, maxRetries); +}; + +export const generateRandomPortWithServerContext = (serverContext: Server | undefined, maxRetries: number): number | null => { + if (!serverContext) { + return null; + } + const serverPorts = new Set(serverContext.ports.map(e => e.port)); // Use a Set for faster lookup + return findAvailablePort(serverPorts, maxRetries); +}; \ No newline at end of file From fc8e4c817e555285d667405200e57cf4d9d45186 Mon Sep 17 00:00:00 2001 From: tylen Date: Sat, 17 May 2025 01:31:31 +0300 Subject: [PATCH 3/3] RandomModal: add possibility to regenerate port Add a button to regenerate a port with selected context, or without. --- app/dash/components/RandomModal.tsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/app/dash/components/RandomModal.tsx b/app/dash/components/RandomModal.tsx index 0880f16..9876731 100644 --- a/app/dash/components/RandomModal.tsx +++ b/app/dash/components/RandomModal.tsx @@ -4,7 +4,7 @@ import { generateRandomPortWithServerContext, generateRandomPortWithUsedPortsContext, } from "@/app/utils"; -import { Copy } from "lucide-react"; +import { Copy, RefreshCcw } from "lucide-react"; import { useEffect, useState } from "react"; type RandomModalProps = { @@ -55,6 +55,19 @@ const RandomModal = (props: RandomModalProps) => { ); }; + const handleRegeneratePort = () => { + if (selectedServerId) { + handleServerContextChange(selectedServerId); + } else { + setRandomPort( + generateRandomPortWithUsedPortsContext( + usedPorts, + generationRetryAttempts + ) + ); + } + }; + useEffect(() => { if (!randomPort) { globalErrorHandler( @@ -109,6 +122,15 @@ const RandomModal = (props: RandomModalProps) => {
+