mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor: add organizations system
This commit is contained in:
@@ -6,9 +6,9 @@ import { TimeSpan } from "lucia";
|
||||
import { Lucia } from "lucia/dist/core.js";
|
||||
import type { Session, User } from "lucia/dist/core.js";
|
||||
import { db } from "../db";
|
||||
import { type DatabaseUser, auth, sessionTable } from "../db/schema";
|
||||
import { type DatabaseUser, auth, session } from "../db/schema";
|
||||
|
||||
export const adapter = new DrizzlePostgreSQLAdapter(db, sessionTable, auth);
|
||||
export const adapter = new DrizzlePostgreSQLAdapter(db, session, auth);
|
||||
|
||||
export const lucia = new Lucia(adapter, {
|
||||
sessionCookie: {
|
||||
@@ -46,6 +46,7 @@ export async function validateRequest(
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
): ReturnValidateToken {
|
||||
console.log(session);
|
||||
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
|
||||
|
||||
if (!sessionId) {
|
||||
|
||||
@@ -32,3 +32,38 @@ export const verification = pgTable("verification", {
|
||||
createdAt: timestamp("created_at"),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
});
|
||||
|
||||
export const organization = pgTable("organization", {
|
||||
id: text("id").primaryKey(),
|
||||
name: text("name").notNull(),
|
||||
slug: text("slug").unique(),
|
||||
logo: text("logo"),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
metadata: text("metadata"),
|
||||
});
|
||||
|
||||
export const member = pgTable("member", {
|
||||
id: text("id").primaryKey(),
|
||||
organizationId: text("organization_id")
|
||||
.notNull()
|
||||
.references(() => organization.id),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id),
|
||||
role: text("role").notNull(),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
});
|
||||
|
||||
export const invitation = pgTable("invitation", {
|
||||
id: text("id").primaryKey(),
|
||||
organizationId: text("organization_id")
|
||||
.notNull()
|
||||
.references(() => organization.id),
|
||||
email: text("email").notNull(),
|
||||
role: text("role"),
|
||||
status: text("status").notNull(),
|
||||
expiresAt: timestamp("expires_at").notNull(),
|
||||
inviterId: text("inviter_id")
|
||||
.notNull()
|
||||
.references(() => user.id),
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { user } from "./user";
|
||||
|
||||
// OLD TABLE
|
||||
export const sessionTable = pgTable("session", {
|
||||
export const session = pgTable("session", {
|
||||
id: text("id").primaryKey(),
|
||||
expiresAt: timestamp("expires_at").notNull(),
|
||||
token: text("token").notNull().unique(),
|
||||
@@ -14,4 +14,5 @@ export const sessionTable = pgTable("session", {
|
||||
.notNull()
|
||||
.references(() => user.id),
|
||||
impersonatedBy: text("impersonated_by"),
|
||||
activeOrganizationId: text("active_organization_id"),
|
||||
});
|
||||
|
||||
@@ -24,11 +24,16 @@ export const user = pgTable("user", {
|
||||
isRegistered: boolean("isRegistered").notNull().default(false),
|
||||
expirationDate: timestamp("expirationDate", {
|
||||
precision: 3,
|
||||
mode: "string",
|
||||
}).notNull(),
|
||||
createdAt: text("createdAt")
|
||||
mode: "date",
|
||||
})
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
.$defaultFn(() => new Date()),
|
||||
createdAt: timestamp("createdAt", {
|
||||
precision: 3,
|
||||
mode: "date",
|
||||
})
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date()),
|
||||
canCreateProjects: boolean("canCreateProjects").notNull().default(false),
|
||||
canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false),
|
||||
canCreateServices: boolean("canCreateServices").notNull().default(false),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { admin } from "better-auth/plugins";
|
||||
import { admin, createAuthMiddleware, organization } from "better-auth/plugins";
|
||||
import { db } from "../db";
|
||||
import * as schema from "../db/schema";
|
||||
import type { IncomingMessage } from "node:http";
|
||||
import { eq } from "drizzle-orm";
|
||||
export const auth = betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "pg",
|
||||
@@ -11,5 +13,38 @@ export const auth = betterAuth({
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
plugins: [admin()],
|
||||
hooks: {
|
||||
after: createAuthMiddleware(async (ctx) => {
|
||||
if (ctx.path.startsWith("/sign-up")) {
|
||||
const newSession = ctx.context.newSession;
|
||||
await db
|
||||
.update(schema.user)
|
||||
.set({
|
||||
role: "admin",
|
||||
})
|
||||
.where(eq(schema.user.id, newSession?.user?.id || ""));
|
||||
}
|
||||
}),
|
||||
},
|
||||
user: {
|
||||
additionalFields: {},
|
||||
},
|
||||
plugins: [organization()],
|
||||
});
|
||||
|
||||
export const validateRequest = async (request: IncomingMessage) => {
|
||||
const session = await auth.api.getSession({
|
||||
headers: new Headers({
|
||||
cookie: request.headers.cookie || "",
|
||||
}),
|
||||
});
|
||||
|
||||
if (!session?.session || !session.user) {
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
return session;
|
||||
};
|
||||
|
||||
@@ -94,8 +94,8 @@ export const updateAdminById = async (
|
||||
};
|
||||
|
||||
export const isAdminPresent = async () => {
|
||||
const admin = await db.query.users.findFirst({
|
||||
where: eq(users.role, "admin"),
|
||||
const admin = await db.query.user.findFirst({
|
||||
where: eq(user.role, "admin"),
|
||||
});
|
||||
if (!admin) {
|
||||
return false;
|
||||
|
||||
@@ -100,10 +100,11 @@ export const findAuthByEmail = async (email: string) => {
|
||||
};
|
||||
|
||||
export const findAuthById = async (authId: string) => {
|
||||
const result = await db.query.auth.findFirst({
|
||||
where: eq(auth.id, authId),
|
||||
const result = await db.query.user.findFirst({
|
||||
where: eq(user.id, authId),
|
||||
columns: {
|
||||
password: false,
|
||||
createdAt: false,
|
||||
updatedAt: false,
|
||||
},
|
||||
});
|
||||
if (!result) {
|
||||
|
||||
@@ -20,18 +20,16 @@ export const findUserById = async (userId: string) => {
|
||||
|
||||
export const findUserByAuthId = async (authId: string) => {
|
||||
const userR = await db.query.user.findFirst({
|
||||
where: eq(user.authId, authId),
|
||||
with: {
|
||||
auth: true,
|
||||
},
|
||||
where: eq(user.id, authId),
|
||||
with: {},
|
||||
});
|
||||
if (!user) {
|
||||
if (!userR) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
return user;
|
||||
return userR;
|
||||
};
|
||||
|
||||
export const findUsers = async (adminId: string) => {
|
||||
|
||||
Reference in New Issue
Block a user