mirror of
https://github.com/dokploy/dokploy.git
synced 2026-06-14 03:19:49 +00:00
refactor: simplify role management by removing unused role schema and related logic; update user role checks in context and procedures
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { db } from "@/server/db";
|
||||
import { invitation, member, organization, role } from "@/server/db/schema";
|
||||
import { createDefaultRoles, IS_CLOUD } from "@dokploy/server/index";
|
||||
import { invitation, member, organization } from "@/server/db/schema";
|
||||
import { IS_CLOUD } from "@dokploy/server/index";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { and, desc, eq, exists } from "drizzle-orm";
|
||||
import { nanoid } from "nanoid";
|
||||
@@ -38,18 +38,12 @@ export const organizationRouter = createTRPCRouter({
|
||||
message: "Failed to create organization",
|
||||
});
|
||||
}
|
||||
await createDefaultRoles(result.id);
|
||||
|
||||
const ownerRole = await db.query.role.findFirst({
|
||||
where: and(eq(role.name, "owner"), eq(role.organizationId, result.id)),
|
||||
});
|
||||
|
||||
await db.insert(member).values({
|
||||
organizationId: result.id,
|
||||
role: "owner",
|
||||
createdAt: new Date(),
|
||||
userId: ctx.user.id,
|
||||
roleId: ownerRole?.roleId || "",
|
||||
});
|
||||
return result;
|
||||
}),
|
||||
|
||||
@@ -1,86 +1,75 @@
|
||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||
import { db } from "@/server/db";
|
||||
import {
|
||||
apiFindOneRole,
|
||||
createRoleSchema,
|
||||
role,
|
||||
updateRoleSchema,
|
||||
} from "@/server/db/schema";
|
||||
import { createRole, removeRoleById, updateRoleById } from "@dokploy/server";
|
||||
import { defaultPermissions } from "@dokploy/server/lib/permissions";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { and, asc, eq } from "drizzle-orm";
|
||||
import { createTRPCRouter } from "@/server/api/trpc";
|
||||
// import { createRole, removeRoleById, updateRoleById } from "@dokploy/server";
|
||||
// import { defaultPermissions } from "@dokploy/server/lib/permissions";
|
||||
|
||||
export const roleRouter = createTRPCRouter({
|
||||
all: protectedProcedure.query(async ({ ctx }) => {
|
||||
const roles = await db.query.role.findMany({
|
||||
where: and(
|
||||
eq(role.organizationId, ctx.session.activeOrganizationId),
|
||||
eq(role.isSystem, false),
|
||||
),
|
||||
orderBy: [asc(role.createdAt)],
|
||||
});
|
||||
return roles;
|
||||
}),
|
||||
delete: protectedProcedure
|
||||
.input(apiFindOneRole)
|
||||
.mutation(async ({ input }) => {
|
||||
try {
|
||||
return removeRoleById(input.roleId);
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : "Error input: Deleting role";
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message,
|
||||
});
|
||||
}
|
||||
}),
|
||||
create: protectedProcedure
|
||||
.input(createRoleSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
return await createRole(
|
||||
{
|
||||
...input,
|
||||
},
|
||||
ctx.session.activeOrganizationId,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error input: Creating role",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.input(updateRoleSchema)
|
||||
.mutation(async ({ input }) => {
|
||||
return await updateRoleById(input.roleId, input);
|
||||
}),
|
||||
getDefaultRoles: protectedProcedure.query(async ({ ctx }) => {
|
||||
const roles = await db.query.role.findMany({
|
||||
where: and(
|
||||
eq(role.organizationId, ctx.session.activeOrganizationId),
|
||||
eq(role.isSystem, true),
|
||||
),
|
||||
});
|
||||
// add the description from the constants roles to the roles
|
||||
const rolesWithDescription = defaultPermissions.map((role) => {
|
||||
const roleInfo = roles.find((r) => r.name === role.name);
|
||||
return {
|
||||
...roleInfo,
|
||||
...role,
|
||||
};
|
||||
});
|
||||
|
||||
const set = new Set(rolesWithDescription.flatMap((r) => r.permissions));
|
||||
|
||||
return {
|
||||
roles: rolesWithDescription,
|
||||
permissions: Array.from(set),
|
||||
};
|
||||
}),
|
||||
// all: protectedProcedure.query(async ({ ctx }) => {
|
||||
// const roles = await db.query.role.findMany({
|
||||
// where: and(
|
||||
// eq(role.organizationId, ctx.session.activeOrganizationId),
|
||||
// eq(role.isSystem, false),
|
||||
// ),
|
||||
// orderBy: [asc(role.createdAt)],
|
||||
// });
|
||||
// return roles;
|
||||
// }),
|
||||
// delete: protectedProcedure
|
||||
// .input(apiFindOneRole)
|
||||
// .mutation(async ({ input }) => {
|
||||
// try {
|
||||
// return removeRoleById(input.roleId);
|
||||
// } catch (error) {
|
||||
// const message =
|
||||
// error instanceof Error ? error.message : "Error input: Deleting role";
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message,
|
||||
// });
|
||||
// }
|
||||
// }),
|
||||
// create: protectedProcedure
|
||||
// .input(createRoleSchema)
|
||||
// .mutation(async ({ input, ctx }) => {
|
||||
// try {
|
||||
// return await createRole(
|
||||
// {
|
||||
// ...input,
|
||||
// },
|
||||
// ctx.session.activeOrganizationId,
|
||||
// );
|
||||
// } catch (error) {
|
||||
// console.error(error);
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: "Error input: Creating role",
|
||||
// cause: error,
|
||||
// });
|
||||
// }
|
||||
// }),
|
||||
// update: protectedProcedure
|
||||
// .input(updateRoleSchema)
|
||||
// .mutation(async ({ input }) => {
|
||||
// return await updateRoleById(input.roleId, input);
|
||||
// }),
|
||||
// getDefaultRoles: protectedProcedure.query(async ({ ctx }) => {
|
||||
// const roles = await db.query.role.findMany({
|
||||
// where: and(
|
||||
// eq(role.organizationId, ctx.session.activeOrganizationId),
|
||||
// eq(role.isSystem, true),
|
||||
// ),
|
||||
// });
|
||||
// // add the description from the constants roles to the roles
|
||||
// const rolesWithDescription = defaultPermissions.map((role) => {
|
||||
// const roleInfo = roles.find((r) => r.name === role.name);
|
||||
// return {
|
||||
// ...roleInfo,
|
||||
// ...role,
|
||||
// };
|
||||
// });
|
||||
// const set = new Set(rolesWithDescription.flatMap((r) => r.permissions));
|
||||
// return {
|
||||
// roles: rolesWithDescription,
|
||||
// permissions: Array.from(set),
|
||||
// };
|
||||
// }),
|
||||
});
|
||||
|
||||
@@ -30,17 +30,7 @@ import { ZodError } from "zod";
|
||||
*/
|
||||
|
||||
interface CreateContextOptions {
|
||||
user:
|
||||
| (User & {
|
||||
role: {
|
||||
roleId: string;
|
||||
name: string;
|
||||
permissions: string[];
|
||||
isSystem: boolean;
|
||||
};
|
||||
ownerId: string;
|
||||
})
|
||||
| null;
|
||||
user: (User & { role: "member" | "admin" | "owner"; ownerId: string }) | null;
|
||||
session:
|
||||
| (Session & { activeOrganizationId: string; impersonatedBy?: string })
|
||||
| null;
|
||||
@@ -192,7 +182,7 @@ export const uploadProcedure = async (opts: any) => {
|
||||
};
|
||||
|
||||
export const cliProcedure = t.procedure.use(({ ctx, next }) => {
|
||||
if (!ctx.session || !ctx.user || ctx.user.role.name !== "owner") {
|
||||
if (!ctx.session || !ctx.user || ctx.user.role !== "owner") {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
}
|
||||
return next({
|
||||
@@ -206,11 +196,7 @@ export const cliProcedure = t.procedure.use(({ ctx, next }) => {
|
||||
});
|
||||
|
||||
export const adminProcedure = t.procedure.use(({ ctx, next }) => {
|
||||
if (
|
||||
!ctx.session ||
|
||||
!ctx.user ||
|
||||
(ctx.user.role.name !== "owner" && ctx.user.role.name !== "admin")
|
||||
) {
|
||||
if (!ctx.session || !ctx.user || ctx.user.role !== "owner") {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
}
|
||||
return next({
|
||||
|
||||
@@ -10,7 +10,7 @@ import { nanoid } from "nanoid";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import { users } from "./user";
|
||||
import { role } from "./rbac";
|
||||
// import { role } from "./rbac";
|
||||
|
||||
export const account = pgTable("account", {
|
||||
id: text("id")
|
||||
@@ -92,8 +92,8 @@ export const member = pgTable("member", {
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
role: text("role"),
|
||||
roleId: text("roleId").references(() => role.roleId, { onDelete: "cascade" }),
|
||||
role: text("role").$type<"owner" | "member" | "admin">(),
|
||||
// roleId: text("roleId").references(() => role.roleId, { onDelete: "cascade" }),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
teamId: text("team_id"),
|
||||
// Permissions
|
||||
@@ -116,10 +116,10 @@ export const memberRelations = relations(member, ({ one }) => ({
|
||||
fields: [member.userId],
|
||||
references: [users.id],
|
||||
}),
|
||||
role: one(role, {
|
||||
fields: [member.roleId],
|
||||
references: [role.roleId],
|
||||
}),
|
||||
// role: one(role, {
|
||||
// fields: [member.roleId],
|
||||
// references: [role.roleId],
|
||||
// }),
|
||||
}));
|
||||
|
||||
export const invitation = pgTable("invitation", {
|
||||
|
||||
@@ -30,7 +30,7 @@ export * from "./server";
|
||||
export * from "./utils";
|
||||
export * from "./preview-deployments";
|
||||
export * from "./ai";
|
||||
export * from "./rbac";
|
||||
// export * from "./rbac";
|
||||
export * from "./account";
|
||||
export * from "./schedule";
|
||||
export * from "./rollbacks";
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { pgTable, text, timestamp, boolean, unique } from "drizzle-orm/pg-core";
|
||||
import { nanoid } from "nanoid";
|
||||
import { organization, member } from "./account";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
// import { relations } from "drizzle-orm";
|
||||
// import { pgTable, text, timestamp, boolean, unique } from "drizzle-orm/pg-core";
|
||||
// import { nanoid } from "nanoid";
|
||||
// import { organization, member } from "./account";
|
||||
// import { createInsertSchema } from "drizzle-zod";
|
||||
// import { z } from "zod";
|
||||
|
||||
export const role = pgTable(
|
||||
"member_role",
|
||||
{
|
||||
roleId: text("roleId")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
name: text("name").notNull().unique(),
|
||||
description: text("description"),
|
||||
canDelete: boolean("canDelete").notNull().default(true),
|
||||
isSystem: boolean("is_system").default(false),
|
||||
permissions: text("permissions").array(),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
organizationId: text("organizationId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
},
|
||||
(table) => ({
|
||||
roleName: unique("role_name_unique").on(table.name, table.organizationId),
|
||||
}),
|
||||
);
|
||||
// export const role = pgTable(
|
||||
// "member_role",
|
||||
// {
|
||||
// roleId: text("roleId")
|
||||
// .primaryKey()
|
||||
// .$defaultFn(() => nanoid()),
|
||||
// name: text("name").notNull().unique(),
|
||||
// description: text("description"),
|
||||
// canDelete: boolean("canDelete").notNull().default(true),
|
||||
// isSystem: boolean("is_system").default(false),
|
||||
// permissions: text("permissions").array(),
|
||||
// createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
// updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
// organizationId: text("organizationId")
|
||||
// .notNull()
|
||||
// .references(() => organization.id, { onDelete: "cascade" }),
|
||||
// },
|
||||
// (table) => ({
|
||||
// roleName: unique("role_name_unique").on(table.name, table.organizationId),
|
||||
// }),
|
||||
// );
|
||||
|
||||
export const roleRelations = relations(role, ({ one, many }) => ({
|
||||
organization: one(organization, {
|
||||
fields: [role.organizationId],
|
||||
references: [organization.id],
|
||||
}),
|
||||
members: many(member),
|
||||
}));
|
||||
// export const roleRelations = relations(role, ({ one, many }) => ({
|
||||
// organization: one(organization, {
|
||||
// fields: [role.organizationId],
|
||||
// references: [organization.id],
|
||||
// }),
|
||||
// members: many(member),
|
||||
// }));
|
||||
|
||||
export type Role = typeof role.$inferSelect;
|
||||
// export type Role = typeof role.$inferSelect;
|
||||
|
||||
export const createRoleSchema = createInsertSchema(role)
|
||||
.omit({
|
||||
roleId: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
isSystem: true,
|
||||
organizationId: true,
|
||||
})
|
||||
.extend({
|
||||
permissions: z.array(z.string()),
|
||||
});
|
||||
// export const createRoleSchema = createInsertSchema(role)
|
||||
// .omit({
|
||||
// roleId: true,
|
||||
// createdAt: true,
|
||||
// updatedAt: true,
|
||||
// isSystem: true,
|
||||
// organizationId: true,
|
||||
// })
|
||||
// .extend({
|
||||
// permissions: z.array(z.string()),
|
||||
// });
|
||||
|
||||
export const updateRoleSchema = createRoleSchema.extend({
|
||||
roleId: z.string().min(1),
|
||||
});
|
||||
// export const updateRoleSchema = createRoleSchema.extend({
|
||||
// roleId: z.string().min(1),
|
||||
// });
|
||||
|
||||
export const apiFindOneRole = z.object({
|
||||
roleId: z.string().min(1),
|
||||
});
|
||||
// export const apiFindOneRole = z.object({
|
||||
// roleId: z.string().min(1),
|
||||
// });
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { IncomingMessage } from "node:http";
|
||||
import * as bcrypt from "bcrypt";
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { APIError, createAuthMiddleware } from "better-auth/api";
|
||||
import { APIError } from "better-auth/api";
|
||||
import { admin, apiKey, organization, twoFactor } from "better-auth/plugins";
|
||||
import { and, desc, eq } from "drizzle-orm";
|
||||
import { IS_CLOUD } from "../constants";
|
||||
@@ -11,12 +11,7 @@ import * as schema from "../db/schema";
|
||||
import { getUserByToken } from "../services/admin";
|
||||
import { sendEmail } from "../verification/send-verification-email";
|
||||
import { getPublicIpWithFallback } from "../wss/utils";
|
||||
import { createDefaultRoles } from "../services/role";
|
||||
import {
|
||||
findWebServer,
|
||||
updateWebServer,
|
||||
} from "@dokploy/server/services/web-server";
|
||||
import type { Role } from "../db/schema/rbac";
|
||||
import { findWebServer, updateWebServer } from "../services/web-server";
|
||||
|
||||
const { handler, api } = betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
@@ -84,48 +79,6 @@ const { handler, api } = betterAuth({
|
||||
});
|
||||
},
|
||||
},
|
||||
hooks: {
|
||||
after: createAuthMiddleware(async (ctx) => {
|
||||
if (ctx.path === "/organization/accept-invitation") {
|
||||
const invitationId = ctx.body.invitationId;
|
||||
|
||||
if (invitationId) {
|
||||
const user = await getUserByToken(invitationId);
|
||||
if (!user) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
const role = await db.query.role.findFirst({
|
||||
where: and(
|
||||
eq(schema.role.name, user.role || "member"),
|
||||
eq(schema.role.organizationId, user.organizationId),
|
||||
),
|
||||
});
|
||||
|
||||
const userTemp = await db.query.users.findFirst({
|
||||
where: eq(schema.users.email, user.email),
|
||||
});
|
||||
|
||||
const member = await db.query.member.findFirst({
|
||||
where: and(
|
||||
eq(schema.member.userId, userTemp?.id || ""),
|
||||
eq(schema.member.organizationId, user.organizationId),
|
||||
),
|
||||
});
|
||||
|
||||
await db
|
||||
.update(schema.member)
|
||||
.set({
|
||||
roleId: role?.roleId || "",
|
||||
})
|
||||
.where(eq(schema.member.userId, member?.userId || ""))
|
||||
.returning();
|
||||
}
|
||||
}
|
||||
}),
|
||||
},
|
||||
databaseHooks: {
|
||||
user: {
|
||||
create: {
|
||||
@@ -135,25 +88,14 @@ const { handler, api } = betterAuth({
|
||||
context?.request?.headers?.get("x-dokploy-token");
|
||||
if (xDokployToken) {
|
||||
const user = await getUserByToken(xDokployToken);
|
||||
|
||||
if (!user) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const ownerRole = await db.query.role.findFirst({
|
||||
where: and(eq(schema.role.name, "owner")),
|
||||
});
|
||||
|
||||
if (!ownerRole) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "Owner role not found",
|
||||
});
|
||||
}
|
||||
|
||||
const isAdminPresent = await db.query.member.findFirst({
|
||||
where: and(eq(schema.member.roleId, ownerRole.roleId)),
|
||||
where: eq(schema.member.role, "owner"),
|
||||
});
|
||||
if (isAdminPresent) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
@@ -164,11 +106,8 @@ const { handler, api } = betterAuth({
|
||||
}
|
||||
},
|
||||
after: async (user) => {
|
||||
const ownerRole = await db.query.role.findFirst({
|
||||
where: and(eq(schema.role.name, "owner")),
|
||||
});
|
||||
const isAdminPresent = await db.query.member.findFirst({
|
||||
where: and(eq(schema.member.roleId, ownerRole?.roleId || "")),
|
||||
where: eq(schema.member.role, "owner"),
|
||||
});
|
||||
|
||||
if (!IS_CLOUD) {
|
||||
@@ -189,20 +128,11 @@ const { handler, api } = betterAuth({
|
||||
.returning()
|
||||
.then((res) => res[0]);
|
||||
|
||||
await createDefaultRoles(organization?.id || "");
|
||||
|
||||
const ownerRole = await tx.query.role.findFirst({
|
||||
where: and(
|
||||
eq(schema.role.name, "owner"),
|
||||
eq(schema.role.organizationId, organization?.id || ""),
|
||||
),
|
||||
});
|
||||
|
||||
await tx.insert(schema.member).values({
|
||||
userId: user.id,
|
||||
organizationId: organization?.id || "",
|
||||
role: "owner",
|
||||
createdAt: new Date(),
|
||||
roleId: ownerRole?.roleId || "",
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -216,7 +146,6 @@ const { handler, api } = betterAuth({
|
||||
where: eq(schema.member.userId, session.userId),
|
||||
orderBy: desc(schema.member.createdAt),
|
||||
with: {
|
||||
role: true,
|
||||
organization: true,
|
||||
},
|
||||
});
|
||||
@@ -225,7 +154,6 @@ const { handler, api } = betterAuth({
|
||||
data: {
|
||||
...session,
|
||||
activeOrganizationId: member?.organization.id,
|
||||
roleId: member?.roleId,
|
||||
},
|
||||
};
|
||||
},
|
||||
@@ -237,9 +165,9 @@ const { handler, api } = betterAuth({
|
||||
updateAge: 60 * 60 * 24,
|
||||
},
|
||||
user: {
|
||||
modelName: "users",
|
||||
modelName: "users_temp",
|
||||
additionalFields: {
|
||||
roleId: {
|
||||
role: {
|
||||
type: "string",
|
||||
// required: true,
|
||||
input: false,
|
||||
@@ -293,7 +221,6 @@ const { handler, api } = betterAuth({
|
||||
export const auth = {
|
||||
handler,
|
||||
createApiKey: api.createApiKey,
|
||||
createInvitation: api.createInvitation,
|
||||
};
|
||||
|
||||
export const validateRequest = async (request: IncomingMessage) => {
|
||||
@@ -348,7 +275,6 @@ export const validateRequest = async (request: IncomingMessage) => {
|
||||
),
|
||||
with: {
|
||||
organization: true,
|
||||
role: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -381,7 +307,7 @@ export const validateRequest = async (request: IncomingMessage) => {
|
||||
createdAt,
|
||||
updatedAt,
|
||||
twoFactorEnabled,
|
||||
role: member?.role,
|
||||
role: member?.role || "member",
|
||||
ownerId: member?.organization.ownerId || apiKeyRecord.user.id,
|
||||
},
|
||||
};
|
||||
@@ -410,7 +336,6 @@ export const validateRequest = async (request: IncomingMessage) => {
|
||||
};
|
||||
}
|
||||
|
||||
let role: Role | null = null;
|
||||
if (session?.user) {
|
||||
const member = await db.query.member.findFirst({
|
||||
where: and(
|
||||
@@ -421,26 +346,17 @@ export const validateRequest = async (request: IncomingMessage) => {
|
||||
),
|
||||
),
|
||||
with: {
|
||||
role: true,
|
||||
organization: true,
|
||||
},
|
||||
});
|
||||
role = member?.role || null;
|
||||
|
||||
session.user.role = member?.role || "member";
|
||||
if (member) {
|
||||
session.user.ownerId = member.organization.ownerId;
|
||||
} else {
|
||||
session.user.ownerId = session.user.id;
|
||||
}
|
||||
}
|
||||
const mockSession = {
|
||||
session: {
|
||||
...session.session,
|
||||
},
|
||||
user: {
|
||||
...session.user,
|
||||
role,
|
||||
ownerId: session.user.ownerId,
|
||||
},
|
||||
};
|
||||
return mockSession;
|
||||
|
||||
return session;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,59 @@
|
||||
import {
|
||||
defaultStatements,
|
||||
memberAc,
|
||||
ownerAc,
|
||||
adminAc,
|
||||
} from "better-auth/plugins/organization/access";
|
||||
import { createAccessControl } from "better-auth/plugins/access";
|
||||
|
||||
/**
|
||||
* make sure to use `as const` so typescript can infer the type correctly
|
||||
*/
|
||||
const statement = {
|
||||
...defaultStatements,
|
||||
project: ["view", "create", "delete"],
|
||||
service: ["view", "create", "delete"],
|
||||
traefik_files: ["access"],
|
||||
docker: ["access"],
|
||||
api: ["access"],
|
||||
schedules: ["access"],
|
||||
git_providers: ["access"],
|
||||
ssh_keys: ["access"],
|
||||
} as const;
|
||||
|
||||
export const ac = createAccessControl(statement);
|
||||
|
||||
export const owner = ac.newRole({
|
||||
...ownerAc.statements,
|
||||
// inherit all the statements from the statements object
|
||||
project: ["create", "view", "delete"],
|
||||
service: ["create", "view", "delete"],
|
||||
traefik_files: ["access"],
|
||||
docker: ["access"],
|
||||
api: ["access"],
|
||||
schedules: ["access"],
|
||||
git_providers: ["access"],
|
||||
ssh_keys: ["access"],
|
||||
});
|
||||
|
||||
export const admin = ac.newRole({
|
||||
...adminAc.statements,
|
||||
project: ["create", "view", "delete"],
|
||||
service: ["create", "view", "delete"],
|
||||
traefik_files: ["access"],
|
||||
docker: ["access"],
|
||||
api: ["access"],
|
||||
schedules: ["access"],
|
||||
git_providers: ["access"],
|
||||
ssh_keys: ["access"],
|
||||
});
|
||||
|
||||
export const member = ac.newRole({
|
||||
...memberAc.statements,
|
||||
project: ["create", "view", "delete"],
|
||||
service: ["create", "view", "delete"],
|
||||
});
|
||||
|
||||
export const PERMISSIONS = {
|
||||
PROJECT: {
|
||||
VIEW: {
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
invitation,
|
||||
member,
|
||||
organization,
|
||||
role,
|
||||
users,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
@@ -35,12 +34,8 @@ export const findOrganizationById = async (organizationId: string) => {
|
||||
};
|
||||
|
||||
export const isAdminPresent = async () => {
|
||||
const ownerRole = await db.query.role.findFirst({
|
||||
where: eq(role.name, "owner"),
|
||||
});
|
||||
|
||||
const admin = await db.query.member.findFirst({
|
||||
where: eq(member.roleId, ownerRole?.roleId || ""),
|
||||
where: eq(member.role, "owner"),
|
||||
});
|
||||
|
||||
if (!admin) {
|
||||
@@ -50,12 +45,8 @@ export const isAdminPresent = async () => {
|
||||
};
|
||||
|
||||
export const findOwner = async () => {
|
||||
const ownerRole = await db.query.role.findFirst({
|
||||
where: eq(role.name, "owner"),
|
||||
});
|
||||
|
||||
const owner = await db.query.member.findFirst({
|
||||
where: eq(member.roleId, ownerRole?.roleId || ""),
|
||||
where: eq(member.role, "owner"),
|
||||
with: {
|
||||
user: true,
|
||||
},
|
||||
|
||||
@@ -1,119 +1,119 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../db";
|
||||
import {
|
||||
type createRoleSchema,
|
||||
member,
|
||||
role,
|
||||
type updateRoleSchema,
|
||||
} from "../db/schema";
|
||||
import type { z } from "zod";
|
||||
import {
|
||||
adminPermissions,
|
||||
memberPermissions,
|
||||
ownerPermissions,
|
||||
} from "../lib/permissions";
|
||||
// import { eq } from "drizzle-orm";
|
||||
// import { db } from "../db";
|
||||
// import {
|
||||
// type createRoleSchema,
|
||||
// member,
|
||||
// role,
|
||||
// type updateRoleSchema,
|
||||
// } from "../db/schema";
|
||||
// import type { z } from "zod";
|
||||
// import {
|
||||
// adminPermissions,
|
||||
// memberPermissions,
|
||||
// ownerPermissions,
|
||||
// } from "../lib/permissions";
|
||||
|
||||
export const createRole = async (
|
||||
input: z.infer<typeof createRoleSchema>,
|
||||
organizationId: string,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const { ...other } = input;
|
||||
const newRole = await tx
|
||||
.insert(role)
|
||||
.values({ ...other, organizationId })
|
||||
.returning()
|
||||
.then((res) => res[0]);
|
||||
// export const createRole = async (
|
||||
// input: z.infer<typeof createRoleSchema>,
|
||||
// organizationId: string,
|
||||
// ) => {
|
||||
// await db.transaction(async (tx) => {
|
||||
// const { ...other } = input;
|
||||
// const newRole = await tx
|
||||
// .insert(role)
|
||||
// .values({ ...other, organizationId })
|
||||
// .returning()
|
||||
// .then((res) => res[0]);
|
||||
|
||||
if (!newRole) {
|
||||
throw new Error("Failed to create role");
|
||||
}
|
||||
// if (!newRole) {
|
||||
// throw new Error("Failed to create role");
|
||||
// }
|
||||
|
||||
return role;
|
||||
});
|
||||
};
|
||||
// return role;
|
||||
// });
|
||||
// };
|
||||
|
||||
const findRoleById = async (roleId: string) => {
|
||||
const result = await db.query.role.findFirst({
|
||||
where: eq(role.roleId, roleId),
|
||||
});
|
||||
// const findRoleById = async (roleId: string) => {
|
||||
// const result = await db.query.role.findFirst({
|
||||
// where: eq(role.roleId, roleId),
|
||||
// });
|
||||
|
||||
if (!result) {
|
||||
throw new Error("Role not found");
|
||||
}
|
||||
// if (!result) {
|
||||
// throw new Error("Role not found");
|
||||
// }
|
||||
|
||||
return result;
|
||||
};
|
||||
// return result;
|
||||
// };
|
||||
|
||||
export const removeRoleById = async (roleId: string) => {
|
||||
const currentRole = await findRoleById(roleId);
|
||||
// export const removeRoleById = async (roleId: string) => {
|
||||
// const currentRole = await findRoleById(roleId);
|
||||
|
||||
if (!currentRole) {
|
||||
throw new Error("Role not found");
|
||||
}
|
||||
// if (!currentRole) {
|
||||
// throw new Error("Role not found");
|
||||
// }
|
||||
|
||||
if (currentRole.isSystem) {
|
||||
throw new Error("Cannot delete system role");
|
||||
}
|
||||
// if (currentRole.isSystem) {
|
||||
// throw new Error("Cannot delete system role");
|
||||
// }
|
||||
|
||||
const members = await db.query.member.findMany({
|
||||
where: eq(member.roleId, roleId),
|
||||
});
|
||||
// const members = await db.query.member.findMany({
|
||||
// where: eq(member.roleId, roleId),
|
||||
// });
|
||||
|
||||
if (members.length > 0) {
|
||||
throw new Error("Cannot delete role with assigned members");
|
||||
}
|
||||
// if (members.length > 0) {
|
||||
// throw new Error("Cannot delete role with assigned members");
|
||||
// }
|
||||
|
||||
await db.delete(role).where(eq(role.roleId, roleId));
|
||||
// await db.delete(role).where(eq(role.roleId, roleId));
|
||||
|
||||
return currentRole;
|
||||
};
|
||||
// return currentRole;
|
||||
// };
|
||||
|
||||
export const updateRoleById = async (
|
||||
roleId: string,
|
||||
input: z.infer<typeof updateRoleSchema>,
|
||||
) => {
|
||||
const currentRole = await findRoleById(roleId);
|
||||
// export const updateRoleById = async (
|
||||
// roleId: string,
|
||||
// input: z.infer<typeof updateRoleSchema>,
|
||||
// ) => {
|
||||
// const currentRole = await findRoleById(roleId);
|
||||
|
||||
if (!currentRole) {
|
||||
throw new Error("Role not found");
|
||||
}
|
||||
// if (!currentRole) {
|
||||
// throw new Error("Role not found");
|
||||
// }
|
||||
|
||||
if (currentRole.isSystem) {
|
||||
throw new Error("Cannot update system role");
|
||||
}
|
||||
// if (currentRole.isSystem) {
|
||||
// throw new Error("Cannot update system role");
|
||||
// }
|
||||
|
||||
await db.update(role).set(input).where(eq(role.roleId, roleId));
|
||||
// await db.update(role).set(input).where(eq(role.roleId, roleId));
|
||||
|
||||
return currentRole;
|
||||
};
|
||||
// return currentRole;
|
||||
// };
|
||||
|
||||
export const createDefaultRoles = async (organizationId: string) => {
|
||||
await db.transaction(async (tx) => {
|
||||
await tx.insert(role).values({
|
||||
name: "owner",
|
||||
description: "Owner of the organization with full access to all features",
|
||||
organizationId,
|
||||
isSystem: true,
|
||||
permissions: ownerPermissions.map((permission) => permission.name),
|
||||
});
|
||||
// export const createDefaultRoles = async (organizationId: string) => {
|
||||
// await db.transaction(async (tx) => {
|
||||
// await tx.insert(role).values({
|
||||
// name: "owner",
|
||||
// description: "Owner of the organization with full access to all features",
|
||||
// organizationId,
|
||||
// isSystem: true,
|
||||
// permissions: ownerPermissions.map((permission) => permission.name),
|
||||
// });
|
||||
|
||||
await tx.insert(role).values({
|
||||
name: "admin",
|
||||
description:
|
||||
"Administrator with access to manage projects, services and configurations",
|
||||
organizationId,
|
||||
isSystem: true,
|
||||
permissions: adminPermissions.map((permission) => permission.name),
|
||||
});
|
||||
// await tx.insert(role).values({
|
||||
// name: "admin",
|
||||
// description:
|
||||
// "Administrator with access to manage projects, services and configurations",
|
||||
// organizationId,
|
||||
// isSystem: true,
|
||||
// permissions: adminPermissions.map((permission) => permission.name),
|
||||
// });
|
||||
|
||||
await tx.insert(role).values({
|
||||
name: "member",
|
||||
description:
|
||||
"Regular member with access to create projects and manage services",
|
||||
organizationId,
|
||||
isSystem: true,
|
||||
permissions: memberPermissions.map((permission) => permission.name),
|
||||
});
|
||||
});
|
||||
};
|
||||
// await tx.insert(role).values({
|
||||
// name: "member",
|
||||
// description:
|
||||
// "Regular member with access to create projects and manage services",
|
||||
// organizationId,
|
||||
// isSystem: true,
|
||||
// permissions: memberPermissions.map((permission) => permission.name),
|
||||
// });
|
||||
// });
|
||||
// };
|
||||
|
||||
Reference in New Issue
Block a user