Skip to content

Commit 8ab139e

Browse files
authored
Merge pull request #3817 from Dokploy/refactor/support-only-for-startup-plan
Refactor/support only for startup plan
2 parents e90d8c0 + 55c04b1 commit 8ab139e

6 files changed

Lines changed: 109 additions & 25 deletions

File tree

apps/dokploy/components/dashboard/settings/servers/setup-server.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export const SetupServer = ({ serverId, asButton = false }: Props) => {
190190
Automatic process
191191
</span>
192192
<Link
193-
href="https://docs.dokploy.com/docs/core/multi-server/instructions#requirements"
193+
href="https://docs.dokploy.com/docs/core/remote-servers/instructions#requirements"
194194
target="_blank"
195195
className="text-primary flex flex-row gap-2"
196196
>

apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-ssh-key.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export const CreateSSHKey = () => {
172172
etc.)
173173
</p>
174174
<Link
175-
href="https://docs.dokploy.com/docs/core/multi-server/instructions#requirements"
175+
href="https://docs.dokploy.com/docs/core/remote-servers/instructions#requirements"
176176
target="_blank"
177177
className="text-primary flex flex-row gap-2 mt-2"
178178
>

apps/dokploy/components/layouts/dashboard-layout.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,19 @@ interface Props {
1111
export const DashboardLayout = ({ children }: Props) => {
1212
const { data: haveRootAccess } = api.user.haveRootAccess.useQuery();
1313
const { data: isCloud } = api.settings.isCloud.useQuery();
14-
const { data: isUserSubscribed } = api.settings.isUserSubscribed.useQuery(
15-
undefined,
16-
{
17-
enabled: isCloud === true,
18-
refetchOnWindowFocus: false,
19-
refetchOnMount: false,
20-
refetchOnReconnect: false,
21-
},
22-
);
14+
const { data: currentPlan } = api.stripe.getCurrentPlan.useQuery(undefined, {
15+
enabled: isCloud === true,
16+
refetchOnWindowFocus: false,
17+
refetchOnMount: false,
18+
refetchOnReconnect: false,
19+
});
20+
21+
const isChatEnabled = isCloud === true && currentPlan === "startup";
2322

2423
return (
2524
<>
2625
<Page>{children}</Page>
27-
{isCloud === true && isUserSubscribed === true && (
26+
{isChatEnabled && (
2827
<>
2928
<HubSpotWidget />
3029
</>

apps/dokploy/server/api/routers/stripe.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,51 @@ import {
2121
STARTUP_PRODUCT_ID,
2222
WEBSITE_URL,
2323
} from "@/server/utils/stripe";
24-
import { adminProcedure, createTRPCRouter } from "../trpc";
24+
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
2525

2626
export const stripeRouter = createTRPCRouter({
27+
/** Returns the current billing plan for the user's organization. Used to gate features like chat (Startup only). */
28+
getCurrentPlan: protectedProcedure.query(async ({ ctx }) => {
29+
if (!IS_CLOUD) return null;
30+
const owner = await findUserById(ctx.user.ownerId);
31+
if (!owner?.stripeCustomerId) return null;
32+
33+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
34+
apiVersion: "2024-09-30.acacia",
35+
});
36+
const subscriptions = await stripe.subscriptions.list({
37+
customer: owner.stripeCustomerId,
38+
status: "active",
39+
expand: ["data.items.data.price"],
40+
});
41+
const activeSub = subscriptions.data[0];
42+
if (!activeSub) return null;
43+
44+
const priceIds = activeSub.items.data.map(
45+
(item) => (item.price as Stripe.Price).id,
46+
);
47+
if (
48+
priceIds.some(
49+
(id) =>
50+
id === STARTUP_BASE_PRICE_MONTHLY_ID ||
51+
id === STARTUP_BASE_PRICE_ANNUAL_ID,
52+
)
53+
) {
54+
return "startup" as const;
55+
}
56+
if (
57+
priceIds.some(
58+
(id) => id === HOBBY_PRICE_MONTHLY_ID || id === HOBBY_PRICE_ANNUAL_ID,
59+
)
60+
) {
61+
return "hobby" as const;
62+
}
63+
if (priceIds.some((id) => LEGACY_PRICE_IDS.includes(id))) {
64+
return "legacy" as const;
65+
}
66+
return null;
67+
}),
68+
2769
getProducts: adminProcedure.query(async ({ ctx }) => {
2870
const user = await findUserById(ctx.user.ownerId);
2971
const stripeCustomerId = user.stripeCustomerId;

packages/server/src/setup/server-setup.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,17 +281,43 @@ const installRequirements = async (
281281
.on("error", (err) => {
282282
client.end();
283283
if (err.level === "client-authentication") {
284-
onData?.(
285-
`Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`,
286-
);
284+
const technicalDetail = `Error: ${err.message} ${err.level}`;
285+
const friendlyMessage = [
286+
"",
287+
"❌ Couldn't connect to your server — the SSH key was not accepted.",
288+
"",
289+
"This usually means the key doesn't match what's on the server, or the key format is invalid.",
290+
"",
291+
`Technical details: ${technicalDetail}`,
292+
"",
293+
"💡 Hints:",
294+
" • Check that the SSH key you added in Dokploy is the same one installed on the server (e.g. in ~/.ssh/authorized_keys).",
295+
" • Try generating a new SSH key in Dokploy and add only the public key to the server, then try again.",
296+
" • Make sure to follow the instructions on the Setup Server Button on the SSH Keys tab",
297+
].join("\n");
298+
onData?.(friendlyMessage);
287299
reject(
288300
new Error(
289-
`Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`,
301+
`Authentication failed: Invalid SSH private key. ${technicalDetail}`,
290302
),
291303
);
292304
} else {
293-
onData?.(`SSH connection error: ${err.message} ${err.level}`);
294-
reject(new Error(`SSH connection error: ${err.message}`));
305+
const technicalDetail = `${err.message} ${err.level ?? ""}`.trim();
306+
const friendlyMessage = [
307+
"",
308+
"❌ Couldn't connect to your server.",
309+
"",
310+
"The connection failed before setup could run. Common causes: wrong IP or port, firewall blocking access, or the server is offline.",
311+
"",
312+
`Technical details: ${technicalDetail}`,
313+
"",
314+
"💡 Hints:",
315+
" • Check that the server IP address and SSH port are correct and the server is powered on.",
316+
" • If the server is in a private network, ensure Dokploy can reach it (VPN, firewall rules, or correct security groups).",
317+
" • Make sure the SSH port (usually 22) is open and the SSH service is running on the server.",
318+
].join("\n");
319+
onData?.(friendlyMessage);
320+
reject(new Error(`SSH connection error: ${technicalDetail}`));
295321
}
296322
})
297323
.connect({

packages/server/src/utils/process/execAsync.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,31 @@ export const execAsyncRemote = async (
201201
.on("error", (err) => {
202202
conn.end();
203203
if (err.level === "client-authentication") {
204+
const technicalDetail = `Error: ${err.message} ${err.level}`;
205+
const friendlyMessage = [
206+
"",
207+
"❌ Couldn't connect to your server — the SSH key was not accepted.",
208+
"",
209+
"This usually means the key doesn't match what's on the server, or the key format is invalid.",
210+
"",
211+
`Technical details: ${technicalDetail}`,
212+
"",
213+
"💡 Hints:",
214+
" • Check that the SSH key you added in Dokploy is the same one installed on the server (e.g. in ~/.ssh/authorized_keys).",
215+
" • Try generating a new SSH key in Dokploy and add only the public key to the server, then try again.",
216+
" • Make sure to follow the instructions on the Setup Server Button on the SSH Keys tab and then click on deployments tab and check the logs for more details.",
217+
].join("\n");
204218
const errorMsg = `Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`;
205-
onData?.(errorMsg);
219+
onData?.(friendlyMessage);
206220
reject(
207-
new ExecError(errorMsg, {
208-
command,
209-
serverId,
210-
originalError: err,
211-
}),
221+
new ExecError(
222+
`Authentication failed: Invalid SSH private key. ${friendlyMessage}`,
223+
{
224+
command,
225+
serverId,
226+
originalError: err,
227+
},
228+
),
212229
);
213230
} else {
214231
const errorMsg = `SSH connection error: ${err.message}`;

0 commit comments

Comments
 (0)