chore: remove application delete volume

This commit is contained in:
djknaeckebrot
2024-12-23 08:36:18 +01:00
parent be2e70a17e
commit 375decebb2
3 changed files with 1113 additions and 1148 deletions

View File

@@ -1,22 +1,21 @@
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter, DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { import {
Form, Form,
FormControl, FormControl,
FormField, FormField,
FormItem, FormItem,
FormLabel, FormLabel,
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
@@ -29,158 +28,134 @@ import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
const deleteApplicationSchema = z.object({ const deleteApplicationSchema = z.object({
projectName: z.string().min(1, { projectName: z.string().min(1, {
message: "Application name is required", message: "Application name is required",
}), }),
deleteVolumes: z.boolean(),
}); });
type DeleteApplication = z.infer<typeof deleteApplicationSchema>; type DeleteApplication = z.infer<typeof deleteApplicationSchema>;
interface Props { interface Props {
applicationId: string; applicationId: string;
} }
export const DeleteApplication = ({ applicationId }: Props) => { export const DeleteApplication = ({ applicationId }: Props) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const { mutateAsync, isLoading } = api.application.delete.useMutation(); const { mutateAsync, isLoading } = api.application.delete.useMutation();
const { data } = api.application.one.useQuery( const { data } = api.application.one.useQuery(
{ applicationId }, { applicationId },
{ enabled: !!applicationId }, { enabled: !!applicationId }
); );
const { push } = useRouter(); const { push } = useRouter();
const form = useForm<DeleteApplication>({ const form = useForm<DeleteApplication>({
defaultValues: { defaultValues: {
projectName: "", projectName: "",
deleteVolumes: false, },
}, resolver: zodResolver(deleteApplicationSchema),
resolver: zodResolver(deleteApplicationSchema), });
});
const onSubmit = async (formData: DeleteApplication) => { const onSubmit = async (formData: DeleteApplication) => {
const expectedName = `${data?.name}/${data?.appName}`; const expectedName = `${data?.name}/${data?.appName}`;
if (formData.projectName === expectedName) { if (formData.projectName === expectedName) {
await mutateAsync({ await mutateAsync({
applicationId, applicationId,
deleteVolumes: formData.deleteVolumes, })
}) .then((data) => {
.then((data) => { push(`/dashboard/project/${data?.projectId}`);
push(`/dashboard/project/${data?.projectId}`); toast.success("Application deleted successfully");
toast.success("Application deleted successfully"); setIsOpen(false);
setIsOpen(false); })
}) .catch(() => {
.catch(() => { toast.error("Error deleting the application");
toast.error("Error deleting the application"); });
}); } else {
} else { form.setError("projectName", {
form.setError("projectName", { message: "Project name does not match",
message: "Project name does not match", });
}); }
} };
};
return ( return (
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}> <Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground" /> <TrashIcon className="size-4 text-muted-foreground" />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
<DialogHeader> <DialogHeader>
<DialogTitle>Are you absolutely sure?</DialogTitle> <DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogDescription> <DialogDescription>
This action cannot be undone. This will permanently delete the This action cannot be undone. This will permanently delete the
application. If you are sure please enter the application name to application. If you are sure please enter the application name to
delete this application. delete this application.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="grid gap-4"> <div className="grid gap-4">
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(onSubmit)} onSubmit={form.handleSubmit(onSubmit)}
id="hook-form-delete-application" id="hook-form-delete-application"
className="grid w-full gap-4" className="grid w-full gap-4"
> >
<FormField <FormField
control={form.control} control={form.control}
name="projectName" name="projectName"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="flex items-center gap-2"> <FormLabel className="flex items-center gap-2">
<span> <span>
To confirm, type{" "} To confirm, type{" "}
<Badge <Badge
className="p-2 rounded-md ml-1 mr-1 hover:border-primary hover:text-primary-foreground hover:bg-primary hover:cursor-pointer" className="p-2 rounded-md ml-1 mr-1 hover:border-primary hover:text-primary-foreground hover:bg-primary hover:cursor-pointer"
variant="outline" variant="outline"
onClick={() => { onClick={() => {
if (data?.name && data?.appName) { if (data?.name && data?.appName) {
navigator.clipboard.writeText( navigator.clipboard.writeText(
`${data.name}/${data.appName}`, `${data.name}/${data.appName}`
); );
toast.success("Copied to clipboard. Be careful!"); toast.success("Copied to clipboard. Be careful!");
} }
}} }}
> >
{data?.name}/{data?.appName}&nbsp; {data?.name}/{data?.appName}&nbsp;
<Copy className="h-4 w-4 ml-1 text-muted-foreground" /> <Copy className="h-4 w-4 ml-1 text-muted-foreground" />
</Badge>{" "} </Badge>{" "}
in the box below: in the box below:
</span> </span>
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder="Enter application name to confirm" placeholder="Enter application name to confirm"
{...field} {...field}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField </form>
control={form.control} </Form>
name="deleteVolumes" </div>
render={({ field }) => ( <DialogFooter>
<FormItem> <Button
<div className="flex items-center"> variant="secondary"
<FormControl> onClick={() => {
<Checkbox setIsOpen(false);
checked={field.value} }}
onCheckedChange={field.onChange} >
/> Cancel
</FormControl> </Button>
<Button
<FormLabel className="ml-2"> isLoading={isLoading}
Delete volumes associated with this compose form="hook-form-delete-application"
</FormLabel> type="submit"
</div> variant="destructive"
<FormMessage /> >
</FormItem> Confirm
)} </Button>
/> </DialogFooter>
</form> </DialogContent>
</Form> </Dialog>
</div> );
<DialogFooter>
<Button
variant="secondary"
onClick={() => {
setIsOpen(false);
}}
>
Cancel
</Button>
<Button
isLoading={isLoading}
form="hook-form-delete-application"
type="submit"
variant="destructive"
>
Confirm
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
import { relations } from "drizzle-orm"; import { relations } from "drizzle-orm";
import { import {
boolean, boolean,
integer, integer,
json, json,
pgEnum, pgEnum,
pgTable, pgTable,
text, text,
} from "drizzle-orm/pg-core"; } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod"; import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
@@ -28,498 +28,493 @@ import { sshKeys } from "./ssh-key";
import { generateAppName } from "./utils"; import { generateAppName } from "./utils";
export const sourceType = pgEnum("sourceType", [ export const sourceType = pgEnum("sourceType", [
"docker", "docker",
"git", "git",
"github", "github",
"gitlab", "gitlab",
"bitbucket", "bitbucket",
"drop", "drop",
]); ]);
export const buildType = pgEnum("buildType", [ export const buildType = pgEnum("buildType", [
"dockerfile", "dockerfile",
"heroku_buildpacks", "heroku_buildpacks",
"paketo_buildpacks", "paketo_buildpacks",
"nixpacks", "nixpacks",
"static", "static",
]); ]);
// TODO: refactor this types // TODO: refactor this types
export interface HealthCheckSwarm { export interface HealthCheckSwarm {
Test?: string[] | undefined; Test?: string[] | undefined;
Interval?: number | undefined; Interval?: number | undefined;
Timeout?: number | undefined; Timeout?: number | undefined;
StartPeriod?: number | undefined; StartPeriod?: number | undefined;
Retries?: number | undefined; Retries?: number | undefined;
} }
export interface RestartPolicySwarm { export interface RestartPolicySwarm {
Condition?: string | undefined; Condition?: string | undefined;
Delay?: number | undefined; Delay?: number | undefined;
MaxAttempts?: number | undefined; MaxAttempts?: number | undefined;
Window?: number | undefined; Window?: number | undefined;
} }
export interface PlacementSwarm { export interface PlacementSwarm {
Constraints?: string[] | undefined; Constraints?: string[] | undefined;
Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined; Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined;
MaxReplicas?: number | undefined; MaxReplicas?: number | undefined;
Platforms?: Platforms?:
| Array<{ | Array<{
Architecture: string; Architecture: string;
OS: string; OS: string;
}> }>
| undefined; | undefined;
} }
export interface UpdateConfigSwarm { export interface UpdateConfigSwarm {
Parallelism: number; Parallelism: number;
Delay?: number | undefined; Delay?: number | undefined;
FailureAction?: string | undefined; FailureAction?: string | undefined;
Monitor?: number | undefined; Monitor?: number | undefined;
MaxFailureRatio?: number | undefined; MaxFailureRatio?: number | undefined;
Order: string; Order: string;
} }
export interface ServiceModeSwarm { export interface ServiceModeSwarm {
Replicated?: { Replicas?: number | undefined } | undefined; Replicated?: { Replicas?: number | undefined } | undefined;
Global?: {} | undefined; Global?: {} | undefined;
ReplicatedJob?: ReplicatedJob?:
| { | {
MaxConcurrent?: number | undefined; MaxConcurrent?: number | undefined;
TotalCompletions?: number | undefined; TotalCompletions?: number | undefined;
} }
| undefined; | undefined;
GlobalJob?: {} | undefined; GlobalJob?: {} | undefined;
} }
export interface NetworkSwarm { export interface NetworkSwarm {
Target?: string | undefined; Target?: string | undefined;
Aliases?: string[] | undefined; Aliases?: string[] | undefined;
DriverOpts?: { [key: string]: string } | undefined; DriverOpts?: { [key: string]: string } | undefined;
} }
export interface LabelsSwarm { export interface LabelsSwarm {
[name: string]: string; [name: string]: string;
} }
export const applications = pgTable("application", { export const applications = pgTable("application", {
applicationId: text("applicationId") applicationId: text("applicationId")
.notNull() .notNull()
.primaryKey() .primaryKey()
.$defaultFn(() => nanoid()), .$defaultFn(() => nanoid()),
name: text("name").notNull(), name: text("name").notNull(),
appName: text("appName") appName: text("appName")
.notNull() .notNull()
.$defaultFn(() => generateAppName("app")) .$defaultFn(() => generateAppName("app"))
.unique(), .unique(),
description: text("description"), description: text("description"),
env: text("env"), env: text("env"),
previewEnv: text("previewEnv"), previewEnv: text("previewEnv"),
previewBuildArgs: text("previewBuildArgs"), previewBuildArgs: text("previewBuildArgs"),
previewWildcard: text("previewWildcard"), previewWildcard: text("previewWildcard"),
previewPort: integer("previewPort").default(3000), previewPort: integer("previewPort").default(3000),
previewHttps: boolean("previewHttps").notNull().default(false), previewHttps: boolean("previewHttps").notNull().default(false),
previewPath: text("previewPath").default("/"), previewPath: text("previewPath").default("/"),
previewCertificateType: certificateType("certificateType") previewCertificateType: certificateType("certificateType")
.notNull() .notNull()
.default("none"), .default("none"),
previewLimit: integer("previewLimit").default(3), previewLimit: integer("previewLimit").default(3),
isPreviewDeploymentsActive: boolean("isPreviewDeploymentsActive").default( isPreviewDeploymentsActive: boolean("isPreviewDeploymentsActive").default(
false, false
), ),
buildArgs: text("buildArgs"), buildArgs: text("buildArgs"),
memoryReservation: integer("memoryReservation"), memoryReservation: integer("memoryReservation"),
memoryLimit: integer("memoryLimit"), memoryLimit: integer("memoryLimit"),
cpuReservation: integer("cpuReservation"), cpuReservation: integer("cpuReservation"),
cpuLimit: integer("cpuLimit"), cpuLimit: integer("cpuLimit"),
title: text("title"), title: text("title"),
enabled: boolean("enabled"), enabled: boolean("enabled"),
subtitle: text("subtitle"), subtitle: text("subtitle"),
command: text("command"), command: text("command"),
refreshToken: text("refreshToken").$defaultFn(() => nanoid()), refreshToken: text("refreshToken").$defaultFn(() => nanoid()),
sourceType: sourceType("sourceType").notNull().default("github"), sourceType: sourceType("sourceType").notNull().default("github"),
// Github // Github
repository: text("repository"), repository: text("repository"),
owner: text("owner"), owner: text("owner"),
branch: text("branch"), branch: text("branch"),
buildPath: text("buildPath").default("/"), buildPath: text("buildPath").default("/"),
autoDeploy: boolean("autoDeploy").$defaultFn(() => true), autoDeploy: boolean("autoDeploy").$defaultFn(() => true),
// Gitlab // Gitlab
gitlabProjectId: integer("gitlabProjectId"), gitlabProjectId: integer("gitlabProjectId"),
gitlabRepository: text("gitlabRepository"), gitlabRepository: text("gitlabRepository"),
gitlabOwner: text("gitlabOwner"), gitlabOwner: text("gitlabOwner"),
gitlabBranch: text("gitlabBranch"), gitlabBranch: text("gitlabBranch"),
gitlabBuildPath: text("gitlabBuildPath").default("/"), gitlabBuildPath: text("gitlabBuildPath").default("/"),
gitlabPathNamespace: text("gitlabPathNamespace"), gitlabPathNamespace: text("gitlabPathNamespace"),
// Bitbucket // Bitbucket
bitbucketRepository: text("bitbucketRepository"), bitbucketRepository: text("bitbucketRepository"),
bitbucketOwner: text("bitbucketOwner"), bitbucketOwner: text("bitbucketOwner"),
bitbucketBranch: text("bitbucketBranch"), bitbucketBranch: text("bitbucketBranch"),
bitbucketBuildPath: text("bitbucketBuildPath").default("/"), bitbucketBuildPath: text("bitbucketBuildPath").default("/"),
// Docker // Docker
username: text("username"), username: text("username"),
password: text("password"), password: text("password"),
dockerImage: text("dockerImage"), dockerImage: text("dockerImage"),
registryUrl: text("registryUrl"), registryUrl: text("registryUrl"),
// Git // Git
customGitUrl: text("customGitUrl"), customGitUrl: text("customGitUrl"),
customGitBranch: text("customGitBranch"), customGitBranch: text("customGitBranch"),
customGitBuildPath: text("customGitBuildPath"), customGitBuildPath: text("customGitBuildPath"),
customGitSSHKeyId: text("customGitSSHKeyId").references( customGitSSHKeyId: text("customGitSSHKeyId").references(
() => sshKeys.sshKeyId, () => sshKeys.sshKeyId,
{ {
onDelete: "set null", onDelete: "set null",
}, }
), ),
dockerfile: text("dockerfile"), dockerfile: text("dockerfile"),
dockerContextPath: text("dockerContextPath"), dockerContextPath: text("dockerContextPath"),
dockerBuildStage: text("dockerBuildStage"), dockerBuildStage: text("dockerBuildStage"),
// Drop // Drop
dropBuildPath: text("dropBuildPath"), dropBuildPath: text("dropBuildPath"),
// Docker swarm json // Docker swarm json
healthCheckSwarm: json("healthCheckSwarm").$type<HealthCheckSwarm>(), healthCheckSwarm: json("healthCheckSwarm").$type<HealthCheckSwarm>(),
restartPolicySwarm: json("restartPolicySwarm").$type<RestartPolicySwarm>(), restartPolicySwarm: json("restartPolicySwarm").$type<RestartPolicySwarm>(),
placementSwarm: json("placementSwarm").$type<PlacementSwarm>(), placementSwarm: json("placementSwarm").$type<PlacementSwarm>(),
updateConfigSwarm: json("updateConfigSwarm").$type<UpdateConfigSwarm>(), updateConfigSwarm: json("updateConfigSwarm").$type<UpdateConfigSwarm>(),
rollbackConfigSwarm: json("rollbackConfigSwarm").$type<UpdateConfigSwarm>(), rollbackConfigSwarm: json("rollbackConfigSwarm").$type<UpdateConfigSwarm>(),
modeSwarm: json("modeSwarm").$type<ServiceModeSwarm>(), modeSwarm: json("modeSwarm").$type<ServiceModeSwarm>(),
labelsSwarm: json("labelsSwarm").$type<LabelsSwarm>(), labelsSwarm: json("labelsSwarm").$type<LabelsSwarm>(),
networkSwarm: json("networkSwarm").$type<NetworkSwarm[]>(), networkSwarm: json("networkSwarm").$type<NetworkSwarm[]>(),
// //
replicas: integer("replicas").default(1).notNull(), replicas: integer("replicas").default(1).notNull(),
applicationStatus: applicationStatus("applicationStatus") applicationStatus: applicationStatus("applicationStatus")
.notNull() .notNull()
.default("idle"), .default("idle"),
buildType: buildType("buildType").notNull().default("nixpacks"), buildType: buildType("buildType").notNull().default("nixpacks"),
herokuVersion: text("herokuVersion").default("24"), herokuVersion: text("herokuVersion").default("24"),
publishDirectory: text("publishDirectory"), publishDirectory: text("publishDirectory"),
createdAt: text("createdAt") createdAt: text("createdAt")
.notNull() .notNull()
.$defaultFn(() => new Date().toISOString()), .$defaultFn(() => new Date().toISOString()),
registryId: text("registryId").references(() => registry.registryId, { registryId: text("registryId").references(() => registry.registryId, {
onDelete: "set null", onDelete: "set null",
}), }),
projectId: text("projectId") projectId: text("projectId")
.notNull() .notNull()
.references(() => projects.projectId, { onDelete: "cascade" }), .references(() => projects.projectId, { onDelete: "cascade" }),
githubId: text("githubId").references(() => github.githubId, { githubId: text("githubId").references(() => github.githubId, {
onDelete: "set null", onDelete: "set null",
}), }),
gitlabId: text("gitlabId").references(() => gitlab.gitlabId, { gitlabId: text("gitlabId").references(() => gitlab.gitlabId, {
onDelete: "set null", onDelete: "set null",
}), }),
bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, { bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, {
onDelete: "set null", onDelete: "set null",
}), }),
serverId: text("serverId").references(() => server.serverId, { serverId: text("serverId").references(() => server.serverId, {
onDelete: "cascade", onDelete: "cascade",
}), }),
}); });
export const applicationsRelations = relations( export const applicationsRelations = relations(
applications, applications,
({ one, many }) => ({ ({ one, many }) => ({
project: one(projects, { project: one(projects, {
fields: [applications.projectId], fields: [applications.projectId],
references: [projects.projectId], references: [projects.projectId],
}), }),
deployments: many(deployments), deployments: many(deployments),
customGitSSHKey: one(sshKeys, { customGitSSHKey: one(sshKeys, {
fields: [applications.customGitSSHKeyId], fields: [applications.customGitSSHKeyId],
references: [sshKeys.sshKeyId], references: [sshKeys.sshKeyId],
}), }),
domains: many(domains), domains: many(domains),
mounts: many(mounts), mounts: many(mounts),
redirects: many(redirects), redirects: many(redirects),
security: many(security), security: many(security),
ports: many(ports), ports: many(ports),
registry: one(registry, { registry: one(registry, {
fields: [applications.registryId], fields: [applications.registryId],
references: [registry.registryId], references: [registry.registryId],
}), }),
github: one(github, { github: one(github, {
fields: [applications.githubId], fields: [applications.githubId],
references: [github.githubId], references: [github.githubId],
}), }),
gitlab: one(gitlab, { gitlab: one(gitlab, {
fields: [applications.gitlabId], fields: [applications.gitlabId],
references: [gitlab.gitlabId], references: [gitlab.gitlabId],
}), }),
bitbucket: one(bitbucket, { bitbucket: one(bitbucket, {
fields: [applications.bitbucketId], fields: [applications.bitbucketId],
references: [bitbucket.bitbucketId], references: [bitbucket.bitbucketId],
}), }),
server: one(server, { server: one(server, {
fields: [applications.serverId], fields: [applications.serverId],
references: [server.serverId], references: [server.serverId],
}), }),
previewDeployments: many(previewDeployments), previewDeployments: many(previewDeployments),
}), })
); );
const HealthCheckSwarmSchema = z const HealthCheckSwarmSchema = z
.object({ .object({
Test: z.array(z.string()).optional(), Test: z.array(z.string()).optional(),
Interval: z.number().optional(), Interval: z.number().optional(),
Timeout: z.number().optional(), Timeout: z.number().optional(),
StartPeriod: z.number().optional(), StartPeriod: z.number().optional(),
Retries: z.number().optional(), Retries: z.number().optional(),
}) })
.strict(); .strict();
const RestartPolicySwarmSchema = z const RestartPolicySwarmSchema = z
.object({ .object({
Condition: z.string().optional(), Condition: z.string().optional(),
Delay: z.number().optional(), Delay: z.number().optional(),
MaxAttempts: z.number().optional(), MaxAttempts: z.number().optional(),
Window: z.number().optional(), Window: z.number().optional(),
}) })
.strict(); .strict();
const PreferenceSchema = z const PreferenceSchema = z
.object({ .object({
Spread: z.object({ Spread: z.object({
SpreadDescriptor: z.string(), SpreadDescriptor: z.string(),
}), }),
}) })
.strict(); .strict();
const PlatformSchema = z const PlatformSchema = z
.object({ .object({
Architecture: z.string(), Architecture: z.string(),
OS: z.string(), OS: z.string(),
}) })
.strict(); .strict();
const PlacementSwarmSchema = z const PlacementSwarmSchema = z
.object({ .object({
Constraints: z.array(z.string()).optional(), Constraints: z.array(z.string()).optional(),
Preferences: z.array(PreferenceSchema).optional(), Preferences: z.array(PreferenceSchema).optional(),
MaxReplicas: z.number().optional(), MaxReplicas: z.number().optional(),
Platforms: z.array(PlatformSchema).optional(), Platforms: z.array(PlatformSchema).optional(),
}) })
.strict(); .strict();
const UpdateConfigSwarmSchema = z const UpdateConfigSwarmSchema = z
.object({ .object({
Parallelism: z.number(), Parallelism: z.number(),
Delay: z.number().optional(), Delay: z.number().optional(),
FailureAction: z.string().optional(), FailureAction: z.string().optional(),
Monitor: z.number().optional(), Monitor: z.number().optional(),
MaxFailureRatio: z.number().optional(), MaxFailureRatio: z.number().optional(),
Order: z.string(), Order: z.string(),
}) })
.strict(); .strict();
const ReplicatedSchema = z const ReplicatedSchema = z
.object({ .object({
Replicas: z.number().optional(), Replicas: z.number().optional(),
}) })
.strict(); .strict();
const ReplicatedJobSchema = z const ReplicatedJobSchema = z
.object({ .object({
MaxConcurrent: z.number().optional(), MaxConcurrent: z.number().optional(),
TotalCompletions: z.number().optional(), TotalCompletions: z.number().optional(),
}) })
.strict(); .strict();
const ServiceModeSwarmSchema = z const ServiceModeSwarmSchema = z
.object({ .object({
Replicated: ReplicatedSchema.optional(), Replicated: ReplicatedSchema.optional(),
Global: z.object({}).optional(), Global: z.object({}).optional(),
ReplicatedJob: ReplicatedJobSchema.optional(), ReplicatedJob: ReplicatedJobSchema.optional(),
GlobalJob: z.object({}).optional(), GlobalJob: z.object({}).optional(),
}) })
.strict(); .strict();
const NetworkSwarmSchema = z.array( const NetworkSwarmSchema = z.array(
z z
.object({ .object({
Target: z.string().optional(), Target: z.string().optional(),
Aliases: z.array(z.string()).optional(), Aliases: z.array(z.string()).optional(),
DriverOpts: z.object({}).optional(), DriverOpts: z.object({}).optional(),
}) })
.strict(), .strict()
); );
const LabelsSwarmSchema = z.record(z.string()); const LabelsSwarmSchema = z.record(z.string());
const createSchema = createInsertSchema(applications, { const createSchema = createInsertSchema(applications, {
appName: z.string(), appName: z.string(),
createdAt: z.string(), createdAt: z.string(),
applicationId: z.string(), applicationId: z.string(),
autoDeploy: z.boolean(), autoDeploy: z.boolean(),
env: z.string().optional(), env: z.string().optional(),
buildArgs: z.string().optional(), buildArgs: z.string().optional(),
name: z.string().min(1), name: z.string().min(1),
description: z.string().optional(), description: z.string().optional(),
memoryReservation: z.number().optional(), memoryReservation: z.number().optional(),
memoryLimit: z.number().optional(), memoryLimit: z.number().optional(),
cpuReservation: z.number().optional(), cpuReservation: z.number().optional(),
cpuLimit: z.number().optional(), cpuLimit: z.number().optional(),
title: z.string().optional(), title: z.string().optional(),
enabled: z.boolean().optional(), enabled: z.boolean().optional(),
subtitle: z.string().optional(), subtitle: z.string().optional(),
dockerImage: z.string().optional(), dockerImage: z.string().optional(),
username: z.string().optional(), username: z.string().optional(),
isPreviewDeploymentsActive: z.boolean().optional(), isPreviewDeploymentsActive: z.boolean().optional(),
password: z.string().optional(), password: z.string().optional(),
registryUrl: z.string().optional(), registryUrl: z.string().optional(),
customGitSSHKeyId: z.string().optional(), customGitSSHKeyId: z.string().optional(),
repository: z.string().optional(), repository: z.string().optional(),
dockerfile: z.string().optional(), dockerfile: z.string().optional(),
branch: z.string().optional(), branch: z.string().optional(),
customGitBranch: z.string().optional(), customGitBranch: z.string().optional(),
customGitBuildPath: z.string().optional(), customGitBuildPath: z.string().optional(),
customGitUrl: z.string().optional(), customGitUrl: z.string().optional(),
buildPath: z.string().optional(), buildPath: z.string().optional(),
projectId: z.string(), projectId: z.string(),
sourceType: z.enum(["github", "docker", "git"]).optional(), sourceType: z.enum(["github", "docker", "git"]).optional(),
applicationStatus: z.enum(["idle", "running", "done", "error"]), applicationStatus: z.enum(["idle", "running", "done", "error"]),
buildType: z.enum([ buildType: z.enum([
"dockerfile", "dockerfile",
"heroku_buildpacks", "heroku_buildpacks",
"paketo_buildpacks", "paketo_buildpacks",
"nixpacks", "nixpacks",
"static", "static",
]), ]),
herokuVersion: z.string().optional(), herokuVersion: z.string().optional(),
publishDirectory: z.string().optional(), publishDirectory: z.string().optional(),
owner: z.string(), owner: z.string(),
healthCheckSwarm: HealthCheckSwarmSchema.nullable(), healthCheckSwarm: HealthCheckSwarmSchema.nullable(),
restartPolicySwarm: RestartPolicySwarmSchema.nullable(), restartPolicySwarm: RestartPolicySwarmSchema.nullable(),
placementSwarm: PlacementSwarmSchema.nullable(), placementSwarm: PlacementSwarmSchema.nullable(),
updateConfigSwarm: UpdateConfigSwarmSchema.nullable(), updateConfigSwarm: UpdateConfigSwarmSchema.nullable(),
rollbackConfigSwarm: UpdateConfigSwarmSchema.nullable(), rollbackConfigSwarm: UpdateConfigSwarmSchema.nullable(),
modeSwarm: ServiceModeSwarmSchema.nullable(), modeSwarm: ServiceModeSwarmSchema.nullable(),
labelsSwarm: LabelsSwarmSchema.nullable(), labelsSwarm: LabelsSwarmSchema.nullable(),
networkSwarm: NetworkSwarmSchema.nullable(), networkSwarm: NetworkSwarmSchema.nullable(),
previewPort: z.number().optional(), previewPort: z.number().optional(),
previewEnv: z.string().optional(), previewEnv: z.string().optional(),
previewBuildArgs: z.string().optional(), previewBuildArgs: z.string().optional(),
previewWildcard: z.string().optional(), previewWildcard: z.string().optional(),
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"]).optional(),
}); });
export const apiCreateApplication = createSchema.pick({ export const apiCreateApplication = createSchema.pick({
name: true, name: true,
appName: true, appName: true,
description: true, description: true,
projectId: true, projectId: true,
serverId: true, serverId: true,
}); });
export const apiFindOneApplication = createSchema export const apiFindOneApplication = createSchema
.pick({ .pick({
applicationId: true, applicationId: true,
}) })
.required(); .required();
export const apiReloadApplication = createSchema export const apiReloadApplication = createSchema
.pick({ .pick({
appName: true, appName: true,
applicationId: true, applicationId: true,
}) })
.required(); .required();
export const apiSaveBuildType = createSchema export const apiSaveBuildType = createSchema
.pick({ .pick({
applicationId: true, applicationId: true,
buildType: true, buildType: true,
dockerfile: true, dockerfile: true,
dockerContextPath: true, dockerContextPath: true,
dockerBuildStage: true, dockerBuildStage: true,
herokuVersion: true, herokuVersion: true,
}) })
.required() .required()
.merge(createSchema.pick({ publishDirectory: true })); .merge(createSchema.pick({ publishDirectory: true }));
export const apiSaveGithubProvider = createSchema export const apiSaveGithubProvider = createSchema
.pick({ .pick({
applicationId: true, applicationId: true,
repository: true, repository: true,
branch: true, branch: true,
owner: true, owner: true,
buildPath: true, buildPath: true,
githubId: true, githubId: true,
}) })
.required(); .required();
export const apiSaveGitlabProvider = createSchema export const apiSaveGitlabProvider = createSchema
.pick({ .pick({
applicationId: true, applicationId: true,
gitlabBranch: true, gitlabBranch: true,
gitlabBuildPath: true, gitlabBuildPath: true,
gitlabOwner: true, gitlabOwner: true,
gitlabRepository: true, gitlabRepository: true,
gitlabId: true, gitlabId: true,
gitlabProjectId: true, gitlabProjectId: true,
gitlabPathNamespace: true, gitlabPathNamespace: true,
}) })
.required(); .required();
export const apiSaveBitbucketProvider = createSchema export const apiSaveBitbucketProvider = createSchema
.pick({ .pick({
bitbucketBranch: true, bitbucketBranch: true,
bitbucketBuildPath: true, bitbucketBuildPath: true,
bitbucketOwner: true, bitbucketOwner: true,
bitbucketRepository: true, bitbucketRepository: true,
bitbucketId: true, bitbucketId: true,
applicationId: true, applicationId: true,
}) })
.required(); .required();
export const apiSaveDockerProvider = createSchema export const apiSaveDockerProvider = createSchema
.pick({ .pick({
dockerImage: true, dockerImage: true,
applicationId: true, applicationId: true,
username: true, username: true,
password: true, password: true,
registryUrl: true, registryUrl: true,
}) })
.required(); .required();
export const apiSaveGitProvider = createSchema export const apiSaveGitProvider = createSchema
.pick({ .pick({
customGitBranch: true, customGitBranch: true,
applicationId: true, applicationId: true,
customGitBuildPath: true, customGitBuildPath: true,
customGitUrl: true, customGitUrl: true,
}) })
.required() .required()
.merge( .merge(
createSchema.pick({ createSchema.pick({
customGitSSHKeyId: true, customGitSSHKeyId: true,
}), })
); );
export const apiSaveEnvironmentVariables = createSchema export const apiSaveEnvironmentVariables = createSchema
.pick({ .pick({
applicationId: true, applicationId: true,
env: true, env: true,
buildArgs: true, buildArgs: true,
}) })
.required(); .required();
export const apiFindMonitoringStats = createSchema export const apiFindMonitoringStats = createSchema
.pick({ .pick({
appName: true, appName: true,
}) })
.required(); .required();
export const apiUpdateApplication = createSchema export const apiUpdateApplication = createSchema
.partial() .partial()
.extend({ .extend({
applicationId: z.string().min(1), applicationId: z.string().min(1),
}) })
.omit({ serverId: true }); .omit({ serverId: true });
export const apiDeleteApplication = z.object({
applicationId: z.string().min(1),
deleteVolumes: z.boolean(),
});