feat(auth): align login and bootstrap with new user schema

This commit is contained in:
2026-06-19 01:05:33 +02:00
parent 2ed9445f7f
commit 01d89cd21b
10 changed files with 503 additions and 60 deletions
+96 -23
View File
@@ -1,4 +1,6 @@
import { fileURLToPath } from "node:url"
import { UserStatus } from "@/generated/prisma/client"
import { normalizeEmail } from "@/lib/email"
import { getPasswordHash } from "@/lib/security"
import prisma from "../src/lib/prisma"
@@ -8,10 +10,19 @@ type BootstrapAdminInput = {
password: string
}
function splitName(name: string) {
const [firstName = "Administrator", ...rest] = name.trim().split(/\s+/)
return {
firstName,
lastName: rest.join(" "),
}
}
function getBootstrapAdminInput(): BootstrapAdminInput {
const isProduction = process.env.NODE_ENV === "production"
const email = process.env.ADMIN_EMAIL ?? "admin@localhost"
const email = process.env.ADMIN_EMAIL ?? "admin@local.host"
const name = process.env.ADMIN_NAME ?? "Administrator"
const password = process.env.ADMIN_PASSWORD
@@ -28,36 +39,98 @@ function getBootstrapAdminInput(): BootstrapAdminInput {
export async function bootstrapAdmin(client: typeof prisma) {
const enabled = process.env.ADMIN_BOOTSTRAP_ENABLED !== "false"
const existingAdmin = await client.user.findFirst({
if (!enabled) return
const admin = getBootstrapAdminInput()
const email = normalizeEmail(admin.email)
const { firstName, lastName } = splitName(admin.name)
const existingUser = await client.user.findUnique({
where: {
role: "ADMIN",
isActive: true,
emailNormalized: email,
},
select: {
id: true,
passwordHash: true,
activatedAt: true,
person: {
select: {
id: true,
},
},
},
})
if (existingAdmin || !enabled) return
const user = existingUser
? await client.user.update({
where: {
id: existingUser.id,
},
data: {
name: admin.name,
email: admin.email,
emailNormalized: email,
role: "ADMIN",
status: UserStatus.ACTIVE,
...(existingUser.passwordHash
? {}
: {
passwordHash: await getPasswordHash(admin.password),
passwordChangedAt: new Date(),
}),
...(existingUser.activatedAt ? {} : { activatedAt: new Date() }),
},
select: {
id: true,
person: {
select: {
id: true,
},
},
},
})
: await client.user.create({
data: {
name: admin.name,
email: admin.email,
emailNormalized: email,
role: "ADMIN",
status: UserStatus.ACTIVE,
passwordHash: await getPasswordHash(admin.password),
activatedAt: new Date(),
passwordChangedAt: new Date(),
},
select: {
id: true,
person: {
select: {
id: true,
},
},
},
})
const admin = getBootstrapAdminInput()
await client.user.upsert({
where: {
email: admin.email,
},
update: {
role: "ADMIN",
isActive: true,
},
create: {
name: admin.name,
email: admin.email,
role: "ADMIN",
password: await getPasswordHash(admin.password),
isActive: true,
},
})
if (!user.person) {
await client.person.upsert({
where: {
userId: user.id,
},
update: {
firstName,
lastName,
email: admin.email,
},
create: {
firstName,
lastName,
email: admin.email,
user: {
connect: {
id: user.id,
},
},
},
})
}
}
async function main() {