on delete cascade for newts to fix delete site
This commit is contained in:
parent
ce5df3b0b9
commit
fb4d27085d
5 changed files with 241 additions and 199 deletions
|
@ -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", {
|
||||
|
|
|
@ -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<CreateSiteFormValues> = {
|
|||
};
|
||||
|
||||
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<PickSiteDefaultsResponse | null>(null);
|
||||
|
||||
const handleCheckboxChange = (checked: boolean) => {
|
||||
setChecked?.(checked);
|
||||
setIsChecked(checked);
|
||||
};
|
||||
|
||||
|
@ -115,10 +98,16 @@ export default function CreateSiteForm({
|
|||
useEffect(() => {
|
||||
if (!open) return;
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
// reset all values
|
||||
setLoading?.(false);
|
||||
setIsLoading(false);
|
||||
form.reset();
|
||||
setChecked?.(false);
|
||||
setKeypair(null);
|
||||
setSiteDefaults(null);
|
||||
|
||||
const generatedKeypair = generateKeypair();
|
||||
setKeypair(generatedKeypair);
|
||||
setIsLoading(false);
|
||||
|
||||
api.get(`/org/${orgId}/pick-site-defaults`)
|
||||
.catch((e) => {
|
||||
|
@ -133,11 +122,11 @@ export default function CreateSiteForm({
|
|||
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,28 +199,6 @@ PersistentKeepalive = 5`
|
|||
const newtConfig = `newt --id ${siteDefaults?.newtId} --secret ${siteDefaults?.newtSecret} --endpoint ${proto}//${siteDefaults?.endpoint}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Credenza
|
||||
open={open}
|
||||
onOpenChange={(val) => {
|
||||
setOpen(val);
|
||||
setLoading(false);
|
||||
|
||||
// reset all values
|
||||
form.reset();
|
||||
setIsChecked(false);
|
||||
setKeypair(null);
|
||||
setSiteDefaults(null);
|
||||
}}
|
||||
>
|
||||
<CredenzaContent>
|
||||
<CredenzaHeader>
|
||||
<CredenzaTitle>Create Site</CredenzaTitle>
|
||||
<CredenzaDescription>
|
||||
Create a new site to start connecting your resources
|
||||
</CredenzaDescription>
|
||||
</CredenzaHeader>
|
||||
<CredenzaBody>
|
||||
<div className="space-y-8">
|
||||
<Form {...form}>
|
||||
<form
|
||||
|
@ -255,8 +220,8 @@ PersistentKeepalive = 5`
|
|||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
This is the name that will
|
||||
be displayed for this site.
|
||||
This is the name that will be displayed for
|
||||
this site.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
@ -271,9 +236,7 @@ PersistentKeepalive = 5`
|
|||
<FormControl>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={
|
||||
field.onChange
|
||||
}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select method" />
|
||||
|
@ -289,44 +252,33 @@ PersistentKeepalive = 5`
|
|||
</Select>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
This is how you will connect
|
||||
your site to Fossorial.
|
||||
This is how you will expose connections.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="max-w-md">
|
||||
{form.watch("method") === "wireguard" &&
|
||||
!isLoading ? (
|
||||
<div className="w-full">
|
||||
{form.watch("method") === "wireguard" && !isLoading ? (
|
||||
<CopyTextBox text={wgConfig} />
|
||||
) : form.watch("method") ===
|
||||
"wireguard" && isLoading ? (
|
||||
<p>
|
||||
Loading WireGuard
|
||||
configuration...
|
||||
</p>
|
||||
) : form.watch("method") === "wireguard" &&
|
||||
isLoading ? (
|
||||
<p>Loading WireGuard configuration...</p>
|
||||
) : (
|
||||
<CopyTextBox
|
||||
text={newtConfig}
|
||||
wrapText={false}
|
||||
/>
|
||||
<CopyTextBox text={newtConfig} wrapText={false} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span className="text-sm text-muted-foreground">
|
||||
You will only be able to see the
|
||||
configuration once.
|
||||
You will only be able to see the configuration once.
|
||||
</span>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
checked={isChecked}
|
||||
onCheckedChange={
|
||||
handleCheckboxChange
|
||||
}
|
||||
onCheckedChange={handleCheckboxChange}
|
||||
/>
|
||||
<label
|
||||
htmlFor="terms"
|
||||
|
@ -338,22 +290,5 @@ PersistentKeepalive = 5`
|
|||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</CredenzaBody>
|
||||
<CredenzaFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
form="create-site-form"
|
||||
loading={loading}
|
||||
disabled={loading || !isChecked}
|
||||
>
|
||||
Create Site
|
||||
</Button>
|
||||
<CredenzaClose asChild>
|
||||
<Button variant="outline">Close</Button>
|
||||
</CredenzaClose>
|
||||
</CredenzaFooter>
|
||||
</CredenzaContent>
|
||||
</Credenza>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<Credenza
|
||||
open={open}
|
||||
onOpenChange={(val) => {
|
||||
setOpen(val);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<CredenzaContent>
|
||||
<CredenzaHeader>
|
||||
<CredenzaTitle>Create Site</CredenzaTitle>
|
||||
<CredenzaDescription>
|
||||
Create a new site to start connecting your resources
|
||||
</CredenzaDescription>
|
||||
</CredenzaHeader>
|
||||
<CredenzaBody>
|
||||
<div className="max-w-md">
|
||||
<CreateSiteForm
|
||||
setLoading={(val) => setLoading(val)}
|
||||
setChecked={(val) => setIsChecked(val)}
|
||||
onCreate={onCreate}
|
||||
orgId={orgId}
|
||||
/>
|
||||
</div>
|
||||
</CredenzaBody>
|
||||
<CredenzaFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
form="create-site-form"
|
||||
loading={loading}
|
||||
disabled={loading || !isChecked}
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
Create Site
|
||||
</Button>
|
||||
<CredenzaClose asChild>
|
||||
<Button variant="outline">Close</Button>
|
||||
</CredenzaClose>
|
||||
</CredenzaFooter>
|
||||
</CredenzaContent>
|
||||
</Credenza>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -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 (
|
||||
<>
|
||||
<CreateSiteForm
|
||||
<CreateSiteFormModal
|
||||
open={isCreateModalOpen}
|
||||
setOpen={setIsCreateModalOpen}
|
||||
onCreate={(val) => {
|
||||
setRows([val, ...rows]);
|
||||
}}
|
||||
orgId={orgId}
|
||||
/>
|
||||
|
||||
{selectedSite && (
|
||||
|
|
|
@ -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<string | null>(null);
|
||||
|
||||
const orgForm = useForm<z.infer<typeof orgSchema>>({
|
||||
|
@ -292,7 +294,18 @@ export default function StepperForm() {
|
|||
)}
|
||||
|
||||
{currentStep === "site" && (
|
||||
<div className="flex justify-end">
|
||||
<div>
|
||||
<CreateSiteForm
|
||||
setLoading={(val) => setLoading(val)}
|
||||
setChecked={(val) => setIsChecked(val)}
|
||||
orgId={orgForm.getValues().orgId}
|
||||
onCreate={() => {
|
||||
router.push(
|
||||
`/${orgForm.getValues().orgId}/settings/resources`
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className="flex justify-between mt-6">
|
||||
<Button
|
||||
type="submit"
|
||||
variant="outline"
|
||||
|
@ -304,6 +317,15 @@ export default function StepperForm() {
|
|||
>
|
||||
Skip for now
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
form="create-site-form"
|
||||
loading={loading}
|
||||
disabled={loading || !isChecked}
|
||||
>
|
||||
Create Site
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
|
Loading…
Add table
Reference in a new issue