mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-29 02:55:22 +02:00
feat: add credentials-based user provisioning alongside invitation flow
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
import { db } from "@dokploy/server/db";
|
||||
import { apikey, member, user } from "@dokploy/server/db/schema";
|
||||
import {
|
||||
account,
|
||||
apikey,
|
||||
invitation,
|
||||
member,
|
||||
user,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import * as bcrypt from "bcrypt";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { auth } from "../lib/auth";
|
||||
|
||||
@@ -389,6 +396,93 @@ export const findMemberById = async (
|
||||
return result;
|
||||
};
|
||||
|
||||
export const createOrganizationUserWithCredentials = async ({
|
||||
organizationId,
|
||||
email,
|
||||
password,
|
||||
role,
|
||||
}: {
|
||||
organizationId: string;
|
||||
email: string;
|
||||
password: string;
|
||||
role: "member" | "admin";
|
||||
}) => {
|
||||
const normalizedEmail = email.trim().toLowerCase();
|
||||
const now = new Date();
|
||||
|
||||
return await db.transaction(async (tx) => {
|
||||
const existingUser = await tx.query.user.findFirst({
|
||||
where: eq(user.email, normalizedEmail),
|
||||
columns: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message:
|
||||
"This email already has an account. Use the invitation link flow for existing users.",
|
||||
});
|
||||
}
|
||||
|
||||
const createdUser = await tx
|
||||
.insert(user)
|
||||
.values({
|
||||
email: normalizedEmail,
|
||||
emailVerified: false,
|
||||
updatedAt: now,
|
||||
})
|
||||
.returning({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
})
|
||||
.then((res) => res[0]);
|
||||
|
||||
if (!createdUser) {
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "Failed to create user",
|
||||
});
|
||||
}
|
||||
|
||||
await tx.insert(account).values({
|
||||
userId: createdUser.id,
|
||||
providerId: "credential",
|
||||
password: bcrypt.hashSync(password, 10),
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
|
||||
await tx.insert(member).values({
|
||||
organizationId,
|
||||
userId: createdUser.id,
|
||||
role,
|
||||
createdAt: now,
|
||||
isDefault: true,
|
||||
});
|
||||
|
||||
await tx
|
||||
.update(invitation)
|
||||
.set({
|
||||
status: "canceled",
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(invitation.organizationId, organizationId),
|
||||
eq(invitation.email, normalizedEmail),
|
||||
eq(invitation.status, "pending"),
|
||||
),
|
||||
);
|
||||
|
||||
return {
|
||||
userId: createdUser.id,
|
||||
email: createdUser.email,
|
||||
role,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const updateUser = async (userId: string, userData: Partial<User>) => {
|
||||
// Validate email if it's being updated
|
||||
if (userData.email !== undefined) {
|
||||
|
||||
Reference in New Issue
Block a user