diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx new file mode 100644 index 00000000..00bf42b9 --- /dev/null +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -0,0 +1,119 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { PenBoxIcon, Plus, SquarePen } from "lucide-react"; +import { useEffect, useState } from "react"; +import { api } from "@/utils/api"; +import { toast } from "sonner"; + +interface Props { + organizationId?: string; + children?: React.ReactNode; +} +export function AddOrganization({ organizationId, children }: Props) { + const utils = api.useUtils(); + const { data: organization } = api.organization.one.useQuery( + { + organizationId: organizationId ?? "", + }, + { + enabled: !!organizationId, + }, + ); + const { mutateAsync, isLoading } = organizationId + ? api.organization.update.useMutation() + : api.organization.create.useMutation(); + const [open, setOpen] = useState(false); + const [name, setName] = useState(""); + + useEffect(() => { + if (organization) { + setName(organization.name); + } + }, [organization]); + const handleSubmit = async () => { + await mutateAsync({ name, organizationId: organizationId ?? "" }) + .then(() => { + setOpen(false); + toast.success( + `Organization ${organizationId ? "updated" : "created"} successfully`, + ); + utils.organization.all.invalidate(); + }) + .catch((error) => { + console.error(error); + toast.error( + `Failed to ${organizationId ? "update" : "create"} organization`, + ); + }); + }; + return ( + + + {organizationId ? ( + e.preventDefault()} + > + + + ) : ( + { + setOpen(true); + }} + onSelect={(e) => e.preventDefault()} + > + + + + + Add organization + + + )} + + + + + {organizationId ? "Update organization" : "Add organization"} + + + {organizationId + ? "Update the organization name" + : "Create a new organization to manage your projects."} + + + + + + Name + + setName(e.target.value)} + className="col-span-3" + /> + + + + + {organizationId ? "Update organization" : "Create organization"} + + + + + ); +} diff --git a/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx b/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx index c6546f2d..58cad791 100644 --- a/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx +++ b/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx @@ -86,6 +86,7 @@ export const AddCertificate = () => { privateKey: data.privateKey, autoRenew: data.autoRenew, serverId: data.serverId, + organizationId: "", }) .then(async () => { toast.success("Certificate Created"); diff --git a/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx b/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx index fc48134a..1a8fe918 100644 --- a/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx +++ b/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx @@ -78,6 +78,7 @@ export const HandleSSHKeys = ({ sshKeyId }: Props) => { const onSubmit = async (data: SSHKey) => { await mutateAsync({ ...data, + organizationId: "", sshKeyId: sshKeyId || "", }) .then(async () => { diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index 9dfbfc93..8aa2a37b 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -76,10 +76,11 @@ export const ShowUsers = () => { Email - Status + Role 2FA + {/* Status */} - Expiration + Created At Actions @@ -89,30 +90,32 @@ export const ShowUsers = () => { return ( - {user.auth.email} + {user.user.email} - {user.isRegistered - ? "Registered" - : "Not Registered"} + {user.role} - {user.auth.is2FAEnabled + {user.user.is2FAEnabled ? "2FA Enabled" : "2FA Not Enabled"} + {/* + + {format(new Date(user.createdAt), "PPpp")} + + */} - {format( - new Date(user.expirationDate), - "PPpp", - )} + {format(new Date(user.createdAt), "PPpp")} @@ -131,7 +134,7 @@ export const ShowUsers = () => { Actions - {!user.isRegistered && ( + {/* {!user.isRegistered && ( { @@ -145,42 +148,44 @@ export const ShowUsers = () => { > Copy Invitation - )} + )} */} - {user.isRegistered && ( + {/* {user.isRegistered && ( - )} + )} */} - { - await mutateAsync({ - authId: user.authId, - }) - .then(() => { - toast.success( - "User deleted successfully", - ); - refetch(); + {user.role !== "owner" && ( + { + await mutateAsync({ + userId: user.userId, }) - .catch(() => { - toast.error( - "Error deleting destination", - ); - }); - }} - > - e.preventDefault()} + .then(() => { + toast.success( + "User deleted successfully", + ); + refetch(); + }) + .catch(() => { + toast.error( + "Error deleting destination", + ); + }); + }} > - Delete User - - + e.preventDefault()} + > + Delete User + + + )} diff --git a/apps/dokploy/components/dashboard/settings/web-domain.tsx b/apps/dokploy/components/dashboard/settings/web-domain.tsx index 40198147..0b002e9d 100644 --- a/apps/dokploy/components/dashboard/settings/web-domain.tsx +++ b/apps/dokploy/components/dashboard/settings/web-domain.tsx @@ -52,7 +52,7 @@ type AddServerDomain = z.infer; export const WebDomain = () => { const { t } = useTranslation("settings"); - const { data: user, refetch } = api.admin.one.useQuery(); + const { data, refetch } = api.auth.get.useQuery(); const { mutateAsync, isLoading } = api.settings.assignDomainServer.useMutation(); @@ -65,14 +65,14 @@ export const WebDomain = () => { resolver: zodResolver(addServerDomain), }); useEffect(() => { - if (user) { + if (data) { form.reset({ - domain: user?.host || "", - certificateType: user?.certificateType, - letsEncryptEmail: user?.letsEncryptEmail || "", + domain: data?.user?.host || "", + certificateType: data?.user?.certificateType, + letsEncryptEmail: data?.user?.letsEncryptEmail || "", }); } - }, [form, form.reset, user]); + }, [form, form.reset, data]); const onSubmit = async (data: AddServerDomain) => { await mutateAsync({ diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 99938a5d..8e8a74dc 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -1,6 +1,7 @@ "use client"; import { Activity, + AudioWaveform, BarChartHorizontalBigIcon, Bell, BlocksIcon, @@ -8,6 +9,7 @@ import { Boxes, ChevronRight, CircleHelp, + Command, CreditCard, Database, Folder, @@ -16,11 +18,13 @@ import { GitBranch, HeartIcon, KeyRound, + Loader2, type LucideIcon, Package, PieChart, Server, ShieldCheck, + Trash2, User, Users, } from "lucide-react"; @@ -480,37 +484,207 @@ interface Props { function LogoWrapper() { return ; } +import { ChevronsUpDown, Plus } from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { AddOrganization } from "../dashboard/organization/handle-organization"; +import { authClient } from "@/lib/auth"; +import { DialogAction } from "../shared/dialog-action"; +import { Button } from "../ui/button"; +import { toast } from "sonner"; +const data = { + user: { + name: "shadcn", + email: "m@example.com", + avatar: "/avatars/shadcn.jpg", + }, + teams: [ + { + name: "Acme Inc", + logo: GalleryVerticalEnd, + plan: "Enterprise", + }, + { + name: "Acme Corp.", + logo: AudioWaveform, + plan: "Startup", + }, + { + name: "Evil Corp.", + logo: Command, + plan: "Free", + }, + ], +}; + +const teams = data.teams; function SidebarLogo() { const { state } = useSidebar(); const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); + const { + data: organizations, + refetch, + isLoading, + } = api.organization.all.useQuery(); + const { mutateAsync: deleteOrganization, isLoading: isRemoving } = + api.organization.delete.useMutation(); + const { isMobile } = useSidebar(); + const { data: activeOrganization } = authClient.useActiveOrganization(); + + const [activeTeam, setActiveTeam] = useState< + typeof activeOrganization | null + >(null); + + useEffect(() => { + if (activeOrganization) { + setActiveTeam(activeOrganization); + } + }, [activeOrganization]); return ( - - + {isLoading ? ( + + Loading... + + + ) : ( + + + + + + {/* */} + + + + + + {activeTeam?.name} + + + + + + + + Organizations + + {organizations?.map((org, index) => ( + + { + await authClient.organization.setActive({ + organizationId: org.id, + }); + + window.location.reload(); + }} + className="w-full gap-2 p-2" + > + + + + {org.name} + {/* ⌘{index + 1} */} + + {/* */} + + + { + await deleteOrganization({ + organizationId: org.id, + }) + .then(() => { + refetch(); + toast.success("Port deleted successfully"); + }) + .catch(() => { + toast.error("Error deleting port"); + }); + }} + > + + + + + + + ))} + + + + + + + )} + + {/* - - + > + + - - Dokploy - - {dokployVersion} - - - + + Dokploy + + {dokployVersion} + + + */} + > ); } @@ -577,12 +751,12 @@ export default function Page({ children }: Props) { > - - - + > */} + + {/* */} diff --git a/apps/dokploy/server/api/root.ts b/apps/dokploy/server/api/root.ts index 68f5e4e0..7860b248 100644 --- a/apps/dokploy/server/api/root.ts +++ b/apps/dokploy/server/api/root.ts @@ -33,7 +33,7 @@ import { sshRouter } from "./routers/ssh-key"; import { stripeRouter } from "./routers/stripe"; import { swarmRouter } from "./routers/swarm"; import { userRouter } from "./routers/user"; - +import { organizationRouter } from "./routers/organization"; /** * This is the primary router for your server. * @@ -75,6 +75,7 @@ export const appRouter = createTRPCRouter({ server: serverRouter, stripe: stripeRouter, swarm: swarmRouter, + organization: organizationRouter, }); // export type definition of API diff --git a/apps/dokploy/server/api/routers/certificate.ts b/apps/dokploy/server/api/routers/certificate.ts index cdca1e97..3dc944ac 100644 --- a/apps/dokploy/server/api/routers/certificate.ts +++ b/apps/dokploy/server/api/routers/certificate.ts @@ -32,10 +32,7 @@ export const certificateRouter = createTRPCRouter({ .input(apiFindCertificate) .query(async ({ input, ctx }) => { const certificates = await findCertificateById(input.certificateId); - if ( - IS_CLOUD && - certificates.organizationId !== ctx.session.activeOrganizationId - ) { + if (certificates.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this certificate", @@ -47,10 +44,7 @@ export const certificateRouter = createTRPCRouter({ .input(apiFindCertificate) .mutation(async ({ input, ctx }) => { const certificates = await findCertificateById(input.certificateId); - if ( - IS_CLOUD && - certificates.organizationId !== ctx.session.activeOrganizationId - ) { + if (certificates.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this certificate", @@ -61,13 +55,7 @@ export const certificateRouter = createTRPCRouter({ }), all: adminProcedure.query(async ({ ctx }) => { return await db.query.certificates.findMany({ - // TODO: Remove this line when the cloud version is ready - ...(IS_CLOUD && { - where: eq( - certificates.organizationId, - ctx.session.activeOrganizationId, - ), - }), + where: eq(certificates.organizationId, ctx.session.activeOrganizationId), }); }), }); diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts new file mode 100644 index 00000000..dd933c9d --- /dev/null +++ b/apps/dokploy/server/api/routers/organization.ts @@ -0,0 +1,86 @@ +import { adminProcedure, createTRPCRouter } from "../trpc"; +import { z } from "zod"; +import { db } from "@/server/db"; +import { member, organization } from "@/server/db/schema"; +import { nanoid } from "nanoid"; +import { desc, eq } from "drizzle-orm"; +import { TRPCError } from "@trpc/server"; +export const organizationRouter = createTRPCRouter({ + create: adminProcedure + .input( + z.object({ + name: z.string(), + }), + ) + .mutation(async ({ ctx, input }) => { + const result = await db + .insert(organization) + .values({ + ...input, + slug: nanoid(), + createdAt: new Date(), + ownerId: ctx.user.ownerId, + }) + .returning() + .then((res) => res[0]); + + if (!result) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to create organization", + }); + } + + const memberResult = await db.insert(member).values({ + organizationId: result.id, + role: "owner", + createdAt: new Date(), + userId: ctx.user.id, + }); + return result; + }), + all: adminProcedure.query(async ({ ctx }) => { + return await db.query.organization.findMany({ + where: eq(organization.ownerId, ctx.user.ownerId), + orderBy: [desc(organization.createdAt)], + }); + }), + one: adminProcedure + .input( + z.object({ + organizationId: z.string(), + }), + ) + .query(async ({ ctx, input }) => { + return await db.query.organization.findFirst({ + where: eq(organization.id, input.organizationId), + }); + }), + update: adminProcedure + .input( + z.object({ + organizationId: z.string(), + name: z.string(), + }), + ) + .mutation(async ({ ctx, input }) => { + const result = await db + .update(organization) + .set({ name: input.name }) + .where(eq(organization.id, input.organizationId)) + .returning(); + return result[0]; + }), + delete: adminProcedure + .input( + z.object({ + organizationId: z.string(), + }), + ) + .mutation(async ({ ctx, input }) => { + const result = await db + .delete(organization) + .where(eq(organization.id, input.organizationId)); + return result; + }), +}); diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index 1ac37716..6ad7e2a9 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -16,8 +16,8 @@ import { updateRegistry, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; -import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; import { eq } from "drizzle-orm"; +import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; export const registryRouter = createTRPCRouter({ create: adminProcedure diff --git a/apps/dokploy/server/api/routers/ssh-key.ts b/apps/dokploy/server/api/routers/ssh-key.ts index 725c67fe..90f7cd6e 100644 --- a/apps/dokploy/server/api/routers/ssh-key.ts +++ b/apps/dokploy/server/api/routers/ssh-key.ts @@ -24,7 +24,6 @@ export const sshRouter = createTRPCRouter({ .input(apiCreateSshKey) .mutation(async ({ input, ctx }) => { try { - console.log(ctx.user.ownerId); await createSshKey({ ...input, organizationId: ctx.session.activeOrganizationId, diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 91db9826..129b0731 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -2,10 +2,17 @@ import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema"; import { findUserByAuthId, findUserById, findUsers } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; - +import { eq } from "drizzle-orm"; +import { member } from "@dokploy/server/db/schema"; +import { db } from "@dokploy/server/db"; export const userRouter = createTRPCRouter({ all: adminProcedure.query(async ({ ctx }) => { - return await findUsers(ctx.user.adminId); + return await db.query.member.findMany({ + where: eq(member.organizationId, ctx.session.activeOrganizationId), + with: { + user: true, + }, + }); }), byAuthId: protectedProcedure .input(apiFindOneUserByAuth) diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 0b5ef270..da765e8f 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -2,6 +2,8 @@ import { relations } from "drizzle-orm"; import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { nanoid } from "nanoid"; import { users_temp } from "./user"; +import { server } from "./server"; +import { projects } from "./project"; export const account = pgTable("account", { id: text("id") @@ -60,12 +62,17 @@ export const organization = pgTable("organization", { .references(() => users_temp.id), }); -export const organizationRelations = relations(organization, ({ one }) => ({ - owner: one(users_temp, { - fields: [organization.ownerId], - references: [users_temp.id], +export const organizationRelations = relations( + organization, + ({ one, many }) => ({ + owner: one(users_temp, { + fields: [organization.ownerId], + references: [users_temp.id], + }), + servers: many(server), + projects: many(projects), }), -})); +); export const member = pgTable("member", { id: text("id") diff --git a/packages/server/src/db/schema/bitbucket.ts b/packages/server/src/db/schema/bitbucket.ts index b8ecb668..0311202d 100644 --- a/packages/server/src/db/schema/bitbucket.ts +++ b/packages/server/src/db/schema/bitbucket.ts @@ -61,5 +61,5 @@ export const apiUpdateBitbucket = createSchema.extend({ name: z.string().min(1), bitbucketUsername: z.string().optional(), bitbucketWorkspaceName: z.string().optional(), - userId: z.string().optional(), + organizationId: z.string().optional(), }); diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 6e29ddf6..4e2917ca 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -150,7 +150,7 @@ export const apiCreateSlack = notificationsSchema export const apiUpdateSlack = apiCreateSlack.partial().extend({ notificationId: z.string().min(1), slackId: z.string(), - userId: z.string().optional(), + organizationId: z.string().optional(), }); export const apiTestSlackConnection = apiCreateSlack.pick({ @@ -177,7 +177,7 @@ export const apiCreateTelegram = notificationsSchema export const apiUpdateTelegram = apiCreateTelegram.partial().extend({ notificationId: z.string().min(1), telegramId: z.string().min(1), - userId: z.string().optional(), + organizationId: z.string().optional(), }); export const apiTestTelegramConnection = apiCreateTelegram.pick({ @@ -204,7 +204,7 @@ export const apiCreateDiscord = notificationsSchema export const apiUpdateDiscord = apiCreateDiscord.partial().extend({ notificationId: z.string().min(1), discordId: z.string().min(1), - userId: z.string().optional(), + organizationId: z.string().optional(), }); export const apiTestDiscordConnection = apiCreateDiscord @@ -238,7 +238,7 @@ export const apiCreateEmail = notificationsSchema export const apiUpdateEmail = apiCreateEmail.partial().extend({ notificationId: z.string().min(1), emailId: z.string().min(1), - userId: z.string().optional(), + organizationId: z.string().optional(), }); export const apiTestEmailConnection = apiCreateEmail.pick({ diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index f1dcd100..c94fd693 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -118,6 +118,10 @@ export const serverRelations = relations(server, ({ one, many }) => ({ mysql: many(mysql), postgres: many(postgres), certificates: many(certificates), + organization: one(organization, { + fields: [server.organizationId], + references: [organization.id], + }), })); const createSchema = createInsertSchema(server, { diff --git a/packages/server/src/services/bitbucket.ts b/packages/server/src/services/bitbucket.ts index 4ce4a7b0..7b5be7d6 100644 --- a/packages/server/src/services/bitbucket.ts +++ b/packages/server/src/services/bitbucket.ts @@ -12,14 +12,14 @@ export type Bitbucket = typeof bitbucket.$inferSelect; export const createBitbucket = async ( input: typeof apiCreateBitbucket._type, - userId: string, + organizationId: string, ) => { return await db.transaction(async (tx) => { const newGitProvider = await tx .insert(gitProvider) .values({ providerType: "bitbucket", - userId: userId, + organizationId: organizationId, name: input.name, }) .returning() @@ -74,12 +74,12 @@ export const updateBitbucket = async ( .where(eq(bitbucket.bitbucketId, bitbucketId)) .returning(); - if (input.name || input.userId) { + if (input.name || input.organizationId) { await tx .update(gitProvider) .set({ name: input.name, - userId: input.userId, + organizationId: input.organizationId, }) .where(eq(gitProvider.gitProviderId, input.gitProviderId)) .returning(); diff --git a/packages/server/src/services/destination.ts b/packages/server/src/services/destination.ts index add0a3df..e66f8695 100644 --- a/packages/server/src/services/destination.ts +++ b/packages/server/src/services/destination.ts @@ -10,13 +10,13 @@ export type Destination = typeof destinations.$inferSelect; export const createDestintation = async ( input: typeof apiCreateDestination._type, - userId: string, + organizationId: string, ) => { const newDestination = await db .insert(destinations) .values({ ...input, - userId: userId, + organizationId: organizationId, }) .returning() .then((value) => value[0]); @@ -46,14 +46,14 @@ export const findDestinationById = async (destinationId: string) => { export const removeDestinationById = async ( destinationId: string, - userId: string, + organizationId: string, ) => { const result = await db .delete(destinations) .where( and( eq(destinations.destinationId, destinationId), - eq(destinations.userId, userId), + eq(destinations.organizationId, organizationId), ), ) .returning(); @@ -73,7 +73,7 @@ export const updateDestinationById = async ( .where( and( eq(destinations.destinationId, destinationId), - eq(destinations.userId, destinationData.userId || ""), + eq(destinations.organizationId, destinationData.organizationId || ""), ), ) .returning(); diff --git a/packages/server/src/services/github.ts b/packages/server/src/services/github.ts index b23edf20..deb5f3fa 100644 --- a/packages/server/src/services/github.ts +++ b/packages/server/src/services/github.ts @@ -12,14 +12,14 @@ import { updatePreviewDeployment } from "./preview-deployment"; export type Github = typeof github.$inferSelect; export const createGithub = async ( input: typeof apiCreateGithub._type, - userId: string, + organizationId: string, ) => { return await db.transaction(async (tx) => { const newGitProvider = await tx .insert(gitProvider) .values({ providerType: "github", - userId: userId, + organizationId: organizationId, name: input.name, }) .returning() diff --git a/packages/server/src/services/gitlab.ts b/packages/server/src/services/gitlab.ts index c581c96c..0822aaab 100644 --- a/packages/server/src/services/gitlab.ts +++ b/packages/server/src/services/gitlab.ts @@ -13,14 +13,14 @@ export type Gitlab = typeof gitlab.$inferSelect; export const createGitlab = async ( input: typeof apiCreateGitlab._type, - userId: string, + organizationId: string, ) => { return await db.transaction(async (tx) => { const newGitProvider = await tx .insert(gitProvider) .values({ providerType: "gitlab", - userId: userId, + organizationId: organizationId, name: input.name, }) .returning() diff --git a/packages/server/src/services/notification.ts b/packages/server/src/services/notification.ts index 03f6bd09..fa355a56 100644 --- a/packages/server/src/services/notification.ts +++ b/packages/server/src/services/notification.ts @@ -24,7 +24,7 @@ export type Notification = typeof notifications.$inferSelect; export const createSlackNotification = async ( input: typeof apiCreateSlack._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newSlack = await tx @@ -54,7 +54,7 @@ export const createSlackNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "slack", - userId: userId, + organizationId: organizationId, serverThreshold: input.serverThreshold, }) .returning() @@ -84,7 +84,7 @@ export const updateSlackNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -114,7 +114,7 @@ export const updateSlackNotification = async ( export const createTelegramNotification = async ( input: typeof apiCreateTelegram._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newTelegram = await tx @@ -144,7 +144,7 @@ export const createTelegramNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "telegram", - userId: userId, + organizationId: organizationId, serverThreshold: input.serverThreshold, }) .returning() @@ -174,7 +174,7 @@ export const updateTelegramNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -204,7 +204,7 @@ export const updateTelegramNotification = async ( export const createDiscordNotification = async ( input: typeof apiCreateDiscord._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newDiscord = await tx @@ -234,7 +234,7 @@ export const createDiscordNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "discord", - userId: userId, + organizationId: organizationId, serverThreshold: input.serverThreshold, }) .returning() @@ -264,7 +264,7 @@ export const updateDiscordNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -294,7 +294,7 @@ export const updateDiscordNotification = async ( export const createEmailNotification = async ( input: typeof apiCreateEmail._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newEmail = await tx @@ -328,7 +328,7 @@ export const createEmailNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "email", - userId: userId, + organizationId: organizationId, serverThreshold: input.serverThreshold, }) .returning() @@ -358,7 +358,7 @@ export const updateEmailNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -392,7 +392,7 @@ export const updateEmailNotification = async ( export const createGotifyNotification = async ( input: typeof apiCreateGotify._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newGotify = await tx @@ -424,7 +424,7 @@ export const createGotifyNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "gotify", - userId: userId, + organizationId: organizationId, }) .returning() .then((value) => value[0]); @@ -453,7 +453,7 @@ export const updateGotifyNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, }) .where(eq(notifications.notificationId, input.notificationId)) .returning() diff --git a/packages/server/src/services/project.ts b/packages/server/src/services/project.ts index 8b80738f..b740834b 100644 --- a/packages/server/src/services/project.ts +++ b/packages/server/src/services/project.ts @@ -16,13 +16,13 @@ export type Project = typeof projects.$inferSelect; export const createProject = async ( input: typeof apiCreateProject._type, - userId: string, + organizationId: string, ) => { const newProject = await db .insert(projects) .values({ ...input, - userId: userId, + organizationId: organizationId, }) .returning() .then((value) => value[0]); diff --git a/packages/server/src/services/server.ts b/packages/server/src/services/server.ts index 7702c90d..afe851ef 100644 --- a/packages/server/src/services/server.ts +++ b/packages/server/src/services/server.ts @@ -1,5 +1,9 @@ import { db } from "@dokploy/server/db"; -import { type apiCreateServer, server } from "@dokploy/server/db/schema"; +import { + type apiCreateServer, + organization, + server, +} from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { desc, eq } from "drizzle-orm"; @@ -7,13 +11,13 @@ export type Server = typeof server.$inferSelect; export const createServer = async ( input: typeof apiCreateServer._type, - userId: string, + organizationId: string, ) => { const newServer = await db .insert(server) .values({ ...input, - userId: userId, + organizationId: organizationId, createdAt: new Date().toISOString(), }) .returning() @@ -47,11 +51,15 @@ export const findServerById = async (serverId: string) => { }; export const findServersByUserId = async (userId: string) => { - const servers = await db.query.server.findMany({ - where: eq(server.userId, userId), - orderBy: desc(server.createdAt), + const orgs = await db.query.organization.findMany({ + where: eq(organization.ownerId, userId), + with: { + servers: true, + }, }); + const servers = orgs.flatMap((org) => org.servers); + return servers; };
Dokploy
- {dokployVersion} -
+ {dokployVersion} +