feat(preview): add custom certificate type for preview deployments

This commit is contained in:
Mauricio Siu
2025-03-08 21:16:18 -06:00
parent e4197d6565
commit de48c81192
8 changed files with 10341 additions and 15 deletions

View File

@@ -35,16 +35,30 @@ import { useForm } from "react-hook-form";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
const schema = z.object({ const schema = z
env: z.string(), .object({
buildArgs: z.string(), env: z.string(),
wildcardDomain: z.string(), buildArgs: z.string(),
port: z.number(), wildcardDomain: z.string(),
previewLimit: z.number(), port: z.number(),
previewHttps: z.boolean(), previewLimit: z.number(),
previewPath: z.string(), previewHttps: z.boolean(),
previewCertificateType: z.enum(["letsencrypt", "none"]), previewPath: z.string(),
}); previewCertificateType: z.enum(["letsencrypt", "none", "custom"]),
previewCustomCertResolver: z.string().optional(),
})
.superRefine((input, ctx) => {
if (
input.previewCertificateType === "custom" &&
!input.previewCustomCertResolver
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["previewCustomCertResolver"],
message: "Required",
});
}
});
type Schema = z.infer<typeof schema>; type Schema = z.infer<typeof schema>;
@@ -90,6 +104,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
previewHttps: data.previewHttps || false, previewHttps: data.previewHttps || false,
previewPath: data.previewPath || "/", previewPath: data.previewPath || "/",
previewCertificateType: data.previewCertificateType || "none", previewCertificateType: data.previewCertificateType || "none",
previewCustomCertResolver: data.previewCustomCertResolver || "",
}); });
} }
}, [data]); }, [data]);
@@ -105,6 +120,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
previewHttps: formData.previewHttps, previewHttps: formData.previewHttps,
previewPath: formData.previewPath, previewPath: formData.previewPath,
previewCertificateType: formData.previewCertificateType, previewCertificateType: formData.previewCertificateType,
previewCustomCertResolver: formData.previewCustomCertResolver,
}) })
.then(() => { .then(() => {
toast.success("Preview Deployments settings updated"); toast.success("Preview Deployments settings updated");
@@ -184,10 +200,6 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Preview Limit</FormLabel> <FormLabel>Preview Limit</FormLabel>
{/* <FormDescription>
Set the limit of preview deployments that can be
created for this app.
</FormDescription> */}
<FormControl> <FormControl>
<NumberInput placeholder="3000" {...field} /> <NumberInput placeholder="3000" {...field} />
</FormControl> </FormControl>
@@ -238,6 +250,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
<SelectItem value={"letsencrypt"}> <SelectItem value={"letsencrypt"}>
Let's Encrypt Let's Encrypt
</SelectItem> </SelectItem>
<SelectItem value={"custom"}>Custom</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />
@@ -245,6 +258,25 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => {
)} )}
/> />
)} )}
{form.watch("previewCertificateType") === "custom" && (
<FormField
control={form.control}
name="previewCustomCertResolver"
render={({ field }) => (
<FormItem>
<FormLabel>Certificate Provider</FormLabel>
<FormControl>
<Input
placeholder="my-custom-resolver"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
</div> </div>
<div className="grid gap-4 lg:grid-cols-2"> <div className="grid gap-4 lg:grid-cols-2">
<div className="flex flex-row items-center justify-between rounded-lg border p-4 col-span-2"> <div className="flex flex-row items-center justify-between rounded-lg border p-4 col-span-2">

View File

@@ -0,0 +1 @@
ALTER TABLE "application" ADD COLUMN "previewCertificateProvider" text;

View File

@@ -0,0 +1 @@
ALTER TABLE "application" RENAME COLUMN "previewCertificateProvider" TO "previewCustomCertResolver";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -512,6 +512,20 @@
"when": 1741487009559, "when": 1741487009559,
"tag": "0072_green_susan_delgado", "tag": "0072_green_susan_delgado",
"breakpoints": true "breakpoints": true
},
{
"idx": 73,
"version": "7",
"when": 1741489681190,
"tag": "0073_hot_domino",
"breakpoints": true
},
{
"idx": 74,
"version": "7",
"when": 1741490064139,
"tag": "0074_black_quasar",
"breakpoints": true
} }
] ]
} }

View File

@@ -124,6 +124,7 @@ export const applications = pgTable("application", {
previewCertificateType: certificateType("certificateType") previewCertificateType: certificateType("certificateType")
.notNull() .notNull()
.default("none"), .default("none"),
previewCustomCertResolver: text("previewCustomCertResolver"),
previewLimit: integer("previewLimit").default(3), previewLimit: integer("previewLimit").default(3),
isPreviewDeploymentsActive: boolean("isPreviewDeploymentsActive").default( isPreviewDeploymentsActive: boolean("isPreviewDeploymentsActive").default(
false, false,
@@ -404,7 +405,7 @@ const createSchema = createInsertSchema(applications, {
previewLimit: z.number().optional(), previewLimit: z.number().optional(),
previewHttps: z.boolean().optional(), previewHttps: z.boolean().optional(),
previewPath: z.string().optional(), previewPath: z.string().optional(),
previewCertificateType: z.enum(["letsencrypt", "none"]).optional(), previewCertificateType: z.enum(["letsencrypt", "none", "custom"]).optional(),
}); });
export const apiCreateApplication = createSchema.pick({ export const apiCreateApplication = createSchema.pick({

View File

@@ -203,6 +203,7 @@ export const createPreviewDeployment = async (
port: application.previewPort, port: application.previewPort,
https: application.previewHttps, https: application.previewHttps,
certificateType: application.previewCertificateType, certificateType: application.previewCertificateType,
customCertResolver: application.previewCustomCertResolver,
domainType: "preview", domainType: "preview",
previewDeploymentId: previewDeployment.previewDeploymentId, previewDeploymentId: previewDeployment.previewDeploymentId,
}); });