diff --git a/apps/dokploy/components/dashboard/application/delete-application.tsx b/apps/dokploy/components/dashboard/application/delete-application.tsx index ff63ef5c..7202b6f1 100644 --- a/apps/dokploy/components/dashboard/application/delete-application.tsx +++ b/apps/dokploy/components/dashboard/application/delete-application.tsx @@ -1,22 +1,21 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { Checkbox } from "@/components/ui/checkbox"; import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; @@ -29,158 +28,134 @@ import { toast } from "sonner"; import { z } from "zod"; const deleteApplicationSchema = z.object({ - projectName: z.string().min(1, { - message: "Application name is required", - }), - deleteVolumes: z.boolean(), + projectName: z.string().min(1, { + message: "Application name is required", + }), }); type DeleteApplication = z.infer; interface Props { - applicationId: string; + applicationId: string; } export const DeleteApplication = ({ applicationId }: Props) => { - const [isOpen, setIsOpen] = useState(false); - const { mutateAsync, isLoading } = api.application.delete.useMutation(); - const { data } = api.application.one.useQuery( - { applicationId }, - { enabled: !!applicationId }, - ); - const { push } = useRouter(); - const form = useForm({ - defaultValues: { - projectName: "", - deleteVolumes: false, - }, - resolver: zodResolver(deleteApplicationSchema), - }); + const [isOpen, setIsOpen] = useState(false); + const { mutateAsync, isLoading } = api.application.delete.useMutation(); + const { data } = api.application.one.useQuery( + { applicationId }, + { enabled: !!applicationId } + ); + const { push } = useRouter(); + const form = useForm({ + defaultValues: { + projectName: "", + }, + resolver: zodResolver(deleteApplicationSchema), + }); - const onSubmit = async (formData: DeleteApplication) => { - const expectedName = `${data?.name}/${data?.appName}`; - if (formData.projectName === expectedName) { - await mutateAsync({ - applicationId, - deleteVolumes: formData.deleteVolumes, - }) - .then((data) => { - push(`/dashboard/project/${data?.projectId}`); - toast.success("Application deleted successfully"); - setIsOpen(false); - }) - .catch(() => { - toast.error("Error deleting the application"); - }); - } else { - form.setError("projectName", { - message: "Project name does not match", - }); - } - }; + const onSubmit = async (formData: DeleteApplication) => { + const expectedName = `${data?.name}/${data?.appName}`; + if (formData.projectName === expectedName) { + await mutateAsync({ + applicationId, + }) + .then((data) => { + push(`/dashboard/project/${data?.projectId}`); + toast.success("Application deleted successfully"); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error deleting the application"); + }); + } else { + form.setError("projectName", { + message: "Project name does not match", + }); + } + }; - return ( - - - - - - - Are you absolutely sure? - - This action cannot be undone. This will permanently delete the - application. If you are sure please enter the application name to - delete this application. - - -
-
- - ( - - - - To confirm, type{" "} - { - if (data?.name && data?.appName) { - navigator.clipboard.writeText( - `${data.name}/${data.appName}`, - ); - toast.success("Copied to clipboard. Be careful!"); - } - }} - > - {data?.name}/{data?.appName}  - - {" "} - in the box below: - - - - - - - - )} - /> - ( - -
- - - - - - Delete volumes associated with this compose - -
- -
- )} - /> - - -
- - - - -
-
- ); + return ( + + + + + + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete the + application. If you are sure please enter the application name to + delete this application. + + +
+
+ + ( + + + + To confirm, type{" "} + { + if (data?.name && data?.appName) { + navigator.clipboard.writeText( + `${data.name}/${data.appName}` + ); + toast.success("Copied to clipboard. Be careful!"); + } + }} + > + {data?.name}/{data?.appName}  + + {" "} + in the box below: + + + + + + + + )} + /> + + +
+ + + + +
+
+ ); }; diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts index 9b16d579..e2fe4cb4 100644 --- a/apps/dokploy/server/api/routers/application.ts +++ b/apps/dokploy/server/api/routers/application.ts @@ -1,55 +1,54 @@ import { - createTRPCRouter, - protectedProcedure, - uploadProcedure, + createTRPCRouter, + protectedProcedure, + uploadProcedure, } from "@/server/api/trpc"; import { db } from "@/server/db"; import { - apiCreateApplication, - apiDeleteApplication, - apiFindMonitoringStats, - apiFindOneApplication, - apiReloadApplication, - apiSaveBitbucketProvider, - apiSaveBuildType, - apiSaveDockerProvider, - apiSaveEnvironmentVariables, - apiSaveGitProvider, - apiSaveGithubProvider, - apiSaveGitlabProvider, - apiUpdateApplication, - applications, + apiCreateApplication, + apiFindMonitoringStats, + apiFindOneApplication, + apiReloadApplication, + apiSaveBitbucketProvider, + apiSaveBuildType, + apiSaveDockerProvider, + apiSaveEnvironmentVariables, + apiSaveGitProvider, + apiSaveGithubProvider, + apiSaveGitlabProvider, + apiUpdateApplication, + applications, } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { cleanQueuesByApplication, myQueue } from "@/server/queues/queueSetup"; import { deploy } from "@/server/utils/deploy"; import { uploadFileSchema } from "@/utils/schema"; import { - IS_CLOUD, - addNewService, - checkServiceAccess, - createApplication, - deleteAllMiddlewares, - findApplicationById, - findProjectById, - getApplicationStats, - readConfig, - readRemoteConfig, - removeDeployments, - removeDirectoryCode, - removeMonitoringDirectory, - removeService, - removeTraefikConfig, - startService, - startServiceRemote, - stopService, - stopServiceRemote, - unzipDrop, - updateApplication, - updateApplicationStatus, - writeConfig, - writeConfigRemote, - // uploadFileSchema + IS_CLOUD, + addNewService, + checkServiceAccess, + createApplication, + deleteAllMiddlewares, + findApplicationById, + findProjectById, + getApplicationStats, + readConfig, + readRemoteConfig, + removeDeployments, + removeDirectoryCode, + removeMonitoringDirectory, + removeService, + removeTraefikConfig, + startService, + startServiceRemote, + stopService, + stopServiceRemote, + unzipDrop, + updateApplication, + updateApplicationStatus, + writeConfig, + writeConfigRemote, + // uploadFileSchema } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; @@ -57,573 +56,569 @@ import { nanoid } from "nanoid"; import { z } from "zod"; export const applicationRouter = createTRPCRouter({ - create: protectedProcedure - .input(apiCreateApplication) - .mutation(async ({ input, ctx }) => { - try { - if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); - } + create: protectedProcedure + .input(apiCreateApplication) + .mutation(async ({ input, ctx }) => { + try { + if (ctx.user.rol === "user") { + await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + } - if (IS_CLOUD && !input.serverId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You need to use a server to create an application", - }); - } + if (IS_CLOUD && !input.serverId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You need to use a server to create an application", + }); + } - const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this project", - }); - } - const newApplication = await createApplication(input); + const project = await findProjectById(input.projectId); + if (project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this project", + }); + } + const newApplication = await createApplication(input); - if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, newApplication.applicationId); - } - return newApplication; - } catch (error: unknown) { - if (error instanceof TRPCError) { - throw error; - } - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error to create the application", - cause: error, - }); - } - }), - one: protectedProcedure - .input(apiFindOneApplication) - .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - await checkServiceAccess( - ctx.user.authId, - input.applicationId, - "access", - ); - } - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this application", - }); - } - return application; - }), + if (ctx.user.rol === "user") { + await addNewService(ctx.user.authId, newApplication.applicationId); + } + return newApplication; + } catch (error: unknown) { + if (error instanceof TRPCError) { + throw error; + } + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error to create the application", + cause: error, + }); + } + }), + one: protectedProcedure + .input(apiFindOneApplication) + .query(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + await checkServiceAccess( + ctx.user.authId, + input.applicationId, + "access" + ); + } + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this application", + }); + } + return application; + }), - reload: protectedProcedure - .input(apiReloadApplication) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to reload this application", - }); - } - if (application.serverId) { - await stopServiceRemote(application.serverId, input.appName); - } else { - await stopService(input.appName); - } - await updateApplicationStatus(input.applicationId, "idle"); + reload: protectedProcedure + .input(apiReloadApplication) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to reload this application", + }); + } + if (application.serverId) { + await stopServiceRemote(application.serverId, input.appName); + } else { + await stopService(input.appName); + } + await updateApplicationStatus(input.applicationId, "idle"); - if (application.serverId) { - await startServiceRemote(application.serverId, input.appName); - } else { - await startService(input.appName); - } - await updateApplicationStatus(input.applicationId, "done"); - return true; - }), + if (application.serverId) { + await startServiceRemote(application.serverId, input.appName); + } else { + await startService(input.appName); + } + await updateApplicationStatus(input.applicationId, "done"); + return true; + }), - delete: protectedProcedure - .input(apiDeleteApplication) - .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - await checkServiceAccess( - ctx.user.authId, - input.applicationId, - "delete", - ); - } - const application = await findApplicationById(input.applicationId); + delete: protectedProcedure + .input(apiFindOneApplication) + .mutation(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + await checkServiceAccess( + ctx.user.authId, + input.applicationId, + "delete" + ); + } + const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to delete this application", - }); - } + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to delete this application", + }); + } - const result = await db - .delete(applications) - .where(eq(applications.applicationId, input.applicationId)) - .returning(); + const result = await db + .delete(applications) + .where(eq(applications.applicationId, input.applicationId)) + .returning(); - const cleanupOperations = [ - async () => await deleteAllMiddlewares(application), - async () => await removeDeployments(application), - async () => - await removeDirectoryCode(application.appName, application.serverId), - async () => - await removeMonitoringDirectory( - application.appName, - application.serverId, - ), - async () => - await removeTraefikConfig(application.appName, application.serverId), - async () => - await removeService( - application?.appName, - application.serverId, - input.deleteVolumes, - ), - ]; + const cleanupOperations = [ + async () => await deleteAllMiddlewares(application), + async () => await removeDeployments(application), + async () => + await removeDirectoryCode(application.appName, application.serverId), + async () => + await removeMonitoringDirectory( + application.appName, + application.serverId + ), + async () => + await removeTraefikConfig(application.appName, application.serverId), + async () => + await removeService(application?.appName, application.serverId), + ]; - for (const operation of cleanupOperations) { - try { - await operation(); - } catch (error) {} - } + for (const operation of cleanupOperations) { + try { + await operation(); + } catch (error) {} + } - return result[0]; - }), + return result[0]; + }), - stop: protectedProcedure - .input(apiFindOneApplication) - .mutation(async ({ input, ctx }) => { - const service = await findApplicationById(input.applicationId); - if (service.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to stop this application", - }); - } - if (service.serverId) { - await stopServiceRemote(service.serverId, service.appName); - } else { - await stopService(service.appName); - } - await updateApplicationStatus(input.applicationId, "idle"); + stop: protectedProcedure + .input(apiFindOneApplication) + .mutation(async ({ input, ctx }) => { + const service = await findApplicationById(input.applicationId); + if (service.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to stop this application", + }); + } + if (service.serverId) { + await stopServiceRemote(service.serverId, service.appName); + } else { + await stopService(service.appName); + } + await updateApplicationStatus(input.applicationId, "idle"); - return service; - }), + return service; + }), - start: protectedProcedure - .input(apiFindOneApplication) - .mutation(async ({ input, ctx }) => { - const service = await findApplicationById(input.applicationId); - if (service.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to start this application", - }); - } + start: protectedProcedure + .input(apiFindOneApplication) + .mutation(async ({ input, ctx }) => { + const service = await findApplicationById(input.applicationId); + if (service.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to start this application", + }); + } - if (service.serverId) { - await startServiceRemote(service.serverId, service.appName); - } else { - await startService(service.appName); - } - await updateApplicationStatus(input.applicationId, "done"); + if (service.serverId) { + await startServiceRemote(service.serverId, service.appName); + } else { + await startService(service.appName); + } + await updateApplicationStatus(input.applicationId, "done"); - return service; - }), + return service; + }), - redeploy: protectedProcedure - .input(apiFindOneApplication) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to redeploy this application", - }); - } - const jobData: DeploymentJob = { - applicationId: input.applicationId, - titleLog: "Rebuild deployment", - descriptionLog: "", - type: "redeploy", - applicationType: "application", - server: !!application.serverId, - }; + redeploy: protectedProcedure + .input(apiFindOneApplication) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to redeploy this application", + }); + } + const jobData: DeploymentJob = { + applicationId: input.applicationId, + titleLog: "Rebuild deployment", + descriptionLog: "", + type: "redeploy", + applicationType: "application", + server: !!application.serverId, + }; - if (IS_CLOUD && application.serverId) { - jobData.serverId = application.serverId; - await deploy(jobData); - return true; - } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - }, - ); - }), - saveEnvironment: protectedProcedure - .input(apiSaveEnvironmentVariables) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to save this environment", - }); - } - await updateApplication(input.applicationId, { - env: input.env, - buildArgs: input.buildArgs, - }); - return true; - }), - saveBuildType: protectedProcedure - .input(apiSaveBuildType) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to save this build type", - }); - } - await updateApplication(input.applicationId, { - buildType: input.buildType, - dockerfile: input.dockerfile, - publishDirectory: input.publishDirectory, - dockerContextPath: input.dockerContextPath, - dockerBuildStage: input.dockerBuildStage, - herokuVersion: input.herokuVersion, - }); + if (IS_CLOUD && application.serverId) { + jobData.serverId = application.serverId; + await deploy(jobData); + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + } + ); + }), + saveEnvironment: protectedProcedure + .input(apiSaveEnvironmentVariables) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to save this environment", + }); + } + await updateApplication(input.applicationId, { + env: input.env, + buildArgs: input.buildArgs, + }); + return true; + }), + saveBuildType: protectedProcedure + .input(apiSaveBuildType) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to save this build type", + }); + } + await updateApplication(input.applicationId, { + buildType: input.buildType, + dockerfile: input.dockerfile, + publishDirectory: input.publishDirectory, + dockerContextPath: input.dockerContextPath, + dockerBuildStage: input.dockerBuildStage, + herokuVersion: input.herokuVersion, + }); - return true; - }), - saveGithubProvider: protectedProcedure - .input(apiSaveGithubProvider) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to save this github provider", - }); - } - await updateApplication(input.applicationId, { - repository: input.repository, - branch: input.branch, - sourceType: "github", - owner: input.owner, - buildPath: input.buildPath, - applicationStatus: "idle", - githubId: input.githubId, - }); + return true; + }), + saveGithubProvider: protectedProcedure + .input(apiSaveGithubProvider) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to save this github provider", + }); + } + await updateApplication(input.applicationId, { + repository: input.repository, + branch: input.branch, + sourceType: "github", + owner: input.owner, + buildPath: input.buildPath, + applicationStatus: "idle", + githubId: input.githubId, + }); - return true; - }), - saveGitlabProvider: protectedProcedure - .input(apiSaveGitlabProvider) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to save this gitlab provider", - }); - } - await updateApplication(input.applicationId, { - gitlabRepository: input.gitlabRepository, - gitlabOwner: input.gitlabOwner, - gitlabBranch: input.gitlabBranch, - gitlabBuildPath: input.gitlabBuildPath, - sourceType: "gitlab", - applicationStatus: "idle", - gitlabId: input.gitlabId, - gitlabProjectId: input.gitlabProjectId, - gitlabPathNamespace: input.gitlabPathNamespace, - }); + return true; + }), + saveGitlabProvider: protectedProcedure + .input(apiSaveGitlabProvider) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to save this gitlab provider", + }); + } + await updateApplication(input.applicationId, { + gitlabRepository: input.gitlabRepository, + gitlabOwner: input.gitlabOwner, + gitlabBranch: input.gitlabBranch, + gitlabBuildPath: input.gitlabBuildPath, + sourceType: "gitlab", + applicationStatus: "idle", + gitlabId: input.gitlabId, + gitlabProjectId: input.gitlabProjectId, + gitlabPathNamespace: input.gitlabPathNamespace, + }); - return true; - }), - saveBitbucketProvider: protectedProcedure - .input(apiSaveBitbucketProvider) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to save this bitbucket provider", - }); - } - await updateApplication(input.applicationId, { - bitbucketRepository: input.bitbucketRepository, - bitbucketOwner: input.bitbucketOwner, - bitbucketBranch: input.bitbucketBranch, - bitbucketBuildPath: input.bitbucketBuildPath, - sourceType: "bitbucket", - applicationStatus: "idle", - bitbucketId: input.bitbucketId, - }); + return true; + }), + saveBitbucketProvider: protectedProcedure + .input(apiSaveBitbucketProvider) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to save this bitbucket provider", + }); + } + await updateApplication(input.applicationId, { + bitbucketRepository: input.bitbucketRepository, + bitbucketOwner: input.bitbucketOwner, + bitbucketBranch: input.bitbucketBranch, + bitbucketBuildPath: input.bitbucketBuildPath, + sourceType: "bitbucket", + applicationStatus: "idle", + bitbucketId: input.bitbucketId, + }); - return true; - }), - saveDockerProvider: protectedProcedure - .input(apiSaveDockerProvider) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to save this docker provider", - }); - } - await updateApplication(input.applicationId, { - dockerImage: input.dockerImage, - username: input.username, - password: input.password, - sourceType: "docker", - applicationStatus: "idle", - registryUrl: input.registryUrl, - }); + return true; + }), + saveDockerProvider: protectedProcedure + .input(apiSaveDockerProvider) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to save this docker provider", + }); + } + await updateApplication(input.applicationId, { + dockerImage: input.dockerImage, + username: input.username, + password: input.password, + sourceType: "docker", + applicationStatus: "idle", + registryUrl: input.registryUrl, + }); - return true; - }), - saveGitProdiver: protectedProcedure - .input(apiSaveGitProvider) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to save this git provider", - }); - } - await updateApplication(input.applicationId, { - customGitBranch: input.customGitBranch, - customGitBuildPath: input.customGitBuildPath, - customGitUrl: input.customGitUrl, - customGitSSHKeyId: input.customGitSSHKeyId, - sourceType: "git", - applicationStatus: "idle", - }); + return true; + }), + saveGitProdiver: protectedProcedure + .input(apiSaveGitProvider) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to save this git provider", + }); + } + await updateApplication(input.applicationId, { + customGitBranch: input.customGitBranch, + customGitBuildPath: input.customGitBuildPath, + customGitUrl: input.customGitUrl, + customGitSSHKeyId: input.customGitSSHKeyId, + sourceType: "git", + applicationStatus: "idle", + }); - return true; - }), - markRunning: protectedProcedure - .input(apiFindOneApplication) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to mark this application as running", - }); - } - await updateApplicationStatus(input.applicationId, "running"); - }), - update: protectedProcedure - .input(apiUpdateApplication) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to update this application", - }); - } - const { applicationId, ...rest } = input; - const updateApp = await updateApplication(applicationId, { - ...rest, - }); + return true; + }), + markRunning: protectedProcedure + .input(apiFindOneApplication) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to mark this application as running", + }); + } + await updateApplicationStatus(input.applicationId, "running"); + }), + update: protectedProcedure + .input(apiUpdateApplication) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to update this application", + }); + } + const { applicationId, ...rest } = input; + const updateApp = await updateApplication(applicationId, { + ...rest, + }); - if (!updateApp) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Update: Error to update application", - }); - } + if (!updateApp) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Update: Error to update application", + }); + } - return true; - }), - refreshToken: protectedProcedure - .input(apiFindOneApplication) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to refresh this application", - }); - } - await updateApplication(input.applicationId, { - refreshToken: nanoid(), - }); - return true; - }), - deploy: protectedProcedure - .input(apiFindOneApplication) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to deploy this application", - }); - } - const jobData: DeploymentJob = { - applicationId: input.applicationId, - titleLog: "Manual deployment", - descriptionLog: "", - type: "deploy", - applicationType: "application", - server: !!application.serverId, - }; - if (IS_CLOUD && application.serverId) { - jobData.serverId = application.serverId; - await deploy(jobData); + return true; + }), + refreshToken: protectedProcedure + .input(apiFindOneApplication) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to refresh this application", + }); + } + await updateApplication(input.applicationId, { + refreshToken: nanoid(), + }); + return true; + }), + deploy: protectedProcedure + .input(apiFindOneApplication) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to deploy this application", + }); + } + const jobData: DeploymentJob = { + applicationId: input.applicationId, + titleLog: "Manual deployment", + descriptionLog: "", + type: "deploy", + applicationType: "application", + server: !!application.serverId, + }; + if (IS_CLOUD && application.serverId) { + jobData.serverId = application.serverId; + await deploy(jobData); - return true; - } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - }, - ); - }), + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + } + ); + }), - cleanQueues: protectedProcedure - .input(apiFindOneApplication) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to clean this application", - }); - } - await cleanQueuesByApplication(input.applicationId); - }), + cleanQueues: protectedProcedure + .input(apiFindOneApplication) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to clean this application", + }); + } + await cleanQueuesByApplication(input.applicationId); + }), - readTraefikConfig: protectedProcedure - .input(apiFindOneApplication) - .query(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to read this application", - }); - } + readTraefikConfig: protectedProcedure + .input(apiFindOneApplication) + .query(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to read this application", + }); + } - let traefikConfig = null; - if (application.serverId) { - traefikConfig = await readRemoteConfig( - application.serverId, - application.appName, - ); - } else { - traefikConfig = readConfig(application.appName); - } - return traefikConfig; - }), + let traefikConfig = null; + if (application.serverId) { + traefikConfig = await readRemoteConfig( + application.serverId, + application.appName + ); + } else { + traefikConfig = readConfig(application.appName); + } + return traefikConfig; + }), - dropDeployment: protectedProcedure - .meta({ - openapi: { - path: "/drop-deployment", - method: "POST", - override: true, - enabled: false, - }, - }) - .use(uploadProcedure) - .input(uploadFileSchema) - .mutation(async ({ input, ctx }) => { - const zipFile = input.zip; + dropDeployment: protectedProcedure + .meta({ + openapi: { + path: "/drop-deployment", + method: "POST", + override: true, + enabled: false, + }, + }) + .use(uploadProcedure) + .input(uploadFileSchema) + .mutation(async ({ input, ctx }) => { + const zipFile = input.zip; - const app = await findApplicationById(input.applicationId as string); + const app = await findApplicationById(input.applicationId as string); - if (app.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to deploy this application", - }); - } + if (app.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to deploy this application", + }); + } - updateApplication(input.applicationId as string, { - sourceType: "drop", - dropBuildPath: input.dropBuildPath, - }); + updateApplication(input.applicationId as string, { + sourceType: "drop", + dropBuildPath: input.dropBuildPath, + }); - await unzipDrop(zipFile, app); - const jobData: DeploymentJob = { - applicationId: app.applicationId, - titleLog: "Manual deployment", - descriptionLog: "", - type: "deploy", - applicationType: "application", - server: !!app.serverId, - }; - if (IS_CLOUD && app.serverId) { - jobData.serverId = app.serverId; - await deploy(jobData); - return true; - } + await unzipDrop(zipFile, app); + const jobData: DeploymentJob = { + applicationId: app.applicationId, + titleLog: "Manual deployment", + descriptionLog: "", + type: "deploy", + applicationType: "application", + server: !!app.serverId, + }; + if (IS_CLOUD && app.serverId) { + jobData.serverId = app.serverId; + await deploy(jobData); + return true; + } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, - }, - ); - return true; - }), - updateTraefikConfig: protectedProcedure - .input(z.object({ applicationId: z.string(), traefikConfig: z.string() })) - .mutation(async ({ input, ctx }) => { - const application = await findApplicationById(input.applicationId); + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + } + ); + return true; + }), + updateTraefikConfig: protectedProcedure + .input(z.object({ applicationId: z.string(), traefikConfig: z.string() })) + .mutation(async ({ input, ctx }) => { + const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to update this application", - }); - } + if (application.project.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to update this application", + }); + } - if (application.serverId) { - await writeConfigRemote( - application.serverId, - application.appName, - input.traefikConfig, - ); - } else { - writeConfig(application.appName, input.traefikConfig); - } - return true; - }), - readAppMonitoring: protectedProcedure - .input(apiFindMonitoringStats) - .query(async ({ input, ctx }) => { - if (IS_CLOUD) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Functionality not available in cloud version", - }); - } - const stats = await getApplicationStats(input.appName); + if (application.serverId) { + await writeConfigRemote( + application.serverId, + application.appName, + input.traefikConfig + ); + } else { + writeConfig(application.appName, input.traefikConfig); + } + return true; + }), + readAppMonitoring: protectedProcedure + .input(apiFindMonitoringStats) + .query(async ({ input, ctx }) => { + if (IS_CLOUD) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Functionality not available in cloud version", + }); + } + const stats = await getApplicationStats(input.appName); - return stats; - }), + return stats; + }), }); diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 923ea130..425f8d13 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -1,11 +1,11 @@ import { relations } from "drizzle-orm"; import { - boolean, - integer, - json, - pgEnum, - pgTable, - text, + boolean, + integer, + json, + pgEnum, + pgTable, + text, } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; @@ -28,498 +28,493 @@ import { sshKeys } from "./ssh-key"; import { generateAppName } from "./utils"; export const sourceType = pgEnum("sourceType", [ - "docker", - "git", - "github", - "gitlab", - "bitbucket", - "drop", + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop", ]); export const buildType = pgEnum("buildType", [ - "dockerfile", - "heroku_buildpacks", - "paketo_buildpacks", - "nixpacks", - "static", + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", ]); // TODO: refactor this types export interface HealthCheckSwarm { - Test?: string[] | undefined; - Interval?: number | undefined; - Timeout?: number | undefined; - StartPeriod?: number | undefined; - Retries?: number | undefined; + Test?: string[] | undefined; + Interval?: number | undefined; + Timeout?: number | undefined; + StartPeriod?: number | undefined; + Retries?: number | undefined; } export interface RestartPolicySwarm { - Condition?: string | undefined; - Delay?: number | undefined; - MaxAttempts?: number | undefined; - Window?: number | undefined; + Condition?: string | undefined; + Delay?: number | undefined; + MaxAttempts?: number | undefined; + Window?: number | undefined; } export interface PlacementSwarm { - Constraints?: string[] | undefined; - Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined; - MaxReplicas?: number | undefined; - Platforms?: - | Array<{ - Architecture: string; - OS: string; - }> - | undefined; + Constraints?: string[] | undefined; + Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined; + MaxReplicas?: number | undefined; + Platforms?: + | Array<{ + Architecture: string; + OS: string; + }> + | undefined; } export interface UpdateConfigSwarm { - Parallelism: number; - Delay?: number | undefined; - FailureAction?: string | undefined; - Monitor?: number | undefined; - MaxFailureRatio?: number | undefined; - Order: string; + Parallelism: number; + Delay?: number | undefined; + FailureAction?: string | undefined; + Monitor?: number | undefined; + MaxFailureRatio?: number | undefined; + Order: string; } export interface ServiceModeSwarm { - Replicated?: { Replicas?: number | undefined } | undefined; - Global?: {} | undefined; - ReplicatedJob?: - | { - MaxConcurrent?: number | undefined; - TotalCompletions?: number | undefined; - } - | undefined; - GlobalJob?: {} | undefined; + Replicated?: { Replicas?: number | undefined } | undefined; + Global?: {} | undefined; + ReplicatedJob?: + | { + MaxConcurrent?: number | undefined; + TotalCompletions?: number | undefined; + } + | undefined; + GlobalJob?: {} | undefined; } export interface NetworkSwarm { - Target?: string | undefined; - Aliases?: string[] | undefined; - DriverOpts?: { [key: string]: string } | undefined; + Target?: string | undefined; + Aliases?: string[] | undefined; + DriverOpts?: { [key: string]: string } | undefined; } export interface LabelsSwarm { - [name: string]: string; + [name: string]: string; } export const applications = pgTable("application", { - applicationId: text("applicationId") - .notNull() - .primaryKey() - .$defaultFn(() => nanoid()), - name: text("name").notNull(), - appName: text("appName") - .notNull() - .$defaultFn(() => generateAppName("app")) - .unique(), - description: text("description"), - env: text("env"), - previewEnv: text("previewEnv"), - previewBuildArgs: text("previewBuildArgs"), - previewWildcard: text("previewWildcard"), - previewPort: integer("previewPort").default(3000), - previewHttps: boolean("previewHttps").notNull().default(false), - previewPath: text("previewPath").default("/"), - previewCertificateType: certificateType("certificateType") - .notNull() - .default("none"), - previewLimit: integer("previewLimit").default(3), - isPreviewDeploymentsActive: boolean("isPreviewDeploymentsActive").default( - false, - ), - buildArgs: text("buildArgs"), - memoryReservation: integer("memoryReservation"), - memoryLimit: integer("memoryLimit"), - cpuReservation: integer("cpuReservation"), - cpuLimit: integer("cpuLimit"), - title: text("title"), - enabled: boolean("enabled"), - subtitle: text("subtitle"), - command: text("command"), - refreshToken: text("refreshToken").$defaultFn(() => nanoid()), - sourceType: sourceType("sourceType").notNull().default("github"), - // Github - repository: text("repository"), - owner: text("owner"), - branch: text("branch"), - buildPath: text("buildPath").default("/"), - autoDeploy: boolean("autoDeploy").$defaultFn(() => true), - // Gitlab - gitlabProjectId: integer("gitlabProjectId"), - gitlabRepository: text("gitlabRepository"), - gitlabOwner: text("gitlabOwner"), - gitlabBranch: text("gitlabBranch"), - gitlabBuildPath: text("gitlabBuildPath").default("/"), - gitlabPathNamespace: text("gitlabPathNamespace"), - // Bitbucket - bitbucketRepository: text("bitbucketRepository"), - bitbucketOwner: text("bitbucketOwner"), - bitbucketBranch: text("bitbucketBranch"), - bitbucketBuildPath: text("bitbucketBuildPath").default("/"), - // Docker - username: text("username"), - password: text("password"), - dockerImage: text("dockerImage"), - registryUrl: text("registryUrl"), - // Git - customGitUrl: text("customGitUrl"), - customGitBranch: text("customGitBranch"), - customGitBuildPath: text("customGitBuildPath"), - customGitSSHKeyId: text("customGitSSHKeyId").references( - () => sshKeys.sshKeyId, - { - onDelete: "set null", - }, - ), - dockerfile: text("dockerfile"), - dockerContextPath: text("dockerContextPath"), - dockerBuildStage: text("dockerBuildStage"), - // Drop - dropBuildPath: text("dropBuildPath"), - // Docker swarm json - healthCheckSwarm: json("healthCheckSwarm").$type(), - restartPolicySwarm: json("restartPolicySwarm").$type(), - placementSwarm: json("placementSwarm").$type(), - updateConfigSwarm: json("updateConfigSwarm").$type(), - rollbackConfigSwarm: json("rollbackConfigSwarm").$type(), - modeSwarm: json("modeSwarm").$type(), - labelsSwarm: json("labelsSwarm").$type(), - networkSwarm: json("networkSwarm").$type(), - // - replicas: integer("replicas").default(1).notNull(), - applicationStatus: applicationStatus("applicationStatus") - .notNull() - .default("idle"), - buildType: buildType("buildType").notNull().default("nixpacks"), - herokuVersion: text("herokuVersion").default("24"), - publishDirectory: text("publishDirectory"), - createdAt: text("createdAt") - .notNull() - .$defaultFn(() => new Date().toISOString()), - registryId: text("registryId").references(() => registry.registryId, { - onDelete: "set null", - }), - projectId: text("projectId") - .notNull() - .references(() => projects.projectId, { onDelete: "cascade" }), - githubId: text("githubId").references(() => github.githubId, { - onDelete: "set null", - }), - gitlabId: text("gitlabId").references(() => gitlab.gitlabId, { - onDelete: "set null", - }), - bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, { - onDelete: "set null", - }), - serverId: text("serverId").references(() => server.serverId, { - onDelete: "cascade", - }), + applicationId: text("applicationId") + .notNull() + .primaryKey() + .$defaultFn(() => nanoid()), + name: text("name").notNull(), + appName: text("appName") + .notNull() + .$defaultFn(() => generateAppName("app")) + .unique(), + description: text("description"), + env: text("env"), + previewEnv: text("previewEnv"), + previewBuildArgs: text("previewBuildArgs"), + previewWildcard: text("previewWildcard"), + previewPort: integer("previewPort").default(3000), + previewHttps: boolean("previewHttps").notNull().default(false), + previewPath: text("previewPath").default("/"), + previewCertificateType: certificateType("certificateType") + .notNull() + .default("none"), + previewLimit: integer("previewLimit").default(3), + isPreviewDeploymentsActive: boolean("isPreviewDeploymentsActive").default( + false + ), + buildArgs: text("buildArgs"), + memoryReservation: integer("memoryReservation"), + memoryLimit: integer("memoryLimit"), + cpuReservation: integer("cpuReservation"), + cpuLimit: integer("cpuLimit"), + title: text("title"), + enabled: boolean("enabled"), + subtitle: text("subtitle"), + command: text("command"), + refreshToken: text("refreshToken").$defaultFn(() => nanoid()), + sourceType: sourceType("sourceType").notNull().default("github"), + // Github + repository: text("repository"), + owner: text("owner"), + branch: text("branch"), + buildPath: text("buildPath").default("/"), + autoDeploy: boolean("autoDeploy").$defaultFn(() => true), + // Gitlab + gitlabProjectId: integer("gitlabProjectId"), + gitlabRepository: text("gitlabRepository"), + gitlabOwner: text("gitlabOwner"), + gitlabBranch: text("gitlabBranch"), + gitlabBuildPath: text("gitlabBuildPath").default("/"), + gitlabPathNamespace: text("gitlabPathNamespace"), + // Bitbucket + bitbucketRepository: text("bitbucketRepository"), + bitbucketOwner: text("bitbucketOwner"), + bitbucketBranch: text("bitbucketBranch"), + bitbucketBuildPath: text("bitbucketBuildPath").default("/"), + // Docker + username: text("username"), + password: text("password"), + dockerImage: text("dockerImage"), + registryUrl: text("registryUrl"), + // Git + customGitUrl: text("customGitUrl"), + customGitBranch: text("customGitBranch"), + customGitBuildPath: text("customGitBuildPath"), + customGitSSHKeyId: text("customGitSSHKeyId").references( + () => sshKeys.sshKeyId, + { + onDelete: "set null", + } + ), + dockerfile: text("dockerfile"), + dockerContextPath: text("dockerContextPath"), + dockerBuildStage: text("dockerBuildStage"), + // Drop + dropBuildPath: text("dropBuildPath"), + // Docker swarm json + healthCheckSwarm: json("healthCheckSwarm").$type(), + restartPolicySwarm: json("restartPolicySwarm").$type(), + placementSwarm: json("placementSwarm").$type(), + updateConfigSwarm: json("updateConfigSwarm").$type(), + rollbackConfigSwarm: json("rollbackConfigSwarm").$type(), + modeSwarm: json("modeSwarm").$type(), + labelsSwarm: json("labelsSwarm").$type(), + networkSwarm: json("networkSwarm").$type(), + // + replicas: integer("replicas").default(1).notNull(), + applicationStatus: applicationStatus("applicationStatus") + .notNull() + .default("idle"), + buildType: buildType("buildType").notNull().default("nixpacks"), + herokuVersion: text("herokuVersion").default("24"), + publishDirectory: text("publishDirectory"), + createdAt: text("createdAt") + .notNull() + .$defaultFn(() => new Date().toISOString()), + registryId: text("registryId").references(() => registry.registryId, { + onDelete: "set null", + }), + projectId: text("projectId") + .notNull() + .references(() => projects.projectId, { onDelete: "cascade" }), + githubId: text("githubId").references(() => github.githubId, { + onDelete: "set null", + }), + gitlabId: text("gitlabId").references(() => gitlab.gitlabId, { + onDelete: "set null", + }), + bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, { + onDelete: "set null", + }), + serverId: text("serverId").references(() => server.serverId, { + onDelete: "cascade", + }), }); export const applicationsRelations = relations( - applications, - ({ one, many }) => ({ - project: one(projects, { - fields: [applications.projectId], - references: [projects.projectId], - }), - deployments: many(deployments), - customGitSSHKey: one(sshKeys, { - fields: [applications.customGitSSHKeyId], - references: [sshKeys.sshKeyId], - }), - domains: many(domains), - mounts: many(mounts), - redirects: many(redirects), - security: many(security), - ports: many(ports), - registry: one(registry, { - fields: [applications.registryId], - references: [registry.registryId], - }), - github: one(github, { - fields: [applications.githubId], - references: [github.githubId], - }), - gitlab: one(gitlab, { - fields: [applications.gitlabId], - references: [gitlab.gitlabId], - }), - bitbucket: one(bitbucket, { - fields: [applications.bitbucketId], - references: [bitbucket.bitbucketId], - }), - server: one(server, { - fields: [applications.serverId], - references: [server.serverId], - }), - previewDeployments: many(previewDeployments), - }), + applications, + ({ one, many }) => ({ + project: one(projects, { + fields: [applications.projectId], + references: [projects.projectId], + }), + deployments: many(deployments), + customGitSSHKey: one(sshKeys, { + fields: [applications.customGitSSHKeyId], + references: [sshKeys.sshKeyId], + }), + domains: many(domains), + mounts: many(mounts), + redirects: many(redirects), + security: many(security), + ports: many(ports), + registry: one(registry, { + fields: [applications.registryId], + references: [registry.registryId], + }), + github: one(github, { + fields: [applications.githubId], + references: [github.githubId], + }), + gitlab: one(gitlab, { + fields: [applications.gitlabId], + references: [gitlab.gitlabId], + }), + bitbucket: one(bitbucket, { + fields: [applications.bitbucketId], + references: [bitbucket.bitbucketId], + }), + server: one(server, { + fields: [applications.serverId], + references: [server.serverId], + }), + previewDeployments: many(previewDeployments), + }) ); const HealthCheckSwarmSchema = z - .object({ - Test: z.array(z.string()).optional(), - Interval: z.number().optional(), - Timeout: z.number().optional(), - StartPeriod: z.number().optional(), - Retries: z.number().optional(), - }) - .strict(); + .object({ + Test: z.array(z.string()).optional(), + Interval: z.number().optional(), + Timeout: z.number().optional(), + StartPeriod: z.number().optional(), + Retries: z.number().optional(), + }) + .strict(); const RestartPolicySwarmSchema = z - .object({ - Condition: z.string().optional(), - Delay: z.number().optional(), - MaxAttempts: z.number().optional(), - Window: z.number().optional(), - }) - .strict(); + .object({ + Condition: z.string().optional(), + Delay: z.number().optional(), + MaxAttempts: z.number().optional(), + Window: z.number().optional(), + }) + .strict(); const PreferenceSchema = z - .object({ - Spread: z.object({ - SpreadDescriptor: z.string(), - }), - }) - .strict(); + .object({ + Spread: z.object({ + SpreadDescriptor: z.string(), + }), + }) + .strict(); const PlatformSchema = z - .object({ - Architecture: z.string(), - OS: z.string(), - }) - .strict(); + .object({ + Architecture: z.string(), + OS: z.string(), + }) + .strict(); const PlacementSwarmSchema = z - .object({ - Constraints: z.array(z.string()).optional(), - Preferences: z.array(PreferenceSchema).optional(), - MaxReplicas: z.number().optional(), - Platforms: z.array(PlatformSchema).optional(), - }) - .strict(); + .object({ + Constraints: z.array(z.string()).optional(), + Preferences: z.array(PreferenceSchema).optional(), + MaxReplicas: z.number().optional(), + Platforms: z.array(PlatformSchema).optional(), + }) + .strict(); const UpdateConfigSwarmSchema = z - .object({ - Parallelism: z.number(), - Delay: z.number().optional(), - FailureAction: z.string().optional(), - Monitor: z.number().optional(), - MaxFailureRatio: z.number().optional(), - Order: z.string(), - }) - .strict(); + .object({ + Parallelism: z.number(), + Delay: z.number().optional(), + FailureAction: z.string().optional(), + Monitor: z.number().optional(), + MaxFailureRatio: z.number().optional(), + Order: z.string(), + }) + .strict(); const ReplicatedSchema = z - .object({ - Replicas: z.number().optional(), - }) - .strict(); + .object({ + Replicas: z.number().optional(), + }) + .strict(); const ReplicatedJobSchema = z - .object({ - MaxConcurrent: z.number().optional(), - TotalCompletions: z.number().optional(), - }) - .strict(); + .object({ + MaxConcurrent: z.number().optional(), + TotalCompletions: z.number().optional(), + }) + .strict(); const ServiceModeSwarmSchema = z - .object({ - Replicated: ReplicatedSchema.optional(), - Global: z.object({}).optional(), - ReplicatedJob: ReplicatedJobSchema.optional(), - GlobalJob: z.object({}).optional(), - }) - .strict(); + .object({ + Replicated: ReplicatedSchema.optional(), + Global: z.object({}).optional(), + ReplicatedJob: ReplicatedJobSchema.optional(), + GlobalJob: z.object({}).optional(), + }) + .strict(); const NetworkSwarmSchema = z.array( - z - .object({ - Target: z.string().optional(), - Aliases: z.array(z.string()).optional(), - DriverOpts: z.object({}).optional(), - }) - .strict(), + z + .object({ + Target: z.string().optional(), + Aliases: z.array(z.string()).optional(), + DriverOpts: z.object({}).optional(), + }) + .strict() ); const LabelsSwarmSchema = z.record(z.string()); const createSchema = createInsertSchema(applications, { - appName: z.string(), - createdAt: z.string(), - applicationId: z.string(), - autoDeploy: z.boolean(), - env: z.string().optional(), - buildArgs: z.string().optional(), - name: z.string().min(1), - description: z.string().optional(), - memoryReservation: z.number().optional(), - memoryLimit: z.number().optional(), - cpuReservation: z.number().optional(), - cpuLimit: z.number().optional(), - title: z.string().optional(), - enabled: z.boolean().optional(), - subtitle: z.string().optional(), - dockerImage: z.string().optional(), - username: z.string().optional(), - isPreviewDeploymentsActive: z.boolean().optional(), - password: z.string().optional(), - registryUrl: z.string().optional(), - customGitSSHKeyId: z.string().optional(), - repository: z.string().optional(), - dockerfile: z.string().optional(), - branch: z.string().optional(), - customGitBranch: z.string().optional(), - customGitBuildPath: z.string().optional(), - customGitUrl: z.string().optional(), - buildPath: z.string().optional(), - projectId: z.string(), - sourceType: z.enum(["github", "docker", "git"]).optional(), - applicationStatus: z.enum(["idle", "running", "done", "error"]), - buildType: z.enum([ - "dockerfile", - "heroku_buildpacks", - "paketo_buildpacks", - "nixpacks", - "static", - ]), - herokuVersion: z.string().optional(), - publishDirectory: z.string().optional(), - owner: z.string(), - healthCheckSwarm: HealthCheckSwarmSchema.nullable(), - restartPolicySwarm: RestartPolicySwarmSchema.nullable(), - placementSwarm: PlacementSwarmSchema.nullable(), - updateConfigSwarm: UpdateConfigSwarmSchema.nullable(), - rollbackConfigSwarm: UpdateConfigSwarmSchema.nullable(), - modeSwarm: ServiceModeSwarmSchema.nullable(), - labelsSwarm: LabelsSwarmSchema.nullable(), - networkSwarm: NetworkSwarmSchema.nullable(), - previewPort: z.number().optional(), - previewEnv: z.string().optional(), - previewBuildArgs: z.string().optional(), - previewWildcard: z.string().optional(), - previewLimit: z.number().optional(), - previewHttps: z.boolean().optional(), - previewPath: z.string().optional(), - previewCertificateType: z.enum(["letsencrypt", "none"]).optional(), + appName: z.string(), + createdAt: z.string(), + applicationId: z.string(), + autoDeploy: z.boolean(), + env: z.string().optional(), + buildArgs: z.string().optional(), + name: z.string().min(1), + description: z.string().optional(), + memoryReservation: z.number().optional(), + memoryLimit: z.number().optional(), + cpuReservation: z.number().optional(), + cpuLimit: z.number().optional(), + title: z.string().optional(), + enabled: z.boolean().optional(), + subtitle: z.string().optional(), + dockerImage: z.string().optional(), + username: z.string().optional(), + isPreviewDeploymentsActive: z.boolean().optional(), + password: z.string().optional(), + registryUrl: z.string().optional(), + customGitSSHKeyId: z.string().optional(), + repository: z.string().optional(), + dockerfile: z.string().optional(), + branch: z.string().optional(), + customGitBranch: z.string().optional(), + customGitBuildPath: z.string().optional(), + customGitUrl: z.string().optional(), + buildPath: z.string().optional(), + projectId: z.string(), + sourceType: z.enum(["github", "docker", "git"]).optional(), + applicationStatus: z.enum(["idle", "running", "done", "error"]), + buildType: z.enum([ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", + ]), + herokuVersion: z.string().optional(), + publishDirectory: z.string().optional(), + owner: z.string(), + healthCheckSwarm: HealthCheckSwarmSchema.nullable(), + restartPolicySwarm: RestartPolicySwarmSchema.nullable(), + placementSwarm: PlacementSwarmSchema.nullable(), + updateConfigSwarm: UpdateConfigSwarmSchema.nullable(), + rollbackConfigSwarm: UpdateConfigSwarmSchema.nullable(), + modeSwarm: ServiceModeSwarmSchema.nullable(), + labelsSwarm: LabelsSwarmSchema.nullable(), + networkSwarm: NetworkSwarmSchema.nullable(), + previewPort: z.number().optional(), + previewEnv: z.string().optional(), + previewBuildArgs: z.string().optional(), + previewWildcard: z.string().optional(), + previewLimit: z.number().optional(), + previewHttps: z.boolean().optional(), + previewPath: z.string().optional(), + previewCertificateType: z.enum(["letsencrypt", "none"]).optional(), }); export const apiCreateApplication = createSchema.pick({ - name: true, - appName: true, - description: true, - projectId: true, - serverId: true, + name: true, + appName: true, + description: true, + projectId: true, + serverId: true, }); export const apiFindOneApplication = createSchema - .pick({ - applicationId: true, - }) - .required(); + .pick({ + applicationId: true, + }) + .required(); export const apiReloadApplication = createSchema - .pick({ - appName: true, - applicationId: true, - }) - .required(); + .pick({ + appName: true, + applicationId: true, + }) + .required(); export const apiSaveBuildType = createSchema - .pick({ - applicationId: true, - buildType: true, - dockerfile: true, - dockerContextPath: true, - dockerBuildStage: true, - herokuVersion: true, - }) - .required() - .merge(createSchema.pick({ publishDirectory: true })); + .pick({ + applicationId: true, + buildType: true, + dockerfile: true, + dockerContextPath: true, + dockerBuildStage: true, + herokuVersion: true, + }) + .required() + .merge(createSchema.pick({ publishDirectory: true })); export const apiSaveGithubProvider = createSchema - .pick({ - applicationId: true, - repository: true, - branch: true, - owner: true, - buildPath: true, - githubId: true, - }) - .required(); + .pick({ + applicationId: true, + repository: true, + branch: true, + owner: true, + buildPath: true, + githubId: true, + }) + .required(); export const apiSaveGitlabProvider = createSchema - .pick({ - applicationId: true, - gitlabBranch: true, - gitlabBuildPath: true, - gitlabOwner: true, - gitlabRepository: true, - gitlabId: true, - gitlabProjectId: true, - gitlabPathNamespace: true, - }) - .required(); + .pick({ + applicationId: true, + gitlabBranch: true, + gitlabBuildPath: true, + gitlabOwner: true, + gitlabRepository: true, + gitlabId: true, + gitlabProjectId: true, + gitlabPathNamespace: true, + }) + .required(); export const apiSaveBitbucketProvider = createSchema - .pick({ - bitbucketBranch: true, - bitbucketBuildPath: true, - bitbucketOwner: true, - bitbucketRepository: true, - bitbucketId: true, - applicationId: true, - }) - .required(); + .pick({ + bitbucketBranch: true, + bitbucketBuildPath: true, + bitbucketOwner: true, + bitbucketRepository: true, + bitbucketId: true, + applicationId: true, + }) + .required(); export const apiSaveDockerProvider = createSchema - .pick({ - dockerImage: true, - applicationId: true, - username: true, - password: true, - registryUrl: true, - }) - .required(); + .pick({ + dockerImage: true, + applicationId: true, + username: true, + password: true, + registryUrl: true, + }) + .required(); export const apiSaveGitProvider = createSchema - .pick({ - customGitBranch: true, - applicationId: true, - customGitBuildPath: true, - customGitUrl: true, - }) - .required() - .merge( - createSchema.pick({ - customGitSSHKeyId: true, - }), - ); + .pick({ + customGitBranch: true, + applicationId: true, + customGitBuildPath: true, + customGitUrl: true, + }) + .required() + .merge( + createSchema.pick({ + customGitSSHKeyId: true, + }) + ); export const apiSaveEnvironmentVariables = createSchema - .pick({ - applicationId: true, - env: true, - buildArgs: true, - }) - .required(); + .pick({ + applicationId: true, + env: true, + buildArgs: true, + }) + .required(); export const apiFindMonitoringStats = createSchema - .pick({ - appName: true, - }) - .required(); + .pick({ + appName: true, + }) + .required(); export const apiUpdateApplication = createSchema - .partial() - .extend({ - applicationId: z.string().min(1), - }) - .omit({ serverId: true }); - -export const apiDeleteApplication = z.object({ - applicationId: z.string().min(1), - deleteVolumes: z.boolean(), -}); + .partial() + .extend({ + applicationId: z.string().min(1), + }) + .omit({ serverId: true });