From 78bfcf5b1c8afa6300c665afb9141a3f0fb9917c Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 15 Jul 2025 15:11:15 -0700 Subject: [PATCH] Fix some clients address showing issues --- server/routers/site/createSite.ts | 4 +- .../[orgId]/settings/sites/CreateSiteForm.tsx | 462 ------------------ src/app/[orgId]/settings/sites/SitesTable.tsx | 10 +- .../settings/sites/[niceId]/SiteInfoCard.tsx | 10 +- .../[orgId]/settings/sites/create/page.tsx | 85 ++-- 5 files changed, 61 insertions(+), 510 deletions(-) delete mode 100644 src/app/[orgId]/settings/sites/CreateSiteForm.tsx diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index 9f1a6eb5..5d72ee35 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -205,7 +205,7 @@ export async function createSite( exitNodeId, name, niceId, - // address: updatedAddress || null, + address: updatedAddress || null, subnet, type, dockerSocketEnabled: type == "newt", @@ -221,7 +221,7 @@ export async function createSite( orgId, name, niceId, - // address: updatedAddress || null, + address: updatedAddress || null, type, dockerSocketEnabled: type == "newt", subnet: "0.0.0.0/0" diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx deleted file mode 100644 index 3189197b..00000000 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ /dev/null @@ -1,462 +0,0 @@ -"use client"; - -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage -} from "@app/components/ui/form"; -import { Input } from "@app/components/ui/input"; -import { toast } from "@app/hooks/useToast"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; -import { useParams, useRouter } from "next/navigation"; -import { - CreateSiteBody, - CreateSiteResponse, - PickSiteDefaultsResponse -} from "@server/routers/site"; -import { generateKeypair } from "./[niceId]/wireguardConfig"; -import CopyTextBox from "@app/components/CopyTextBox"; -import { Checkbox } from "@app/components/ui/checkbox"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue -} from "@app/components/ui/select"; -import { formatAxiosError } from "@app/lib/api"; -import { createApiClient } from "@app/lib/api"; -import { useEnvContext } from "@app/hooks/useEnvContext"; -import { SiteRow } from "./SitesTable"; -import { AxiosResponse } from "axios"; -import { Button } from "@app/components/ui/button"; -import Link from "next/link"; -import { - ArrowUpRight, - ChevronsUpDown, - Loader2, - SquareArrowOutUpRight -} from "lucide-react"; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger -} from "@app/components/ui/collapsible"; -import LoaderPlaceholder from "@app/components/PlaceHolderLoader"; -import { useTranslations } from "next-intl"; - -type CreateSiteFormProps = { - onCreate?: (site: SiteRow) => void; - setLoading?: (loading: boolean) => void; - setChecked?: (checked: boolean) => void; - orgId: string; -}; - -export default function CreateSiteForm({ - onCreate, - setLoading, - setChecked, - orgId -}: CreateSiteFormProps) { - const api = createApiClient(useEnvContext()); - const { env } = useEnvContext(); - - const [isLoading, setIsLoading] = useState(false); - const [isChecked, setIsChecked] = useState(false); - - const [isOpen, setIsOpen] = useState(false); - - const [keypair, setKeypair] = useState<{ - publicKey: string; - privateKey: string; - } | null>(null); - - const t = useTranslations(); - - const createSiteFormSchema = z.object({ - name: z - .string() - .min(2, { - message: t('nameMin', {len: 2}) - }) - .max(30, { - message: t('nameMax', {len: 30}) - }), - method: z.enum(["wireguard", "newt", "local"]) - }); - - type CreateSiteFormValues = z.infer; - - const defaultValues: Partial = { - name: "", - method: "newt" - }; - - const [siteDefaults, setSiteDefaults] = - useState(null); - - const [loadingPage, setLoadingPage] = useState(true); - - const handleCheckboxChange = (checked: boolean) => { - // setChecked?.(checked); - setIsChecked(checked); - }; - - const form = useForm({ - resolver: zodResolver(createSiteFormSchema), - defaultValues - }); - - const nameField = form.watch("name"); - const methodField = form.watch("method"); - - useEffect(() => { - const nameIsValid = nameField?.length >= 2 && nameField?.length <= 30; - const isFormValid = methodField === "local" || isChecked; - - // Only set checked to true if name is valid AND (method is local OR checkbox is checked) - setChecked?.(nameIsValid && isFormValid); - }, [nameField, methodField, isChecked, setChecked]); - - useEffect(() => { - if (!open) return; - - const load = async () => { - setLoadingPage(true); - // reset all values - setLoading?.(false); - setIsLoading(false); - form.reset(); - setChecked?.(false); - setKeypair(null); - setSiteDefaults(null); - - const generatedKeypair = generateKeypair(); - setKeypair(generatedKeypair); - - await api - .get(`/org/${orgId}/pick-site-defaults`) - .catch((e) => { - // update the default value of the form to be local method - form.setValue("method", "local"); - }) - .then((res) => { - if (res && res.status === 200) { - setSiteDefaults(res.data.data); - } - }); - await new Promise((resolve) => setTimeout(resolve, 200)); - - setLoadingPage(false); - }; - - load(); - }, [open]); - - async function onSubmit(data: CreateSiteFormValues) { - setLoading?.(true); - setIsLoading(true); - let payload: CreateSiteBody = { - name: data.name, - type: data.method - }; - - if (data.method == "wireguard") { - if (!keypair || !siteDefaults) { - toast({ - variant: "destructive", - title: t('siteErrorCreate'), - description: t('siteErrorCreateKeyPair') - }); - setLoading?.(false); - setIsLoading(false); - return; - } - - payload = { - ...payload, - subnet: siteDefaults.subnet, - exitNodeId: siteDefaults.exitNodeId, - pubKey: keypair.publicKey - }; - } - if (data.method === "newt") { - if (!siteDefaults) { - toast({ - variant: "destructive", - title: t('siteErrorCreate'), - description: t('siteErrorCreateDefaults') - }); - setLoading?.(false); - setIsLoading(false); - return; - } - - payload = { - ...payload, - subnet: siteDefaults.subnet, - exitNodeId: siteDefaults.exitNodeId, - secret: siteDefaults.newtSecret, - newtId: siteDefaults.newtId - }; - } - - const res = await api - .put< - AxiosResponse - >(`/org/${orgId}/site/`, payload) - .catch((e) => { - toast({ - variant: "destructive", - title: t('siteErrorCreate'), - description: formatAxiosError(e) - }); - }); - - if (res && res.status === 201) { - const data = res.data.data; - - onCreate?.({ - name: data.name, - id: data.siteId, - nice: data.niceId.toString(), - address: data.address?.split("/")[0], - mbIn: - data.type == "wireguard" || data.type == "newt" - ? t('megabytes', {count: 0}) - : "-", - mbOut: - data.type == "wireguard" || data.type == "newt" - ? t('megabytes', {count: 0}) - : "-", - orgId: orgId as string, - type: data.type as any, - online: false - }); - } - - setLoading?.(false); - setIsLoading(false); - } - - const wgConfig = - keypair && siteDefaults - ? `[Interface] -Address = ${siteDefaults.subnet} -ListenPort = 51820 -PrivateKey = ${keypair.privateKey} - -[Peer] -PublicKey = ${siteDefaults.publicKey} -AllowedIPs = ${siteDefaults.address.split("/")[0]}/32 -Endpoint = ${siteDefaults.endpoint}:${siteDefaults.listenPort} -PersistentKeepalive = 5` - : ""; - - const newtConfig = `newt --id ${siteDefaults?.newtId} --secret ${siteDefaults?.newtSecret} --endpoint ${env.app.dashboardUrl}`; - - const newtConfigDockerCompose = `services: - newt: - image: fosrl/newt - container_name: newt - restart: unless-stopped - environment: - - PANGOLIN_ENDPOINT=${env.app.dashboardUrl} - - NEWT_ID=${siteDefaults?.newtId} - - NEWT_SECRET=${siteDefaults?.newtSecret}`; - - const newtConfigDockerRun = `docker run -dit fosrl/newt --id ${siteDefaults?.newtId} --secret ${siteDefaults?.newtSecret} --endpoint ${env.app.dashboardUrl}`; - - return loadingPage ? ( - - ) : ( -
-
- - ( - - {t('name')} - - - - - - {t('siteNameDescription')} - - - )} - /> - ( - - {t('method')} - - - - - - {t('siteMethodDescription')} - - - )} - /> - - {form.watch("method") === "newt" && ( - - - {t('siteLearnNewt')} - - - - )} - -
- {form.watch("method") === "wireguard" && !isLoading ? ( - <> - - - {t('siteSeeConfigOnce')} - - - ) : form.watch("method") === "wireguard" && - isLoading ? ( -

{t('siteLoadWGConfig')}

- ) : form.watch("method") === "newt" && siteDefaults ? ( - <> -
- -
- -
- - {t('siteSeeConfigOnce')} - -
- - - -
- -
- {t('dockerCompose')} - -
-
- {t('dockerRun')} - - -
-
-
-
- - ) : null} -
- - {form.watch("method") === "local" && ( - - {t('siteLearnLocal')} - - - )} - - {(form.watch("method") === "newt" || - form.watch("method") === "wireguard") && ( -
- - -
- )} - - -
- ); -} diff --git a/src/app/[orgId]/settings/sites/SitesTable.tsx b/src/app/[orgId]/settings/sites/SitesTable.tsx index 668c57ba..8387ab7c 100644 --- a/src/app/[orgId]/settings/sites/SitesTable.tsx +++ b/src/app/[orgId]/settings/sites/SitesTable.tsx @@ -1,6 +1,6 @@ "use client"; -import { ColumnDef } from "@tanstack/react-table"; +import { Column, ColumnDef } from "@tanstack/react-table"; import { SitesDataTable } from "./SitesDataTable"; import { DropdownMenu, @@ -20,7 +20,6 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; import { AxiosResponse } from "axios"; import { useState, useEffect } from "react"; -import CreateSiteForm from "./CreateSiteForm"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; import { toast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/api"; @@ -60,6 +59,7 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) { const api = createApiClient(useEnvContext()); const t = useTranslations(); + const { env } = useEnvContext(); // Update local state when props change (e.g., after refresh) useEffect(() => { @@ -280,9 +280,9 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) { } } }, - { + ...(env.flags.enableClients ? [{ accessorKey: "address", - header: ({ column }) => { + header: ({ column }: { column: Column }) => { return (