diff --git a/apps/dokploy/__test__/permissions/enterprise-only-resources.test.ts b/apps/dokploy/__test__/permissions/enterprise-only-resources.test.ts
index ecfbc380d..4849a0ba7 100644
--- a/apps/dokploy/__test__/permissions/enterprise-only-resources.test.ts
+++ b/apps/dokploy/__test__/permissions/enterprise-only-resources.test.ts
@@ -42,6 +42,7 @@ const ENTERPRISE_RESOURCES = [
"auditLog",
"vaultProvider",
"dnsProvider",
+ "ai",
];
describe("enterpriseOnlyResources set", () => {
diff --git a/apps/dokploy/__test__/utils/remote-stream.test.ts b/apps/dokploy/__test__/utils/remote-stream.test.ts
index 6f7a3b358..e428de9f3 100644
--- a/apps/dokploy/__test__/utils/remote-stream.test.ts
+++ b/apps/dokploy/__test__/utils/remote-stream.test.ts
@@ -16,7 +16,7 @@ describe("pipeBetweenServers", () => {
target: { serverId: null, command: "sleep 0.3; cat > /dev/null" },
});
expect(bytes).toBe(11);
- });
+ }, 30_000);
it("pipes data larger than the pipe buffer unchanged", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "pipe-"));
@@ -47,7 +47,7 @@ describe("pipeBetweenServers", () => {
target: { serverId: null, command: "echo boom >&2; exit 3" },
}),
).rejects.toThrow("target exited with code 3: boom");
- });
+ }, 30_000);
it("reports a source failure", async () => {
await expect(
@@ -56,5 +56,5 @@ describe("pipeBetweenServers", () => {
target: { serverId: null, command: "cat > /dev/null" },
}),
).rejects.toThrow("source exited with code 2");
- });
+ }, 30_000);
});
diff --git a/apps/dokploy/components/dashboard/settings/ai-form.tsx b/apps/dokploy/components/dashboard/settings/ai-form.tsx
index 797aad23c..abe1a1c3e 100644
--- a/apps/dokploy/components/dashboard/settings/ai-form.tsx
+++ b/apps/dokploy/components/dashboard/settings/ai-form.tsx
@@ -18,7 +18,9 @@ import { HandleAiProviders } from "./handle-ai-providers";
export const AiForm = () => {
const { data: aiConfigs, refetch, isPending } = api.ai.getAll.useQuery();
const { mutateAsync, isPending: isRemoving } = api.ai.delete.useMutation();
+ const { data: permissions } = api.user.getPermissions.useQuery();
const { data: currentUser } = api.user.get.useQuery();
+
const isOrgAdmin =
currentUser?.role === "owner" || currentUser?.role === "admin";
@@ -36,7 +38,9 @@ export const AiForm = () => {
{isOrgAdmin && }
- {aiConfigs && aiConfigs?.length > 0 && }
+ {permissions?.ai.create && aiConfigs && aiConfigs?.length > 0 && (
+
+ )}
@@ -53,7 +57,7 @@ export const AiForm = () => {
You don't have any AI configurations
-
+ {permissions?.ai.create && }
) : (
@@ -70,33 +74,37 @@ export const AiForm = () => {
{config.model}
-
- {
- await mutateAsync({
- aiId: config.aiId,
- })
- .then(() => {
- toast.success("AI deleted successfully");
- refetch();
+ {permissions?.ai.update && (
+
+ )}
+ {permissions?.ai.delete && (
+ {
+ await mutateAsync({
+ aiId: config.aiId,
})
- .catch(() => {
- toast.error("Error deleting AI");
- });
- }}
- >
-
-
+
+
+ )}
diff --git a/apps/dokploy/components/dashboard/settings/handle-ai.tsx b/apps/dokploy/components/dashboard/settings/handle-ai.tsx
index b7cf186e7..0cd73d82a 100644
--- a/apps/dokploy/components/dashboard/settings/handle-ai.tsx
+++ b/apps/dokploy/components/dashboard/settings/handle-ai.tsx
@@ -148,6 +148,7 @@ export const HandleAi = ({ aiId }: Props) => {
{
apiUrl: apiUrl ?? "",
apiKey: apiKey ?? "",
+ ...(aiId ? { aiId } : {}),
},
{
enabled: !!apiUrl && (isLocalOllama || !!apiKey),
@@ -482,6 +483,7 @@ export const HandleAi = ({ aiId }: Props) => {
apiUrl={apiUrl}
apiKey={apiKey}
model={form.watch("model")}
+ aiId={aiId}
/>