feat: add credentials-based user provisioning alongside invitation flow

This commit is contained in:
mhbdev
2026-02-11 17:06:46 +03:30
parent 89416fef47
commit c6efe6f35b
3 changed files with 339 additions and 37 deletions

View File

@@ -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) {