From fb4d27085d085c1747755fcee4d02ea937eadcd6 Mon Sep 17 00:00:00 2001 From: Milo Schwartz Date: Sat, 21 Dec 2024 21:50:30 -0500 Subject: [PATCH] on delete cascade for newts to fix delete site --- server/db/schema.ts | 4 +- .../sites/components/CreateSiteForm.tsx | 309 +++++++----------- .../sites/components/CreateSiteModal.tsx | 80 +++++ .../settings/sites/components/SitesTable.tsx | 7 +- src/app/setup/page.tsx | 40 ++- 5 files changed, 241 insertions(+), 199 deletions(-) create mode 100644 src/app/[orgId]/settings/sites/components/CreateSiteModal.tsx diff --git a/server/db/schema.ts b/server/db/schema.ts index f7bc47e3..5ab72289 100644 --- a/server/db/schema.ts +++ b/server/db/schema.ts @@ -96,7 +96,9 @@ export const newts = sqliteTable("newt", { newtId: text("id").primaryKey(), secretHash: text("secretHash").notNull(), dateCreated: text("dateCreated").notNull(), - siteId: integer("siteId").references(() => sites.siteId) + siteId: integer("siteId").references(() => sites.siteId, { + onDelete: "cascade" + }) }); export const twoFactorBackupCodes = sqliteTable("twoFactorBackupCodes", { diff --git a/src/app/[orgId]/settings/sites/components/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/components/CreateSiteForm.tsx index b26c6f4d..6a5d3c17 100644 --- a/src/app/[orgId]/settings/sites/components/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/components/CreateSiteForm.tsx @@ -1,6 +1,5 @@ "use client"; -import { Button, buttonVariants } from "@app/components/ui/button"; import { Form, FormControl, @@ -16,17 +15,6 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; -import { - Credenza, - CredenzaBody, - CredenzaClose, - CredenzaContent, - CredenzaDescription, - CredenzaFooter, - CredenzaHeader, - CredenzaTitle -} from "@app/components/Credenza"; -import { useOrgContext } from "@app/hooks/useOrgContext"; import { useParams, useRouter } from "next/navigation"; import { CreateSiteBody, @@ -49,11 +37,6 @@ import { useEnvContext } from "@app/hooks/useEnvContext"; import { SiteRow } from "./SitesTable"; import { AxiosResponse } from "axios"; -const method = [ - { label: "Newt", value: "newt" }, - { label: "WireGuard", value: "wireguard" } -] as const; - const createSiteFormSchema = z.object({ name: z .string() @@ -74,36 +57,36 @@ const defaultValues: Partial = { }; type CreateSiteFormProps = { - open: boolean; - setOpen: (open: boolean) => void; onCreate?: (site: SiteRow) => void; + setLoading?: (loading: boolean) => void; + setChecked?: (checked: boolean) => void; + orgId: string; }; export default function CreateSiteForm({ - open, - setOpen, - onCreate + onCreate, + setLoading, + setChecked, + orgId }: CreateSiteFormProps) { const { toast } = useToast(); const api = createApiClient(useEnvContext()); - const [loading, setLoading] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [isChecked, setIsChecked] = useState(false); - const params = useParams(); - const orgId = params.orgId; const router = useRouter(); const [keypair, setKeypair] = useState<{ publicKey: string; privateKey: string; } | null>(null); - const [isLoading, setIsLoading] = useState(true); - const [isChecked, setIsChecked] = useState(false); const [siteDefaults, setSiteDefaults] = useState(null); const handleCheckboxChange = (checked: boolean) => { + setChecked?.(checked); setIsChecked(checked); }; @@ -115,29 +98,35 @@ export default function CreateSiteForm({ useEffect(() => { if (!open) return; - if (typeof window !== "undefined") { - const generatedKeypair = generateKeypair(); - setKeypair(generatedKeypair); - setIsLoading(false); + // reset all values + setLoading?.(false); + setIsLoading(false); + form.reset(); + setChecked?.(false); + setKeypair(null); + setSiteDefaults(null); - api.get(`/org/${orgId}/pick-site-defaults`) - .catch((e) => { - toast({ - variant: "destructive", - title: "Error picking site defaults", - description: formatAxiosError(e) - }); - }) - .then((res) => { - if (res && res.status === 200) { - setSiteDefaults(res.data.data); - } + const generatedKeypair = generateKeypair(); + setKeypair(generatedKeypair); + + api.get(`/org/${orgId}/pick-site-defaults`) + .catch((e) => { + toast({ + variant: "destructive", + title: "Error picking site defaults", + description: formatAxiosError(e) }); - } + }) + .then((res) => { + if (res && res.status === 200) { + setSiteDefaults(res.data.data); + } + }); }, [open]); async function onSubmit(data: CreateSiteFormValues) { - setLoading(true); + setLoading?.(true); + setIsLoading(true); if (!siteDefaults || !keypair) { return; } @@ -169,9 +158,6 @@ export default function CreateSiteForm({ // navigate to the site page // router.push(`/${orgId}/settings/sites/${niceId}`); - // close the modal - setOpen(false); - const data = res.data.data; onCreate?.({ @@ -186,7 +172,8 @@ export default function CreateSiteForm({ }); } - setLoading(false); + setLoading?.(false); + setIsLoading(false); } const wgConfig = @@ -212,148 +199,96 @@ PersistentKeepalive = 5` const newtConfig = `newt --id ${siteDefaults?.newtId} --secret ${siteDefaults?.newtSecret} --endpoint ${proto}//${siteDefaults?.endpoint}`; return ( - <> - { - setOpen(val); - setLoading(false); - - // reset all values - form.reset(); - setIsChecked(false); - setKeypair(null); - setSiteDefaults(null); - }} - > - - - Create Site - - Create a new site to start connecting your resources - - - -
-
- - ( - - Name - - - - - This is the name that will - be displayed for this site. - - - - )} - /> - ( - - Method - - - - - This is how you will connect - your site to Fossorial. - - - - )} +
+ + + ( + + Name + + + + + This is the name that will be displayed for + this site. + + + + )} + /> + ( + + Method + + + + + This is how you will expose connections. + + + + )} + /> -
- {form.watch("method") === "wireguard" && - !isLoading ? ( - - ) : form.watch("method") === - "wireguard" && isLoading ? ( -

- Loading WireGuard - configuration... -

- ) : ( - - )} -
+
+ {form.watch("method") === "wireguard" && !isLoading ? ( + + ) : form.watch("method") === "wireguard" && + isLoading ? ( +

Loading WireGuard configuration...

+ ) : ( + + )} +
- - You will only be able to see the - configuration once. - + + You will only be able to see the configuration once. + -
- - -
- - -
- - - - - - - - - - + I have copied the config + +
+ + + ); } diff --git a/src/app/[orgId]/settings/sites/components/CreateSiteModal.tsx b/src/app/[orgId]/settings/sites/components/CreateSiteModal.tsx new file mode 100644 index 00000000..fd6ff914 --- /dev/null +++ b/src/app/[orgId]/settings/sites/components/CreateSiteModal.tsx @@ -0,0 +1,80 @@ +"use client"; + +import { Button } from "@app/components/ui/button"; +import { useState } from "react"; +import { + Credenza, + CredenzaBody, + CredenzaClose, + CredenzaContent, + CredenzaDescription, + CredenzaFooter, + CredenzaHeader, + CredenzaTitle +} from "@app/components/Credenza"; +import { SiteRow } from "./SitesTable"; +import CreateSiteForm from "./CreateSiteForm"; + +type CreateSiteFormProps = { + open: boolean; + setOpen: (open: boolean) => void; + onCreate?: (site: SiteRow) => void; + orgId: string; +}; + +export default function CreateSiteFormModal({ + open, + setOpen, + onCreate, + orgId +}: CreateSiteFormProps) { + const [loading, setLoading] = useState(false); + const [isChecked, setIsChecked] = useState(false); + + return ( + <> + { + setOpen(val); + setLoading(false); + }} + > + + + Create Site + + Create a new site to start connecting your resources + + + +
+ setLoading(val)} + setChecked={(val) => setIsChecked(val)} + onCreate={onCreate} + orgId={orgId} + /> +
+
+ + + + + + +
+
+ + ); +} diff --git a/src/app/[orgId]/settings/sites/components/SitesTable.tsx b/src/app/[orgId]/settings/sites/components/SitesTable.tsx index 335b54c4..a36a0723 100644 --- a/src/app/[orgId]/settings/sites/components/SitesTable.tsx +++ b/src/app/[orgId]/settings/sites/components/SitesTable.tsx @@ -26,6 +26,7 @@ import { useToast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/utils"; import { createApiClient } from "@app/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; +import CreateSiteFormModal from "./CreateSiteModal"; export type SiteRow = { id: number; @@ -68,6 +69,8 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) { .then(() => { router.refresh(); setIsDeleteModalOpen(false); + + const newRows = rows.filter((row) => row.id !== siteId); }); }; @@ -188,7 +191,6 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) { }, cell: ({ row }) => { const originalRow = row.original; - console.log(originalRow.online); if (originalRow.online) { return ( @@ -257,12 +259,13 @@ export default function SitesTable({ sites, orgId }: SitesTableProps) { return ( <> - { setRows([val, ...rows]); }} + orgId={orgId} /> {selectedSite && ( diff --git a/src/app/setup/page.tsx b/src/app/setup/page.tsx index 8bb574e1..17aad703 100644 --- a/src/app/setup/page.tsx +++ b/src/app/setup/page.tsx @@ -32,6 +32,7 @@ import { FormMessage } from "@app/components/ui/form"; import { Alert, AlertDescription } from "@app/components/ui/alert"; +import CreateSiteForm from "../[orgId]/settings/sites/components/CreateSiteForm"; type Step = "org" | "site" | "resources"; @@ -45,6 +46,7 @@ export default function StepperForm() { const [orgIdTaken, setOrgIdTaken] = useState(false); const [loading, setLoading] = useState(false); + const [isChecked, setIsChecked] = useState(false); const [error, setError] = useState(null); const orgForm = useForm>({ @@ -292,18 +294,38 @@ export default function StepperForm() { )} {currentStep === "site" && ( -
- + /> +
+ + +
)}