feat: add organizations and members

This commit is contained in:
Mauricio Siu
2025-02-17 02:48:42 -06:00
parent c7d47a6003
commit b73e4102dd
17 changed files with 5385 additions and 329 deletions

View File

@@ -9,7 +9,7 @@ import {
import {
IS_CLOUD,
createInvitation,
findUserByAuthId,
findOrganizationById,
findUserById,
getUserByToken,
removeUserById,
@@ -98,21 +98,20 @@ export const adminRouter = createTRPCRouter({
try {
const user = await findUserById(input.id);
if (user.id !== ctx.user.ownerId) {
const organization = await findOrganizationById(
ctx.session?.activeOrganizationId || "",
);
if (organization?.ownerId !== ctx.user.ownerId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to assign permissions",
});
}
await updateUser(user.id, {
...input,
});
// await db
// .update(users)
// .set({
// ...input,
// })
// .where(eq(users.userId, input.userId));
} catch (error) {
throw error;
}

View File

@@ -1,29 +1,43 @@
import { db } from "@/server/db";
import { invitation, member, organization } from "@/server/db/schema";
import {
invitation,
member,
organization,
users_temp,
} from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
import { desc, eq } from "drizzle-orm";
import { and, desc, eq, exists } from "drizzle-orm";
import { nanoid } from "nanoid";
import { z } from "zod";
import { adminProcedure, createTRPCRouter } from "../trpc";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
import { auth, IS_CLOUD } from "@dokploy/server/index";
export const organizationRouter = createTRPCRouter({
create: adminProcedure
create: protectedProcedure
.input(
z.object({
name: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
if (ctx.user.rol !== "owner" && !IS_CLOUD) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Only the organization owner can create an organization",
});
}
const result = await db
.insert(organization)
.values({
...input,
slug: nanoid(),
createdAt: new Date(),
ownerId: ctx.user.ownerId,
ownerId: ctx.user.id,
})
.returning()
.then((res) => res[0]);
console.log("result", result);
if (!result) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
@@ -39,13 +53,24 @@ export const organizationRouter = createTRPCRouter({
});
return result;
}),
all: adminProcedure.query(async ({ ctx }) => {
return await db.query.organization.findMany({
where: eq(organization.ownerId, ctx.user.ownerId),
orderBy: [desc(organization.createdAt)],
all: protectedProcedure.query(async ({ ctx }) => {
const memberResult = await db.query.organization.findMany({
where: (organization) =>
exists(
db
.select()
.from(member)
.where(
and(
eq(member.organizationId, organization.id),
eq(member.userId, ctx.user.id),
),
),
),
});
return memberResult;
}),
one: adminProcedure
one: protectedProcedure
.input(
z.object({
organizationId: z.string(),
@@ -56,7 +81,7 @@ export const organizationRouter = createTRPCRouter({
where: eq(organization.id, input.organizationId),
});
}),
update: adminProcedure
update: protectedProcedure
.input(
z.object({
organizationId: z.string(),
@@ -64,6 +89,12 @@ export const organizationRouter = createTRPCRouter({
}),
)
.mutation(async ({ ctx, input }) => {
if (ctx.user.rol !== "owner" && !IS_CLOUD) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Only the organization owner can update it",
});
}
const result = await db
.update(organization)
.set({ name: input.name })
@@ -71,16 +102,41 @@ export const organizationRouter = createTRPCRouter({
.returning();
return result[0];
}),
delete: adminProcedure
delete: protectedProcedure
.input(
z.object({
organizationId: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
if (ctx.user.rol !== "owner" && !IS_CLOUD) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Only the organization owner can delete it",
});
}
const org = await db.query.organization.findFirst({
where: eq(organization.id, input.organizationId),
});
if (!org) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Organization not found",
});
}
if (org.ownerId !== ctx.user.id) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Only the organization owner can delete it",
});
}
const result = await db
.delete(organization)
.where(eq(organization.id, input.organizationId));
return result;
}),
allInvitations: adminProcedure.query(async ({ ctx }) => {
@@ -89,4 +145,13 @@ export const organizationRouter = createTRPCRouter({
orderBy: [desc(invitation.status)],
});
}),
acceptInvitation: adminProcedure
.input(z.object({ invitationId: z.string() }))
.mutation(async ({ ctx, input }) => {
const result = await auth.api.acceptInvitation({
invitationId: input.invitationId,
});
return result;
}),
});

View File

@@ -1,8 +1,8 @@
import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema";
import {
IS_CLOUD,
findUserByAuthId,
findUserById,
IS_CLOUD,
removeUserById,
updateUser,
verify2FA,