diff --git a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx
index 6e56bdd70..fc8333430 100644
--- a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx
+++ b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx
@@ -91,7 +91,8 @@ export const ShowBilling = () => {
api.stripe.upgradeSubscription.useMutation();
const utils = api.useUtils();
- const [serverQuantity, setServerQuantity] = useState(3);
+ const [hobbyServerQuantity, setHobbyServerQuantity] = useState(1);
+ const [startupServerQuantity, setStartupServerQuantity] = useState(STARTUP_SERVERS_INCLUDED);
const [isAnnual, setIsAnnual] = useState(false);
const [upgradeTier, setUpgradeTier] = useState<"hobby" | "startup" | null>(
null,
@@ -111,6 +112,12 @@ export const ShowBilling = () => {
productId: string,
) => {
const stripe = await stripePromise;
+ const serverQuantity =
+ tier === "startup"
+ ? startupServerQuantity
+ : tier === "hobby"
+ ? hobbyServerQuantity
+ : hobbyServerQuantity;
if (data && data.subscriptions.length === 0) {
createCheckoutSession({
tier,
@@ -679,7 +686,7 @@ export const ShowBilling = () => {
$
{calculatePriceHobby(
- serverQuantity,
+ hobbyServerQuantity,
isAnnual,
).toFixed(2)}
/{isAnnual ? "yr" : "mo"}
@@ -692,7 +699,7 @@ export const ShowBilling = () => {
$
{(
- calculatePriceHobby(serverQuantity, true) / 12
+ calculatePriceHobby(hobbyServerQuantity, true) / 12
).toFixed(2)}
/mo
@@ -724,19 +731,19 @@ export const ShowBilling = () => {
Servers:
- setServerQuantity(
+ setHobbyServerQuantity(
Math.max(
1,
Number(
@@ -750,7 +757,7 @@ export const ShowBilling = () => {
@@ -775,7 +782,7 @@ export const ShowBilling = () => {
onClick={() =>
handleCheckout("hobby", data!.hobbyProductId!)
}
- disabled={serverQuantity < 1}
+ disabled={hobbyServerQuantity < 1}
>
Get Started
@@ -806,7 +813,7 @@ export const ShowBilling = () => {
$
{calculatePriceStartup(
- serverQuantity,
+ startupServerQuantity,
isAnnual,
).toFixed(2)}
/{isAnnual ? "yr" : "mo"}
@@ -819,7 +826,7 @@ export const ShowBilling = () => {
$
{(
- calculatePriceStartup(serverQuantity, true) / 12
+ calculatePriceStartup(startupServerQuantity, true) / 12
).toFixed(2)}
/mo
@@ -856,13 +863,13 @@ export const ShowBilling = () => {
- setServerQuantity(
+ setStartupServerQuantity(
Math.max(
STARTUP_SERVERS_INCLUDED,
Number(
@@ -887,7 +894,7 @@ export const ShowBilling = () => {
variant="outline"
size="icon"
className="h-8 w-8"
- onClick={() => setServerQuantity((q) => q + 1)}
+ onClick={() => setStartupServerQuantity((q) => q + 1)}
>
@@ -917,7 +924,7 @@ export const ShowBilling = () => {
)
}
disabled={
- serverQuantity < STARTUP_SERVERS_INCLUDED
+ startupServerQuantity < STARTUP_SERVERS_INCLUDED
}
>
Get Started
@@ -1009,7 +1016,7 @@ export const ShowBilling = () => {
${" "}
{calculatePrice(
- serverQuantity,
+ hobbyServerQuantity,
isAnnual,
).toFixed(2)}{" "}
USD
@@ -1018,7 +1025,7 @@ export const ShowBilling = () => {
${" "}
{(
- calculatePrice(serverQuantity, isAnnual) / 12
+ calculatePrice(hobbyServerQuantity, isAnnual) / 12
).toFixed(2)}{" "}
/ Month USD
@@ -1026,7 +1033,7 @@ export const ShowBilling = () => {
) : (
${" "}
- {calculatePrice(serverQuantity, isAnnual).toFixed(
+ {calculatePrice(hobbyServerQuantity, isAnnual).toFixed(
2,
)}{" "}
USD
@@ -1071,26 +1078,26 @@ export const ShowBilling = () => {
- {serverQuantity} Servers
+ {hobbyServerQuantity} Servers
{
- setServerQuantity(
+ setHobbyServerQuantity(
e.target.value as unknown as number,
);
}}
@@ -1099,7 +1106,7 @@ export const ShowBilling = () => {
diff --git a/apps/dokploy/server/api/routers/stripe.ts b/apps/dokploy/server/api/routers/stripe.ts
index 109f2aac3..2a6292dfc 100644
--- a/apps/dokploy/server/api/routers/stripe.ts
+++ b/apps/dokploy/server/api/routers/stripe.ts
@@ -21,7 +21,12 @@ import {
STARTUP_PRODUCT_ID,
WEBSITE_URL,
} from "@/server/utils/stripe";
-import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
+import {
+ adminProcedure,
+ createTRPCRouter,
+ protectedProcedure,
+ withPermission,
+} from "../trpc";
export const stripeRouter = createTRPCRouter({
/** Returns the current billing plan for the user's organization. Used to gate features like chat (Startup only). */
@@ -314,16 +319,18 @@ export const stripeRouter = createTRPCRouter({
return { ok: true };
}),
- canCreateMoreServers: adminProcedure.query(async ({ ctx }) => {
- const user = await findUserById(ctx.user.ownerId);
- const servers = await findServersByUserId(user.id);
+ canCreateMoreServers: withPermission("server", "create").query(
+ async ({ ctx }) => {
+ const user = await findUserById(ctx.user.ownerId);
+ const servers = await findServersByUserId(user.id);
- if (!IS_CLOUD) {
- return true;
- }
+ if (!IS_CLOUD) {
+ return true;
+ }
- return servers.length < user.serversQuantity;
- }),
+ return servers.length < user.serversQuantity;
+ },
+ ),
getInvoices: adminProcedure.query(async ({ ctx }) => {
const user = await findUserById(ctx.user.ownerId);
diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts
index 0721bbf85..0b425ee89 100644
--- a/packages/server/src/lib/auth.ts
+++ b/packages/server/src/lib/auth.ts
@@ -116,7 +116,7 @@ const { handler, api } = betterAuth({
emailAndPassword: {
enabled: true,
autoSignIn: !IS_CLOUD,
- requireEmailVerification: IS_CLOUD,
+ requireEmailVerification: IS_CLOUD && process.env.NODE_ENV === "production",
password: {
async hash(password) {
return bcrypt.hashSync(password, 10);