diff --git a/apps/dokploy/components/dashboard/settings/git/show-git-providers.tsx b/apps/dokploy/components/dashboard/settings/git/show-git-providers.tsx
index a96bcf26c..2827a9611 100644
--- a/apps/dokploy/components/dashboard/settings/git/show-git-providers.tsx
+++ b/apps/dokploy/components/dashboard/settings/git/show-git-providers.tsx
@@ -5,6 +5,7 @@ import {
ImportIcon,
Loader2,
Trash2,
+ Users,
} from "lucide-react";
import Link from "next/link";
import { toast } from "sonner";
@@ -24,6 +25,13 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
+import { Switch } from "@/components/ui/switch";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
import { api } from "@/utils/api";
import { useUrl } from "@/utils/hooks/use-url";
import { AddBitbucketProvider } from "./bitbucket/add-bitbucket-provider";
@@ -39,6 +47,8 @@ export const ShowGitProviders = () => {
const { data, isPending, refetch } = api.gitProvider.getAll.useQuery();
const { mutateAsync, isPending: isRemoving } =
api.gitProvider.remove.useMutation();
+ const { mutateAsync: toggleShare, isPending: isToggling } =
+ api.gitProvider.toggleShare.useMutation();
const url = useUrl();
const getGitlabUrl = (
@@ -154,10 +164,62 @@ export const ShowGitProviders = () => {
)}
+ {!gitProvider.isOwner && (
+
+
+ Shared
+
+ )}
+ {gitProvider.isOwner && (
+
+
+
+
+
+ {
+ await toggleShare({
+ gitProviderId:
+ gitProvider.gitProviderId,
+ sharedWithOrganization: checked,
+ })
+ .then(() => {
+ toast.success(
+ checked
+ ? "Provider shared with organization"
+ : "Provider unshared",
+ );
+ refetch();
+ })
+ .catch(() => {
+ toast.error(
+ "Error updating sharing",
+ );
+ });
+ }}
+ />
+
+
+
+ Share with entire organization
+
+
+
+ )}
+
{isBitbucket &&
gitProvider.bitbucket?.appPassword &&
!gitProvider.bitbucket?.apiToken ? (
@@ -222,62 +284,71 @@ export const ShowGitProviders = () => {
)}
- {isGithub && haveGithubRequirements && (
-
- )}
+ {gitProvider.isOwner && (
+ <>
+ {isGithub && haveGithubRequirements && (
+
+ )}
- {isGitlab && (
-
- )}
+ {isGitlab && (
+
+ )}
- {isBitbucket && (
-
- )}
+ {isBitbucket && (
+
+ )}
- {isGitea && (
-
- )}
+ {isGitea && (
+
+ )}
- {
- await mutateAsync({
- gitProviderId: gitProvider.gitProviderId,
- })
- .then(() => {
- toast.success(
- "Git Provider deleted successfully",
- );
- refetch();
- })
- .catch(() => {
- toast.error(
- "Error deleting Git Provider",
- );
- });
- }}
- >
-
-
+ {
+ await mutateAsync({
+ gitProviderId:
+ gitProvider.gitProviderId,
+ })
+ .then(() => {
+ toast.success(
+ "Git Provider deleted successfully",
+ );
+ refetch();
+ })
+ .catch(() => {
+ toast.error(
+ "Error deleting Git Provider",
+ );
+ });
+ }}
+ >
+
+
+ >
+ )}
diff --git a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx
index 0617aa68d..37463fd62 100644
--- a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx
+++ b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx
@@ -54,6 +54,7 @@ const notificationBaseSchema = z.object({
appDeploy: z.boolean().default(false),
appBuildError: z.boolean().default(false),
databaseBackup: z.boolean().default(false),
+ dokployBackup: z.boolean().default(false),
volumeBackup: z.boolean().default(false),
dokployRestart: z.boolean().default(false),
dockerCleanup: z.boolean().default(false),
@@ -355,6 +356,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
dockerCleanup: notification.dockerCleanup,
webhookUrl: notification.slack?.webhookUrl,
@@ -369,6 +371,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
botToken: notification.telegram?.botToken,
messageThreadId: notification.telegram?.messageThreadId || "",
@@ -384,6 +387,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
type: notification.notificationType,
webhookUrl: notification.discord?.webhookUrl,
@@ -398,6 +402,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
type: notification.notificationType,
smtpServer: notification.email?.smtpServer,
@@ -416,6 +421,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
type: notification.notificationType,
apiKey: notification.resend?.apiKey,
@@ -431,6 +437,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
type: notification.notificationType,
appToken: notification.gotify?.appToken,
@@ -446,6 +453,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
type: notification.notificationType,
accessToken: notification.ntfy?.accessToken || "",
@@ -462,6 +470,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
type: notification.notificationType,
webhookUrl: notification.mattermost?.webhookUrl,
@@ -477,6 +486,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
type: notification.notificationType,
webhookUrl: notification.lark?.webhookUrl,
name: notification.name,
@@ -490,6 +500,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
type: notification.notificationType,
webhookUrl: notification.teams?.webhookUrl,
@@ -503,6 +514,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
type: notification.notificationType,
endpoint: notification.custom?.endpoint || "",
headers: notification.custom?.headers
@@ -524,6 +536,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: notification.appDeploy,
dokployRestart: notification.dokployRestart,
databaseBackup: notification.databaseBackup,
+ dokployBackup: notification.dokployBackup,
volumeBackup: notification.volumeBackup,
type: notification.notificationType,
userKey: notification.pushover?.userKey,
@@ -562,6 +575,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy,
dokployRestart,
databaseBackup,
+ dokployBackup,
volumeBackup,
dockerCleanup,
serverThreshold,
@@ -573,6 +587,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
webhookUrl: data.webhookUrl,
channel: data.channel,
@@ -588,6 +603,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
botToken: data.botToken,
messageThreadId: data.messageThreadId || "",
@@ -604,6 +620,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
webhookUrl: data.webhookUrl,
decoration: data.decoration,
@@ -619,6 +636,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
smtpServer: data.smtpServer,
smtpPort: data.smtpPort,
@@ -638,6 +656,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
apiKey: data.apiKey,
fromAddress: data.fromAddress,
@@ -654,6 +673,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
serverUrl: data.serverUrl,
appToken: data.appToken,
@@ -670,6 +690,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
serverUrl: data.serverUrl,
accessToken: data.accessToken || "",
@@ -686,6 +707,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
webhookUrl: data.webhookUrl,
channel: data.channel || undefined,
@@ -702,6 +724,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
webhookUrl: data.webhookUrl,
name: data.name,
@@ -716,6 +739,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
webhookUrl: data.webhookUrl,
name: data.name,
@@ -742,6 +766,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
endpoint: data.endpoint,
headers: headersRecord,
@@ -761,6 +786,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
appDeploy: appDeploy,
dokployRestart: dokployRestart,
databaseBackup: databaseBackup,
+ dokployBackup: dokployBackup,
volumeBackup: volumeBackup,
userKey: data.userKey,
apiToken: data.apiToken,
@@ -1856,6 +1882,27 @@ export const HandleNotifications = ({ notificationId }: Props) => {
)}
/>
+ (
+
+
+ Dokploy Backup
+
+ Trigger the action when a dokploy backup is created.
+
+
+
+
+
+
+ )}
+ />
+
{
const { data: projects } = api.project.allForPermissions.useQuery(undefined, {
enabled: isOpen,
});
+ const { data: haveValidLicense } =
+ api.licenseKey.haveValidLicenseKey.useQuery();
+
+ const { data: gitProviders } = api.gitProvider.allForPermissions.useQuery(
+ undefined,
+ {
+ enabled: isOpen && !!haveValidLicense,
+ },
+ );
const { data, refetch } = api.user.one.useQuery(
{
@@ -214,6 +225,7 @@ export const AddUserPermissions = ({ userId, role }: Props) => {
accessedProjects: [],
accessedEnvironments: [],
accessedServices: [],
+ accessedGitProviders: [],
canDeleteEnvironments: false,
canCreateProjects: false,
canCreateServices: false,
@@ -235,6 +247,7 @@ export const AddUserPermissions = ({ userId, role }: Props) => {
accessedProjects: data.accessedProjects || [],
accessedEnvironments: data.accessedEnvironments || [],
accessedServices: data.accessedServices || [],
+ accessedGitProviders: data.accessedGitProviders || [],
canCreateProjects: data.canCreateProjects,
canCreateServices: data.canCreateServices,
canDeleteProjects: data.canDeleteProjects,
@@ -262,6 +275,7 @@ export const AddUserPermissions = ({ userId, role }: Props) => {
accessedProjects: data.accessedProjects || [],
accessedEnvironments: data.accessedEnvironments || [],
accessedServices: data.accessedServices || [],
+ accessedGitProviders: data.accessedGitProviders || [],
canAccessToDocker: data.canAccessToDocker,
canAccessToAPI: data.canAccessToAPI,
canAccessToSSHKeys: data.canAccessToSSHKeys,
@@ -870,6 +884,78 @@ export const AddUserPermissions = ({ userId, role }: Props) => {
)}
/>
+ {haveValidLicense ? (
+ (
+
+
+ Git Providers
+
+ Select the Git Providers that the user can access
+
+
+ {gitProviders?.length === 0 && (
+
+ No git providers found
+
+ )}
+
+ {gitProviders?.map((provider) => (
+
(
+
+
+ {
+ if (checked) {
+ field.onChange([
+ ...(field.value || []),
+ provider.gitProviderId,
+ ]);
+ } else {
+ field.onChange(
+ field.value?.filter(
+ (v) => v !== provider.gitProviderId,
+ ),
+ );
+ }
+ }}
+ />
+
+
+
+ {provider.name}
+
+
+ ({provider.providerType})
+
+
+
+ )}
+ />
+ ))}
+
+
+
+ )}
+ />
+ ) : (
+
+
+
+ )}