refactor: update webhooks and added validation to prevent deploy when the server is inactive

This commit is contained in:
Mauricio Siu 2024-10-21 00:34:16 -06:00
parent 1907e7e59c
commit fbda00f059
18 changed files with 440 additions and 202 deletions

View File

@ -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),
}),
]);

View File

@ -96,7 +96,6 @@ export const ShowBilling = () => {
)}
</div>
)}
{products?.map((product) => {
const featured = true;
return (

View File

@ -118,6 +118,7 @@ export const ShowServers = () => {
<TableBody>
{data?.map((server) => {
const canDelete = server.totalSum === 0;
const isActive = server.serverStatus === "active";
return (
<TableRow key={server.serverId}>
<TableCell className="w-[100px]">{server.name}</TableCell>
@ -164,18 +165,25 @@ export const ShowServers = () => {
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
{server.sshKeyId && (
<TerminalModal serverId={server.serverId}>
<span>Enter the terminal</span>
</TerminalModal>
{isActive && (
<>
{server.sshKeyId && (
<TerminalModal serverId={server.serverId}>
<span>Enter the terminal</span>
</TerminalModal>
)}
<SetupServer serverId={server.serverId} />
<UpdateServer serverId={server.serverId} />
{server.sshKeyId && (
<ShowServerActions
serverId={server.serverId}
/>
)}
</>
)}
<SetupServer serverId={server.serverId} />
<UpdateServer serverId={server.serverId} />
{server.sshKeyId && (
<ShowServerActions serverId={server.serverId} />
)}
<DialogAction
disabled={!canDelete}
title={
@ -220,17 +228,21 @@ export const ShowServers = () => {
</DropdownMenuItem>
</DialogAction>
{server.sshKeyId && (
{isActive && (
<>
<DropdownMenuSeparator />
<DropdownMenuLabel>Extra</DropdownMenuLabel>
{server.sshKeyId && (
<>
<DropdownMenuSeparator />
<DropdownMenuLabel>Extra</DropdownMenuLabel>
<ShowTraefikFileSystemModal
serverId={server.serverId}
/>
<ShowDockerContainersModal
serverId={server.serverId}
/>
<ShowTraefikFileSystemModal
serverId={server.serverId}
/>
<ShowDockerContainersModal
serverId={server.serverId}
/>
</>
)}
</>
)}
</DropdownMenuContent>

View File

@ -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);
}
}
};

View File

@ -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 = (
</h1>
<span className="text-sm">{data?.appName}</span>
</div>
<div>
<Badge>{data?.server?.name || "Dokploy Server"}</Badge>
<div className="flex flex-row h-fit w-fit gap-2">
<Badge
variant={
data?.server?.serverStatus === "active"
? "default"
: "destructive"
}
>
{data?.server?.name || "Dokploy Server"}
</Badge>
{data?.server?.serverStatus === "inactive" && (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Label className="break-all w-fit flex flex-row gap-1 items-center">
<HelpCircle className="size-4 text-muted-foreground" />
</Label>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
You cannot, deploy this application because the server
is inactive, please upgrade your plan to add more
servers.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
{data?.description && (

View File

@ -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 = (
</h1>
<span className="text-sm">{data?.appName}</span>
</div>
<div>
<Badge>{data?.server?.name || "Dokploy Server"}</Badge>
<div className="flex flex-row h-fit w-fit gap-2">
<Badge
variant={
data?.server?.serverStatus === "active"
? "default"
: "destructive"
}
>
{data?.server?.name || "Dokploy Server"}
</Badge>
{data?.server?.serverStatus === "inactive" && (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Label className="break-all w-fit flex flex-row gap-1 items-center">
<HelpCircle className="size-4 text-muted-foreground" />
</Label>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
You cannot, deploy this application because the server
is inactive, please upgrade your plan to add more
servers.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
{data?.description && (
<p className="text-sm text-muted-foreground max-w-6xl">

View File

@ -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 = (
</h1>
<span className="text-sm">{data?.appName}</span>
</div>
<div>
<Badge>{data?.server?.name || "Dokploy Server"}</Badge>
<div className="flex flex-row h-fit w-fit gap-2">
<Badge
variant={
data?.server?.serverStatus === "active"
? "default"
: "destructive"
}
>
{data?.server?.name || "Dokploy Server"}
</Badge>
{data?.server?.serverStatus === "inactive" && (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Label className="break-all w-fit flex flex-row gap-1 items-center">
<HelpCircle className="size-4 text-muted-foreground" />
</Label>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
You cannot, deploy this application because the server
is inactive, please upgrade your plan to add more
servers.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
{data?.description && (
<p className="text-sm text-muted-foreground max-w-6xl">

View File

@ -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 = (
</h1>
<span className="text-sm">{data?.appName}</span>
</div>
<div>
<Badge>{data?.server?.name || "Dokploy Server"}</Badge>
<div className="flex flex-row h-fit w-fit gap-2">
<Badge
variant={
data?.server?.serverStatus === "active"
? "default"
: "destructive"
}
>
{data?.server?.name || "Dokploy Server"}
</Badge>
{data?.server?.serverStatus === "inactive" && (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Label className="break-all w-fit flex flex-row gap-1 items-center">
<HelpCircle className="size-4 text-muted-foreground" />
</Label>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
You cannot, deploy this application because the server
is inactive, please upgrade your plan to add more
servers.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
{data?.description && (
<p className="text-sm text-muted-foreground max-w-6xl">

View File

@ -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 = (
</h1>
<span className="text-sm">{data?.appName}</span>
</div>
<div>
<Badge>{data?.server?.name || "Dokploy Server"}</Badge>
<div className="flex flex-row h-fit w-fit gap-2">
<Badge
variant={
data?.server?.serverStatus === "active"
? "default"
: "destructive"
}
>
{data?.server?.name || "Dokploy Server"}
</Badge>
{data?.server?.serverStatus === "inactive" && (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Label className="break-all w-fit flex flex-row gap-1 items-center">
<HelpCircle className="size-4 text-muted-foreground" />
</Label>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
You cannot, deploy this application because the server
is inactive, please upgrade your plan to add more
servers.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
{data?.description && (
<p className="text-sm text-muted-foreground max-w-6xl">

View File

@ -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 = (
</h1>
<span className="text-sm">{data?.appName}</span>
</div>
<div>
<Badge>{data?.server?.name || "Dokploy Server"}</Badge>
<div className="flex flex-row h-fit w-fit gap-2">
<Badge
variant={
data?.server?.serverStatus === "active"
? "default"
: "destructive"
}
>
{data?.server?.name || "Dokploy Server"}
</Badge>
{data?.server?.serverStatus === "inactive" && (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Label className="break-all w-fit flex flex-row gap-1 items-center">
<HelpCircle className="size-4 text-muted-foreground" />
</Label>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
You cannot, deploy this application because the server
is inactive, please upgrade your plan to add more
servers.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
{data?.description && (
<p className="text-sm text-muted-foreground max-w-6xl">

View File

@ -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 = (
</h1>
<span className="text-sm">{data?.appName}</span>
</div>
<div>
<Badge>{data?.server?.name || "Dokploy Server"}</Badge>
<div className="flex flex-row h-fit w-fit gap-2">
<Badge
variant={
data?.server?.serverStatus === "active"
? "default"
: "destructive"
}
>
{data?.server?.name || "Dokploy Server"}
</Badge>
{data?.server?.serverStatus === "inactive" && (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Label className="break-all w-fit flex flex-row gap-1 items-center">
<HelpCircle className="size-4 text-muted-foreground" />
</Label>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
You cannot, deploy this application because the server
is inactive, please upgrade your plan to add more
servers.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
{data?.description && (
<p className="text-sm text-muted-foreground max-w-6xl">

View File

@ -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,

View File

@ -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

View File

@ -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,
});

View File

@ -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 * * *",

View File

@ -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);

View File

@ -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: {

View File

@ -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);