diff --git a/components/dashboard/application/domains/add-domain.tsx b/components/dashboard/application/domains/add-domain.tsx index 623779dd..76899a7c 100644 --- a/components/dashboard/application/domains/add-domain.tsx +++ b/components/dashboard/application/domains/add-domain.tsx @@ -28,23 +28,14 @@ import { } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; -import { z } from "zod"; -const hostnameRegex = /^[a-zA-Z0-9][a-zA-Z0-9\.-]*\.[a-zA-Z]{2,}$/; -const domain = z.object({ - host: z.string().regex(hostnameRegex, { message: "Invalid hostname" }), - path: z.string().min(1), - port: z - .number() - .min(1, { message: "Port must be at least 1" }) - .max(65535, { message: "Port must be 65535 or below" }), - https: z.boolean(), - certificateType: z.enum(["letsencrypt", "none"]), -}); +import { domain } from "@/server/db/validations"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { flushSync } from "react-dom"; +import type z from "zod"; type Domain = z.infer; @@ -74,16 +65,7 @@ export const AddDomain = ({ ? api.domain.update.useMutation() : api.domain.create.useMutation(); - const defaultValues: Domain = { - host: "", - https: false, - path: "/", - port: 3000, - certificateType: "none", - }; - const form = useForm({ - defaultValues, resolver: zodResolver(domain), }); @@ -91,8 +73,9 @@ export const AddDomain = ({ if (data) { form.reset({ ...data, - path: data.path || defaultValues.path, - port: data.port || defaultValues.port, + /* Convert null to undefined */ + path: data.path || undefined, + port: data.port || undefined, }); } }, [form, form.reset, data]); @@ -120,11 +103,19 @@ export const AddDomain = ({ applicationId, }); await utils.application.readTraefikConfig.invalidate({ applicationId }); + + /* + Reset form if it was a new domain + Flushsync is needed for a bug witht he react-hook-form reset method + https://github.com/orgs/react-hook-form/discussions/7589#discussioncomment-10060621 + */ + if (!domainId) { + flushSync(() => form.reset()); + } setIsOpen(false); }) .catch(() => { toast.error(dictionary.error); - setIsOpen(false); }); }; return ( @@ -239,6 +230,7 @@ export const AddDomain = ({ Automatically provision SSL Certificate. + ({ references: [applications.applicationId], }), })); -const hostnameRegex = /^[a-zA-Z0-9][a-zA-Z0-9\.-]*\.[a-zA-Z]{2,}$/; -const createSchema = createInsertSchema(domains, { - domainId: z.string().min(1), - host: z.string().min(1), - path: z.string().min(1), - port: z.number(), - https: z.boolean(), - applicationId: z.string(), - certificateType: z.enum(["letsencrypt", "none"]), -}); -export const apiCreateDomain = createSchema - .pick({ - host: true, - path: true, - port: true, - https: true, - applicationId: true, - certificateType: true, - }) - .required(); +const createSchema = createInsertSchema(domains, domain._def.schema.shape); + +export const apiCreateDomain = createSchema.pick({ + host: true, + path: true, + port: true, + https: true, + applicationId: true, + certificateType: true, +}); export const apiFindDomain = createSchema .pick({ @@ -59,19 +49,16 @@ export const apiFindDomain = createSchema }) .required(); -export const apiFindDomainByApplication = createSchema - .pick({ - applicationId: true, - }) - .required(); +export const apiFindDomainByApplication = createSchema.pick({ + applicationId: true, +}); export const apiUpdateDomain = createSchema .pick({ - domainId: true, host: true, path: true, port: true, https: true, certificateType: true, }) - .required(); + .merge(createSchema.pick({ domainId: true }).required()); diff --git a/server/db/validations/index.ts b/server/db/validations/index.ts new file mode 100644 index 00000000..fcb6117a --- /dev/null +++ b/server/db/validations/index.ts @@ -0,0 +1,25 @@ +import { z } from "zod"; + +export const domain = z + .object({ + host: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9\.-]*\.[a-zA-Z]{2,}$/, { + message: "Invalid hostname", + }), + path: z.string().min(1).optional(), + port: z + .number() + .min(1, { message: "Port must be at least 1" }) + .max(65535, { message: "Port must be 65535 or below" }) + .optional(), + https: z.boolean().optional(), + certificateType: z.enum(["letsencrypt", "none"]).optional(), + }) + .superRefine((input, ctx) => { + if (input.https && !input.certificateType) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ["certificateType"], + message: "Required", + }); + } + });