From fbda00f0595113116605b88bb26a92364fca3cb5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 21 Oct 2024 00:34:16 -0600 Subject: [PATCH] refactor: update webhooks and added validation to prevent deploy when the server is inactive --- apps/api/src/schema.ts | 4 +- .../settings/billing/show-billing.tsx | 1 - .../settings/servers/show-servers.tsx | 50 ++++--- apps/dokploy/pages/api/stripe/webhook.ts | 139 ++++-------------- .../services/application/[applicationId].tsx | 43 +++++- .../services/compose/[composeId].tsx | 42 +++++- .../services/mariadb/[mariadbId].tsx | 42 +++++- .../[projectId]/services/mongo/[mongoId].tsx | 42 +++++- .../[projectId]/services/mysql/[mysqlId].tsx | 42 +++++- .../services/postgres/[postgresId].tsx | 42 +++++- .../[projectId]/services/redis/[redisId].tsx | 42 +++++- apps/dokploy/server/api/routers/backup.ts | 20 +++ apps/dokploy/server/api/routers/mariadb.ts | 2 + apps/dokploy/server/api/routers/server.ts | 39 ++++- apps/dokploy/server/api/routers/settings.ts | 7 + apps/dokploy/server/api/routers/stripe.ts | 52 +------ apps/dokploy/server/utils/deploy.ts | 5 + apps/schedules/src/utils.ts | 28 +++- 18 files changed, 440 insertions(+), 202 deletions(-) diff --git a/apps/api/src/schema.ts b/apps/api/src/schema.ts index 4655f006..5f26e018 100644 --- a/apps/api/src/schema.ts +++ b/apps/api/src/schema.ts @@ -8,7 +8,7 @@ export const deployJobSchema = z.discriminatedUnion("applicationType", [ server: z.boolean().optional(), type: z.enum(["deploy", "redeploy"]), applicationType: z.literal("application"), - serverId: z.string(), + serverId: z.string().min(1), }), z.object({ composeId: z.string(), @@ -17,7 +17,7 @@ export const deployJobSchema = z.discriminatedUnion("applicationType", [ server: z.boolean().optional(), type: z.enum(["deploy", "redeploy"]), applicationType: z.literal("compose"), - serverId: z.string(), + serverId: z.string().min(1), }), ]); diff --git a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx index b8359732..049ce2b8 100644 --- a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx +++ b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx @@ -96,7 +96,6 @@ export const ShowBilling = () => { )} )} - {products?.map((product) => { const featured = true; return ( diff --git a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx index 3dbbe75a..31540b84 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx @@ -118,6 +118,7 @@ export const ShowServers = () => { {data?.map((server) => { const canDelete = server.totalSum === 0; + const isActive = server.serverStatus === "active"; return ( {server.name} @@ -164,18 +165,25 @@ export const ShowServers = () => { Actions - {server.sshKeyId && ( - - Enter the terminal - + + {isActive && ( + <> + {server.sshKeyId && ( + + Enter the terminal + + )} + + + + {server.sshKeyId && ( + + )} + )} - - - - {server.sshKeyId && ( - - )} { - {server.sshKeyId && ( + {isActive && ( <> - - Extra + {server.sshKeyId && ( + <> + + Extra - - + + + + )} )} diff --git a/apps/dokploy/pages/api/stripe/webhook.ts b/apps/dokploy/pages/api/stripe/webhook.ts index ca4de9c8..5dfa6fe0 100644 --- a/apps/dokploy/pages/api/stripe/webhook.ts +++ b/apps/dokploy/pages/api/stripe/webhook.ts @@ -75,21 +75,7 @@ export default async function handler( return res.status(400).send("Webhook Error: Admin not found"); } const newServersQuantity = admin.serversQuantity; - const servers = await findServersByAdminIdSorted(admin.adminId); - - if (servers.length > newServersQuantity) { - for (const [index, server] of servers.entries()) { - if (index < newServersQuantity) { - await activateServer(server.serverId); - } else { - await deactivateServer(server.serverId); - } - } - } else { - for (const server of servers) { - await activateServer(server.serverId); - } - } + await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); break; } case "customer.subscription.created": { @@ -101,20 +87,11 @@ export default async function handler( serversQuantity: newSubscription?.items?.data?.[0]?.quantity ?? 0, stripeCustomerId: newSubscription.customer as string, }) - .where( - eq( - admins.stripeCustomerId, - typeof newSubscription.customer === "string" - ? newSubscription.customer - : "", - ), - ) + .where(eq(admins.stripeCustomerId, newSubscription.customer as string)) .returning(); const admin = await findAdminByStripeCustomerId( - typeof newSubscription.customer === "string" - ? newSubscription.customer - : "", + newSubscription.customer as string, ); if (!admin) { @@ -122,26 +99,7 @@ export default async function handler( } const newServersQuantity = admin.serversQuantity; - const servers = await findServersByAdminIdSorted(admin.adminId); - - // 4 > 3 - if (servers.length > newServersQuantity) { - for (const [index, server] of servers.entries()) { - // 0 < 3 = true - // 1 < 3 = true - // 2 < 3 = true - // 3 < 3 = false - if (index < newServersQuantity) { - await activateServer(server.serverId); - } else { - await deactivateServer(server.serverId); - } - } - } else { - for (const server of servers) { - await activateServer(server.serverId); - } - } + await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); break; } @@ -155,19 +113,10 @@ export default async function handler( stripeSubscriptionId: null, serversQuantity: 0, }) - .where( - eq( - admins.stripeCustomerId, - typeof newSubscription.customer === "string" - ? newSubscription.customer - : "", - ), - ); + .where(eq(admins.stripeCustomerId, newSubscription.customer as string)); const admin = await findAdminByStripeCustomerId( - typeof newSubscription.customer === "string" - ? newSubscription.customer - : "", + newSubscription.customer as string, ); if (!admin) { @@ -179,25 +128,15 @@ export default async function handler( } case "customer.subscription.updated": { const newSubscription = event.data.object as Stripe.Subscription; - await db .update(admins) .set({ serversQuantity: newSubscription?.items?.data?.[0]?.quantity ?? 0, }) - .where( - eq( - admins.stripeCustomerId, - typeof newSubscription.customer === "string" - ? newSubscription.customer - : "", - ), - ); + .where(eq(admins.stripeCustomerId, newSubscription.customer as string)); const admin = await findAdminByStripeCustomerId( - typeof newSubscription.customer === "string" - ? newSubscription.customer - : "", + newSubscription.customer as string, ); if (!admin) { @@ -205,21 +144,7 @@ export default async function handler( } const newServersQuantity = admin.serversQuantity; - const servers = await findServersByAdminIdSorted(admin.adminId); - - if (servers.length > newServersQuantity) { - for (const [index, server] of servers.entries()) { - if (index < newServersQuantity) { - await activateServer(server.serverId); - } else { - await deactivateServer(server.serverId); - } - } - } else { - for (const server of servers) { - await activateServer(server.serverId); - } - } + await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); break; } @@ -245,21 +170,7 @@ export default async function handler( return res.status(400).send("Webhook Error: Admin not found"); } const newServersQuantity = admin.serversQuantity; - const servers = await findServersByAdminIdSorted(admin.adminId); - if (servers.length > newServersQuantity) { - for (const [index, server] of servers.entries()) { - if (index < newServersQuantity) { - await activateServer(server.serverId); - } else { - await deactivateServer(server.serverId); - } - } - } else { - for (const server of servers) { - await activateServer(server.serverId); - } - } - + await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); break; } case "invoice.payment_failed": { @@ -269,15 +180,10 @@ export default async function handler( .set({ serversQuantity: 0, }) - .where( - eq( - admins.stripeCustomerId, - typeof newInvoice.customer === "string" ? newInvoice.customer : "", - ), - ); + .where(eq(admins.stripeCustomerId, newInvoice.customer as string)); const admin = await findAdminByStripeCustomerId( - typeof newInvoice.customer === "string" ? newInvoice.customer : "", + newInvoice.customer as string, ); if (!admin) { @@ -308,7 +214,6 @@ export default async function handler( break; } - default: console.log(`Unhandled event type: ${event.type}`); } @@ -354,3 +259,23 @@ export const findServersByAdminIdSorted = async (adminId: string) => { return servers; }; +export const updateServersBasedOnQuantity = async ( + adminId: string, + newServersQuantity: number, +) => { + const servers = await findServersByAdminIdSorted(adminId); + + if (servers.length > newServersQuantity) { + for (const [index, server] of servers.entries()) { + if (index < newServersQuantity) { + await activateServer(server.serverId); + } else { + await deactivateServer(server.serverId); + } + } + } else { + for (const server of servers) { + await activateServer(server.serverId); + } + } +}; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index 05625f5e..b09eb525 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -22,13 +22,20 @@ import { BreadcrumbItem, BreadcrumbLink, } from "@/components/ui/breadcrumb"; +import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; -import { GlobeIcon } from "lucide-react"; +import { GlobeIcon, HelpCircle } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -100,8 +107,38 @@ const Service = ( {data?.appName} -
- {data?.server?.name || "Dokploy Server"} +
+ + {data?.server?.name || "Dokploy Server"} + + {data?.server?.serverStatus === "inactive" && ( + + + + + + + + You cannot, deploy this application because the server + is inactive, please upgrade your plan to add more + servers. + + + + + )}
{data?.description && ( diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index 2f14cfc4..d6746a9e 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -16,13 +16,21 @@ import { BreadcrumbItem, BreadcrumbLink, } from "@/components/ui/breadcrumb"; +import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { CircuitBoard } from "lucide-react"; +import { HelpCircle } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -94,8 +102,38 @@ const Service = ( {data?.appName}
-
- {data?.server?.name || "Dokploy Server"} +
+ + {data?.server?.name || "Dokploy Server"} + + {data?.server?.serverStatus === "inactive" && ( + + + + + + + + You cannot, deploy this application because the server + is inactive, please upgrade your plan to add more + servers. + + + + + )}
{data?.description && (

diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index 50f7b664..12a793a7 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -17,12 +17,20 @@ import { BreadcrumbItem, BreadcrumbLink, } from "@/components/ui/breadcrumb"; +import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; +import { HelpCircle } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -82,8 +90,38 @@ const Mariadb = ( {data?.appName}

-
- {data?.server?.name || "Dokploy Server"} +
+ + {data?.server?.name || "Dokploy Server"} + + {data?.server?.serverStatus === "inactive" && ( + + + + + + + + You cannot, deploy this application because the server + is inactive, please upgrade your plan to add more + servers. + + + + + )}
{data?.description && (

diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index 09b054f5..e5245527 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -17,12 +17,20 @@ import { BreadcrumbItem, BreadcrumbLink, } from "@/components/ui/breadcrumb"; +import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; +import { HelpCircle } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -83,8 +91,38 @@ const Mongo = ( {data?.appName}

-
- {data?.server?.name || "Dokploy Server"} +
+ + {data?.server?.name || "Dokploy Server"} + + {data?.server?.serverStatus === "inactive" && ( + + + + + + + + You cannot, deploy this application because the server + is inactive, please upgrade your plan to add more + servers. + + + + + )}
{data?.description && (

diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index de81b64c..0dd68baa 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -17,12 +17,20 @@ import { BreadcrumbItem, BreadcrumbLink, } from "@/components/ui/breadcrumb"; +import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; +import { HelpCircle } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -81,8 +89,38 @@ const MySql = ( {data?.appName}

-
- {data?.server?.name || "Dokploy Server"} +
+ + {data?.server?.name || "Dokploy Server"} + + {data?.server?.serverStatus === "inactive" && ( + + + + + + + + You cannot, deploy this application because the server + is inactive, please upgrade your plan to add more + servers. + + + + + )}
{data?.description && (

diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index 196ae54c..1affd1d5 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -17,12 +17,20 @@ import { BreadcrumbItem, BreadcrumbLink, } from "@/components/ui/breadcrumb"; +import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; +import { HelpCircle } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -82,8 +90,38 @@ const Postgresql = ( {data?.appName}

-
- {data?.server?.name || "Dokploy Server"} +
+ + {data?.server?.name || "Dokploy Server"} + + {data?.server?.serverStatus === "inactive" && ( + + + + + + + + You cannot, deploy this application because the server + is inactive, please upgrade your plan to add more + servers. + + + + + )}
{data?.description && (

diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index 4b7f2ddc..58e77ff4 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -16,12 +16,20 @@ import { BreadcrumbItem, BreadcrumbLink, } from "@/components/ui/breadcrumb"; +import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; +import { HelpCircle } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -81,8 +89,38 @@ const Redis = ( {data?.appName}

-
- {data?.server?.name || "Dokploy Server"} +
+ + {data?.server?.name || "Dokploy Server"} + + {data?.server?.serverStatus === "inactive" && ( + + + + + + + + You cannot, deploy this application because the server + is inactive, please upgrade your plan to add more + servers. + + + + + )}
{data?.description && (

diff --git a/apps/dokploy/server/api/routers/backup.ts b/apps/dokploy/server/api/routers/backup.ts index 553b7685..c6ae38b6 100644 --- a/apps/dokploy/server/api/routers/backup.ts +++ b/apps/dokploy/server/api/routers/backup.ts @@ -14,6 +14,7 @@ import { findMongoByBackupId, findMySqlByBackupId, findPostgresByBackupId, + findServerById, removeBackupById, removeScheduleBackup, runMariadbBackup, @@ -36,6 +37,25 @@ export const backupRouter = createTRPCRouter({ const backup = await findBackupById(newBackup.backupId); if (IS_CLOUD && backup.enabled) { + const databaseType = backup.databaseType; + let serverId = ""; + if (databaseType === "postgres" && backup.postgres?.serverId) { + serverId = backup.postgres.serverId; + } else if (databaseType === "mysql" && backup.mysql?.serverId) { + serverId = backup.mysql.serverId; + } else if (databaseType === "mongo" && backup.mongo?.serverId) { + serverId = backup.mongo.serverId; + } else if (databaseType === "mariadb" && backup.mariadb?.serverId) { + serverId = backup.mariadb.serverId; + } + const server = await findServerById(serverId); + + if (server.serverStatus === "inactive") { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Server is inactive", + }); + } await schedule({ cronSchedule: backup.schedule, backupId: backup.backupId, diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 99de2e3b..6755d647 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -18,6 +18,7 @@ import { deployMariadb, findMariadbById, findProjectById, + findServerById, removeMariadbById, removeService, startService, @@ -151,6 +152,7 @@ export const mariadbRouter = createTRPCRouter({ message: "You are not authorized to deploy this mariadb", }); } + return deployMariadb(input.mariadbId); }), changeStatus: protectedProcedure diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index 77221212..84e383af 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -1,3 +1,4 @@ +import { updateServersBasedOnQuantity } from "@/pages/api/stripe/webhook"; import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc"; import { db } from "@/server/db"; import { @@ -15,15 +16,17 @@ import { server, } from "@/server/db/schema"; import { + IS_CLOUD, createServer, deleteServer, + findAdminById, findServerById, + findServersByAdminId, haveActiveServices, removeDeploymentsByServerId, serverSetup, updateServerById, } from "@dokploy/server"; -// import { serverSetup } from "@/server/setup/server-setup"; import { TRPCError } from "@trpc/server"; import { and, desc, eq, getTableColumns, isNotNull, sql } from "drizzle-orm"; @@ -32,6 +35,14 @@ export const serverRouter = createTRPCRouter({ .input(apiCreateServer) .mutation(async ({ ctx, input }) => { try { + const admin = await findAdminById(ctx.user.adminId); + const servers = await findServersByAdminId(admin.adminId); + if (IS_CLOUD && servers.length >= admin.serversQuantity) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "You cannot create more servers", + }); + } const project = await createServer(input, ctx.user.adminId); return project; } catch (error) { @@ -77,13 +88,17 @@ export const serverRouter = createTRPCRouter({ return result; }), withSSHKey: protectedProcedure.query(async ({ ctx }) => { - return await db.query.server.findMany({ + const result = await db.query.server.findMany({ orderBy: desc(server.createdAt), - where: and( - isNotNull(server.sshKeyId), - eq(server.adminId, ctx.user.adminId), - ), + where: IS_CLOUD + ? and( + isNotNull(server.sshKeyId), + eq(server.adminId, ctx.user.adminId), + eq(server.serverStatus, "active"), + ) + : and(isNotNull(server.sshKeyId), eq(server.adminId, ctx.user.adminId)), }); + return result; }), setup: protectedProcedure .input(apiFindOneServer) @@ -124,7 +139,12 @@ export const serverRouter = createTRPCRouter({ const currentServer = await findServerById(input.serverId); await removeDeploymentsByServerId(currentServer); await deleteServer(input.serverId); + const admin = await findAdminById(ctx.user.adminId); + await updateServersBasedOnQuantity( + admin.adminId, + admin.serversQuantity, + ); return currentServer; } catch (error) { throw error; @@ -141,6 +161,13 @@ export const serverRouter = createTRPCRouter({ message: "You are not authorized to update this server", }); } + + if (server.serverStatus === "inactive") { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Server is inactive", + }); + } const currentServer = await updateServerById(input.serverId, { ...input, }); diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index ebdc0340..ca1db29d 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -221,6 +221,13 @@ export const settingsRouter = createTRPCRouter({ } if (server.enableDockerCleanup) { + const server = await findServerById(input.serverId); + if (server.serverStatus === "inactive") { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Server is inactive", + }); + } if (IS_CLOUD) { await schedule({ cronSchedule: "0 0 * * *", diff --git a/apps/dokploy/server/api/routers/stripe.ts b/apps/dokploy/server/api/routers/stripe.ts index 429ad29e..17f321e2 100644 --- a/apps/dokploy/server/api/routers/stripe.ts +++ b/apps/dokploy/server/api/routers/stripe.ts @@ -1,4 +1,3 @@ -import { admins } from "@/server/db/schema"; import { getStripeItems } from "@/server/utils/stripe"; import { IS_CLOUD, @@ -7,7 +6,6 @@ import { updateAdmin, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; -import { eq } from "drizzle-orm"; import Stripe from "stripe"; import { z } from "zod"; import { adminProcedure, createTRPCRouter } from "../trpc"; @@ -56,7 +54,7 @@ export const stripeRouter = createTRPCRouter({ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", { apiVersion: "2024-09-30.acacia", }); - // await updateAdmin(ctx.user.a, { + // await updateAdmin(ctx.user.authId, { // stripeCustomerId: null, // stripeSubscriptionId: null, // serversQuantity: 0, @@ -110,55 +108,7 @@ export const stripeRouter = createTRPCRouter({ } }, ), - success: adminProcedure.query(async ({ ctx }) => { - const sessionId = ctx.req.query.sessionId as string; - if (!sessionId) { - throw new Error("No session_id provided"); - } - - // const session = await stripe.checkout.sessions.retrieve(sessionId); - - // if (session.payment_status === "paid") { - // const admin = await findAdminById(ctx.user.adminId); - - // // if (admin.stripeSubscriptionId) { - // // const subscription = await stripe.subscriptions.retrieve( - // // admin.stripeSubscriptionId, - // // ); - // // if (subscription.status === "active") { - // // await stripe.subscriptions.update(admin.stripeSubscriptionId, { - // // cancel_at_period_end: true, - // // }); - // // } - // // } - // console.log("Payment successful!"); - - // const stripeCustomerId = session.customer as string; - // console.log("Stripe Customer ID:", stripeCustomerId); - - // const stripeSubscriptionId = session.subscription as string; - // const suscription = - // await stripe.subscriptions.retrieve(stripeSubscriptionId); - // console.log("Stripe Subscription ID:", stripeSubscriptionId); - - // await db - // ?.update(admins) - // .set({ - // stripeCustomerId, - // stripeSubscriptionId, - // serversQuantity: suscription?.items?.data?.[0]?.quantity ?? 0, - // }) - // .where(eq(admins.adminId, ctx.user.adminId)) - // .returning(); - // } else { - // console.log("Payment not completed or failed."); - // } - - ctx.res.redirect("/dashboard/settings/billing"); - - return true; - }), canCreateMoreServers: adminProcedure.query(async ({ ctx }) => { const admin = await findAdminById(ctx.user.adminId); const servers = await findServersByAdminId(admin.adminId); diff --git a/apps/dokploy/server/utils/deploy.ts b/apps/dokploy/server/utils/deploy.ts index 2dc56921..f0e4cd78 100644 --- a/apps/dokploy/server/utils/deploy.ts +++ b/apps/dokploy/server/utils/deploy.ts @@ -1,7 +1,12 @@ +import { findServerById } from "@dokploy/server"; import type { DeploymentJob } from "../queues/deployments-queue"; export const deploy = async (jobData: DeploymentJob) => { try { + const server = await findServerById(jobData.serverId as string); + if (server.serverStatus === "inactive") { + throw new Error("Server is inactive"); + } const result = await fetch(`${process.env.SERVER_URL}/deploy`, { method: "POST", headers: { diff --git a/apps/schedules/src/utils.ts b/apps/schedules/src/utils.ts index e29e6dc0..65f3cbda 100644 --- a/apps/schedules/src/utils.ts +++ b/apps/schedules/src/utils.ts @@ -3,6 +3,7 @@ import { cleanUpSystemPrune, cleanUpUnusedImages, findBackupById, + findServerById, runMariadbBackup, runMongoBackup, runMySqlBackup, @@ -21,22 +22,47 @@ export const runJobs = async (job: QueueJob) => { const { backupId } = job; const backup = await findBackupById(backupId); const { databaseType, postgres, mysql, mongo, mariadb } = backup; + if (databaseType === "postgres" && postgres) { + const server = await findServerById(postgres.serverId as string); + if (server.serverStatus === "inactive") { + logger.info("Server is inactive"); + return; + } await runPostgresBackup(postgres, backup); } else if (databaseType === "mysql" && mysql) { + const server = await findServerById(mysql.serverId as string); + if (server.serverStatus === "inactive") { + logger.info("Server is inactive"); + return; + } await runMySqlBackup(mysql, backup); } else if (databaseType === "mongo" && mongo) { + const server = await findServerById(mongo.serverId as string); + if (server.serverStatus === "inactive") { + logger.info("Server is inactive"); + return; + } await runMongoBackup(mongo, backup); } else if (databaseType === "mariadb" && mariadb) { + const server = await findServerById(mariadb.serverId as string); + if (server.serverStatus === "inactive") { + logger.info("Server is inactive"); + return; + } await runMariadbBackup(mariadb, backup); } } if (job.type === "server") { const { serverId } = job; + const server = await findServerById(serverId); + if (server.serverStatus === "inactive") { + logger.info("Server is inactive"); + return; + } await cleanUpUnusedImages(serverId); await cleanUpDockerBuilder(serverId); await cleanUpSystemPrune(serverId); - // await sendDockerCleanupNotifications(); } } catch (error) { logger.error(error);