Merge pull request #3817 from Dokploy/refactor/support-only-for-startup-plan

Refactor/support only for startup plan
This commit is contained in:
Mauricio Siu
2026-02-27 00:20:54 -06:00
committed by GitHub
6 changed files with 109 additions and 25 deletions

View File

@@ -190,7 +190,7 @@ export const SetupServer = ({ serverId, asButton = false }: Props) => {
Automatic process
</span>
<Link
href="https://docs.dokploy.com/docs/core/multi-server/instructions#requirements"
href="https://docs.dokploy.com/docs/core/remote-servers/instructions#requirements"
target="_blank"
className="text-primary flex flex-row gap-2"
>

View File

@@ -172,7 +172,7 @@ export const CreateSSHKey = () => {
etc.)
</p>
<Link
href="https://docs.dokploy.com/docs/core/multi-server/instructions#requirements"
href="https://docs.dokploy.com/docs/core/remote-servers/instructions#requirements"
target="_blank"
className="text-primary flex flex-row gap-2 mt-2"
>

View File

@@ -11,20 +11,19 @@ interface Props {
export const DashboardLayout = ({ children }: Props) => {
const { data: haveRootAccess } = api.user.haveRootAccess.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery();
const { data: isUserSubscribed } = api.settings.isUserSubscribed.useQuery(
undefined,
{
enabled: isCloud === true,
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
},
);
const { data: currentPlan } = api.stripe.getCurrentPlan.useQuery(undefined, {
enabled: isCloud === true,
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
});
const isChatEnabled = isCloud === true && currentPlan === "startup";
return (
<>
<Page>{children}</Page>
{isCloud === true && isUserSubscribed === true && (
{isChatEnabled && (
<>
<HubSpotWidget />
</>

View File

@@ -21,9 +21,51 @@ import {
STARTUP_PRODUCT_ID,
WEBSITE_URL,
} from "@/server/utils/stripe";
import { adminProcedure, createTRPCRouter } from "../trpc";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
export const stripeRouter = createTRPCRouter({
/** Returns the current billing plan for the user's organization. Used to gate features like chat (Startup only). */
getCurrentPlan: protectedProcedure.query(async ({ ctx }) => {
if (!IS_CLOUD) return null;
const owner = await findUserById(ctx.user.ownerId);
if (!owner?.stripeCustomerId) return null;
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2024-09-30.acacia",
});
const subscriptions = await stripe.subscriptions.list({
customer: owner.stripeCustomerId,
status: "active",
expand: ["data.items.data.price"],
});
const activeSub = subscriptions.data[0];
if (!activeSub) return null;
const priceIds = activeSub.items.data.map(
(item) => (item.price as Stripe.Price).id,
);
if (
priceIds.some(
(id) =>
id === STARTUP_BASE_PRICE_MONTHLY_ID ||
id === STARTUP_BASE_PRICE_ANNUAL_ID,
)
) {
return "startup" as const;
}
if (
priceIds.some(
(id) => id === HOBBY_PRICE_MONTHLY_ID || id === HOBBY_PRICE_ANNUAL_ID,
)
) {
return "hobby" as const;
}
if (priceIds.some((id) => LEGACY_PRICE_IDS.includes(id))) {
return "legacy" as const;
}
return null;
}),
getProducts: adminProcedure.query(async ({ ctx }) => {
const user = await findUserById(ctx.user.ownerId);
const stripeCustomerId = user.stripeCustomerId;

View File

@@ -281,17 +281,43 @@ const installRequirements = async (
.on("error", (err) => {
client.end();
if (err.level === "client-authentication") {
onData?.(
`Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`,
);
const technicalDetail = `Error: ${err.message} ${err.level}`;
const friendlyMessage = [
"",
"❌ Couldn't connect to your server — the SSH key was not accepted.",
"",
"This usually means the key doesn't match what's on the server, or the key format is invalid.",
"",
`Technical details: ${technicalDetail}`,
"",
"💡 Hints:",
" • Check that the SSH key you added in Dokploy is the same one installed on the server (e.g. in ~/.ssh/authorized_keys).",
" • Try generating a new SSH key in Dokploy and add only the public key to the server, then try again.",
" • Make sure to follow the instructions on the Setup Server Button on the SSH Keys tab",
].join("\n");
onData?.(friendlyMessage);
reject(
new Error(
`Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`,
`Authentication failed: Invalid SSH private key. ${technicalDetail}`,
),
);
} else {
onData?.(`SSH connection error: ${err.message} ${err.level}`);
reject(new Error(`SSH connection error: ${err.message}`));
const technicalDetail = `${err.message} ${err.level ?? ""}`.trim();
const friendlyMessage = [
"",
"❌ Couldn't connect to your server.",
"",
"The connection failed before setup could run. Common causes: wrong IP or port, firewall blocking access, or the server is offline.",
"",
`Technical details: ${technicalDetail}`,
"",
"💡 Hints:",
" • Check that the server IP address and SSH port are correct and the server is powered on.",
" • If the server is in a private network, ensure Dokploy can reach it (VPN, firewall rules, or correct security groups).",
" • Make sure the SSH port (usually 22) is open and the SSH service is running on the server.",
].join("\n");
onData?.(friendlyMessage);
reject(new Error(`SSH connection error: ${technicalDetail}`));
}
})
.connect({

View File

@@ -201,14 +201,31 @@ export const execAsyncRemote = async (
.on("error", (err) => {
conn.end();
if (err.level === "client-authentication") {
const technicalDetail = `Error: ${err.message} ${err.level}`;
const friendlyMessage = [
"",
"❌ Couldn't connect to your server — the SSH key was not accepted.",
"",
"This usually means the key doesn't match what's on the server, or the key format is invalid.",
"",
`Technical details: ${technicalDetail}`,
"",
"💡 Hints:",
" • Check that the SSH key you added in Dokploy is the same one installed on the server (e.g. in ~/.ssh/authorized_keys).",
" • Try generating a new SSH key in Dokploy and add only the public key to the server, then try again.",
" • 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.",
].join("\n");
const errorMsg = `Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`;
onData?.(errorMsg);
onData?.(friendlyMessage);
reject(
new ExecError(errorMsg, {
command,
serverId,
originalError: err,
}),
new ExecError(
`Authentication failed: Invalid SSH private key. ${friendlyMessage}`,
{
command,
serverId,
originalError: err,
},
),
);
} else {
const errorMsg = `SSH connection error: ${err.message}`;