From 2d40b2dfe586ffa077ea374e0a35499f2783c8b4 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Fri, 18 Oct 2024 01:43:45 -0600
Subject: [PATCH] feat: add stripe integration
---
.../components/layouts/settings-layout.tsx | 12 ++++
apps/dokploy/package.json | 1 +
.../pages/dashboard/settings/billing.tsx | 66 +++++++++++++++++++
.../pages/dashboard/settings/cluster.tsx | 1 -
apps/dokploy/server/api/root.ts | 2 +
apps/dokploy/server/api/routers/stripe.ts | 22 +++++++
pnpm-lock.yaml | 12 ++++
7 files changed, 115 insertions(+), 1 deletion(-)
create mode 100644 apps/dokploy/pages/dashboard/settings/billing.tsx
create mode 100644 apps/dokploy/server/api/routers/stripe.ts
diff --git a/apps/dokploy/components/layouts/settings-layout.tsx b/apps/dokploy/components/layouts/settings-layout.tsx
index a44278d1..c6880446 100644
--- a/apps/dokploy/components/layouts/settings-layout.tsx
+++ b/apps/dokploy/components/layouts/settings-layout.tsx
@@ -80,6 +80,7 @@ export const SettingsLayout = ({ children }: Props) => {
icon: ListMusic,
href: "/dashboard/settings/registry",
},
+
...(!isCloud
? [
{
@@ -102,6 +103,16 @@ export const SettingsLayout = ({ children }: Props) => {
icon: Server,
href: "/dashboard/settings/servers",
},
+ ...(isCloud
+ ? [
+ {
+ title: "Billing",
+ label: "",
+ icon: CreditCardIcon,
+ href: "/dashboard/settings/billing",
+ },
+ ]
+ : []),
]
: []),
...(user?.canAccessToSSHKeys
@@ -137,6 +148,7 @@ import {
Activity,
Bell,
BoxesIcon,
+ CreditCardIcon,
Database,
GitBranch,
KeyIcon,
diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json
index 79da59e3..66dd76e1 100644
--- a/apps/dokploy/package.json
+++ b/apps/dokploy/package.json
@@ -34,6 +34,7 @@
"test": "vitest --config __test__/vitest.config.ts"
},
"dependencies": {
+ "stripe": "17.2.0",
"@dokploy/server": "workspace:*",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-yaml": "^6.1.1",
diff --git a/apps/dokploy/pages/dashboard/settings/billing.tsx b/apps/dokploy/pages/dashboard/settings/billing.tsx
new file mode 100644
index 00000000..45d1159b
--- /dev/null
+++ b/apps/dokploy/pages/dashboard/settings/billing.tsx
@@ -0,0 +1,66 @@
+import { ShowNodes } from "@/components/dashboard/settings/cluster/nodes/show-nodes";
+import { DashboardLayout } from "@/components/layouts/dashboard-layout";
+import { SettingsLayout } from "@/components/layouts/settings-layout";
+import { api } from "@/utils/api";
+import { IS_CLOUD, validateRequest } from "@dokploy/server";
+import type { GetServerSidePropsContext } from "next";
+import React, { type ReactElement } from "react";
+
+const Page = () => {
+ const { data } = api.stripe.getProducts.useQuery();
+ console.log(data);
+ return (
+
+ {data?.map((product) => (
+
+
{product.name}
+ {product.description && (
+
{product.description}
+ )}
+
+ Price: {product.default_price.unit_amount / 100}{" "}
+ {product.default_price.currency.toUpperCase()}
+
+ {/*
*/}
+
+ ))}
+
+ );
+};
+
+export default Page;
+
+Page.getLayout = (page: ReactElement) => {
+ return (
+
+ {page}
+
+ );
+};
+export async function getServerSideProps(
+ ctx: GetServerSidePropsContext<{ serviceId: string }>,
+) {
+ if (!IS_CLOUD) {
+ return {
+ redirect: {
+ permanent: true,
+ destination: "/dashboard/projects",
+ },
+ };
+ }
+ const { user, session } = await validateRequest(ctx.req, ctx.res);
+ if (!user || user.rol === "user") {
+ return {
+ redirect: {
+ permanent: true,
+ destination: "/",
+ },
+ };
+ }
+
+ return {
+ props: {},
+ };
+}
diff --git a/apps/dokploy/pages/dashboard/settings/cluster.tsx b/apps/dokploy/pages/dashboard/settings/cluster.tsx
index 5551b999..b7c7ed87 100644
--- a/apps/dokploy/pages/dashboard/settings/cluster.tsx
+++ b/apps/dokploy/pages/dashboard/settings/cluster.tsx
@@ -1,5 +1,4 @@
import { ShowNodes } from "@/components/dashboard/settings/cluster/nodes/show-nodes";
-import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/show-registry";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { SettingsLayout } from "@/components/layouts/settings-layout";
import { IS_CLOUD, validateRequest } from "@dokploy/server";
diff --git a/apps/dokploy/server/api/root.ts b/apps/dokploy/server/api/root.ts
index ad08911d..1b67d350 100644
--- a/apps/dokploy/server/api/root.ts
+++ b/apps/dokploy/server/api/root.ts
@@ -29,6 +29,7 @@ import { securityRouter } from "./routers/security";
import { serverRouter } from "./routers/server";
import { settingsRouter } from "./routers/settings";
import { sshRouter } from "./routers/ssh-key";
+import { stripeRouter } from "./routers/stripe";
import { userRouter } from "./routers/user";
/**
@@ -69,6 +70,7 @@ export const appRouter = createTRPCRouter({
gitlab: gitlabRouter,
github: githubRouter,
server: serverRouter,
+ stripe: stripeRouter,
});
// export type definition of API
diff --git a/apps/dokploy/server/api/routers/stripe.ts b/apps/dokploy/server/api/routers/stripe.ts
new file mode 100644
index 00000000..ec3a35df
--- /dev/null
+++ b/apps/dokploy/server/api/routers/stripe.ts
@@ -0,0 +1,22 @@
+import Stripe from "stripe";
+import { adminProcedure, createTRPCRouter } from "../trpc";
+
+export const stripeRouter = createTRPCRouter({
+ getProducts: adminProcedure.query(async () => {
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", {
+ apiVersion: "2024-09-30.acacia",
+ });
+
+ const products = await stripe.products.list({
+ expand: ["data.default_price"],
+ });
+ console.log(products);
+ return products.data;
+ }),
+});
+// {
+// "Parallelism": 1,
+// "Delay": 10000000000,
+// "FailureAction": "rollback",
+// "Order": "start-first"
+// }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 11189f2f..be31816a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -353,6 +353,9 @@ importers:
ssh2:
specifier: 1.15.0
version: 1.15.0
+ stripe:
+ specifier: 17.2.0
+ version: 17.2.0
superjson:
specifier: ^2.2.1
version: 2.2.1
@@ -7641,6 +7644,10 @@ packages:
strip-literal@2.1.0:
resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==}
+ stripe@17.2.0:
+ resolution: {integrity: sha512-KuDplY9WrNKi07+uEFeguis/Mh+HC+hfEMy8P5snhQzCXUv01o+4KcIJ9auFxpv4Odp2R2krs9rZ9PhQl8+Sdw==}
+ engines: {node: '>=12.*'}
+
style-mod@4.1.2:
resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==}
@@ -16297,6 +16304,11 @@ snapshots:
dependencies:
js-tokens: 9.0.0
+ stripe@17.2.0:
+ dependencies:
+ '@types/node': 20.14.10
+ qs: 6.12.3
+
style-mod@4.1.2: {}
style-to-object@0.4.4: