diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index ba2009bd..ca1bf3c2 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -65,7 +65,7 @@ export const ProfileForm = () => { isLoading: isUpdating, isError, error, - } = api.auth.update.useMutation(); + } = api.user.update.useMutation(); const { t } = useTranslation("settings"); const [gravatarHash, setGravatarHash] = useState(null); diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index f1296ce3..cba10ca0 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -27,6 +27,8 @@ import { Trash2, User, Users, + ChevronsUpDown, + Plus, } from "lucide-react"; import { usePathname } from "next/navigation"; import type * as React from "react"; @@ -75,6 +77,20 @@ import { useRouter } from "next/router"; import { Logo } from "../shared/logo"; import { UpdateServerButton } from "./update-server"; import { UserNav } from "./user-nav"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { authClient } from "@/lib/auth-client"; +import { toast } from "sonner"; +import { AddOrganization } from "../dashboard/organization/handle-organization"; +import { DialogAction } from "../shared/dialog-action"; +import { Button } from "../ui/button"; // The types of the queries we are going to use type AuthQueryOutput = inferRouterOutputs["auth"]["get"]; @@ -473,46 +489,6 @@ 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 { authClient } from "@/lib/auth-client"; -import { toast } from "sonner"; -import { AddOrganization } from "../dashboard/organization/handle-organization"; -import { DialogAction } from "../shared/dialog-action"; -import { Button } from "../ui/button"; -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", - }, - ], -}; function SidebarLogo() { const { state } = useSidebar(); @@ -529,6 +505,10 @@ function SidebarLogo() { api.organization.delete.useMutation(); const { isMobile } = useSidebar(); const { data: activeOrganization } = authClient.useActiveOrganization(); + const utils = api.useUtils(); + + const { data: invitations, refetch: refetchInvitations } = + api.user.getInvitations.useQuery(); const [activeTeam, setActiveTeam] = useState< typeof activeOrganization | null @@ -549,31 +529,27 @@ function SidebarLogo() { ) : ( - + - {/*
*/} -
- -
-
- - {activeTeam?.name} - +
+
+ +
+
+

+ {activeOrganization?.name} +

+
@@ -587,14 +563,13 @@ function SidebarLogo() { Organizations - {organizations?.map((org, index) => ( + {organizations?.map((org) => (
{ await authClient.organization.setActive({ organizationId: org.id, }); - window.location.reload(); }} className="w-full gap-2 p-2" @@ -655,35 +630,76 @@ function SidebarLogo() { )} + + + + + + + Pending Invitations + {invitations && invitations.length > 0 ? ( + invitations.map((invitation) => ( +
+ e.preventDefault()} + > +
{invitation.email}
+
+ Expires:{" "} + {new Date(invitation.expiresAt).toLocaleDateString()} +
+
+ Role: {invitation.role} +
+
+ { + const { error } = + await authClient.organization.acceptInvitation({ + invitationId: invitation.id, + }); + + if (error) { + toast.error( + error.message || "Error accepting invitation", + ); + } else { + toast.success("Invitation accepted successfully"); + await refetchInvitations(); + } + }} + > + + +
+ )) + ) : ( + + No pending invitations + + )} +
+
)} - - {/* -
- -
- -
-

Dokploy

-

- {dokployVersion} -

-
- */} ); } diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts index 490da340..269ac77b 100644 --- a/apps/dokploy/server/api/routers/application.ts +++ b/apps/dokploy/server/api/routers/application.ts @@ -81,7 +81,11 @@ export const applicationRouter = createTRPCRouter({ const newApplication = await createApplication(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newApplication.applicationId); + await addNewService( + ctx.user.id, + newApplication.applicationId, + project.organizationId, + ); } return newApplication; } catch (error: unknown) { diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index d9cd46d2..258a03d4 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -80,7 +80,11 @@ export const composeRouter = createTRPCRouter({ const newService = await createCompose(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newService.composeId); + await addNewService( + ctx.user.id, + newService.composeId, + project.organizationId, + ); } return newService; @@ -424,7 +428,11 @@ export const composeRouter = createTRPCRouter({ }); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, compose.composeId); + await addNewService( + ctx.user.id, + compose.composeId, + project.organizationId, + ); } if (mounts && mounts?.length > 0) { diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 4276560c..5735620e 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -57,7 +57,11 @@ export const mariadbRouter = createTRPCRouter({ } const newMariadb = await createMariadb(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newMariadb.mariadbId); + await addNewService( + ctx.user.id, + newMariadb.mariadbId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index d1d12bd0..7f8716a5 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -56,7 +56,11 @@ export const mongoRouter = createTRPCRouter({ } const newMongo = await createMongo(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newMongo.mongoId); + await addNewService( + ctx.user.id, + newMongo.mongoId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index dc107bdb..96ea4846 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -59,7 +59,11 @@ export const mysqlRouter = createTRPCRouter({ const newMysql = await createMysql(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newMysql.mysqlId); + await addNewService( + ctx.user.id, + newMysql.mysqlId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index b74bc0f6..aa3a0459 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -64,7 +64,11 @@ export const postgresRouter = createTRPCRouter({ } const newPostgres = await createPostgres(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newPostgres.postgresId); + await addNewService( + ctx.user.id, + newPostgres.postgresId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index db76ee6c..6d5a84d5 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -56,7 +56,11 @@ export const redisRouter = createTRPCRouter({ } const newRedis = await createRedis(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newRedis.redisId); + await addNewService( + ctx.user.id, + newRedis.redisId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index 6ad7e2a9..62c8a9b6 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -18,7 +18,7 @@ import { import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; - +import { db } from "@/server/db"; export const registryRouter = createTRPCRouter({ create: adminProcedure .input(apiCreateRegistry) diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 6b4e8ede..872ee074 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -15,10 +15,11 @@ import { apiAssignPermissions, apiFindOneToken, apiUpdateUser, + invitation, member, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; -import { and, asc, desc, eq } from "drizzle-orm"; +import { and, asc, desc, eq, gt } from "drizzle-orm"; import { z } from "zod"; import { adminProcedure, @@ -115,14 +116,34 @@ export const userRouter = createTRPCRouter({ }); } + const { id, ...rest } = input; + + console.log(rest); await db .update(member) .set({ - ...input, + ...rest, }) - .where(eq(member.userId, input.id)); + .where( + and( + eq(member.userId, input.id), + eq( + member.organizationId, + ctx.session?.activeOrganizationId || "", + ), + ), + ); } catch (error) { throw error; } }), + getInvitations: protectedProcedure.query(async ({ ctx }) => { + return await db.query.invitation.findMany({ + where: and( + eq(invitation.email, ctx.user.email), + gt(invitation.expiresAt, new Date()), + eq(invitation.status, "pending"), + ), + }); + }), });