81 lines
1.7 KiB
TypeScript
81 lines
1.7 KiB
TypeScript
import { fileURLToPath } from "node:url"
|
|
import { getPasswordHash } from "@/lib/security"
|
|
import prisma from "../src/lib/prisma"
|
|
|
|
type BootstrapAdminInput = {
|
|
username: string
|
|
email: string
|
|
name: string
|
|
password: string
|
|
}
|
|
|
|
function getBootstrapAdminInput(): BootstrapAdminInput {
|
|
const isProduction = process.env.NODE_ENV === "production"
|
|
|
|
const username = process.env.ADMIN_USERNAME ?? "admin"
|
|
const email = process.env.ADMIN_EMAIL ?? "admin@localhost"
|
|
const name = process.env.ADMIN_NAME ?? "Administrator"
|
|
const password = process.env.ADMIN_PASSWORD
|
|
|
|
if (isProduction && !password) {
|
|
throw new Error("ADMIN_PASSWORD is required to bootstrap an admin user")
|
|
}
|
|
|
|
return {
|
|
username,
|
|
email,
|
|
name,
|
|
password: password ?? "admin",
|
|
}
|
|
}
|
|
|
|
export async function bootstrapAdmin(client: typeof prisma) {
|
|
const enabled = process.env.ADMIN_BOOTSTRAP_ENABLED !== "false"
|
|
const existingAdmin = await client.user.findFirst({
|
|
where: {
|
|
role: "ADMIN",
|
|
isActive: true,
|
|
},
|
|
select: {
|
|
id: true,
|
|
},
|
|
})
|
|
|
|
if (existingAdmin || !enabled) return
|
|
|
|
const admin = getBootstrapAdminInput()
|
|
|
|
await client.user.upsert({
|
|
where: {
|
|
email: admin.email,
|
|
},
|
|
update: {
|
|
role: "ADMIN",
|
|
isActive: true,
|
|
},
|
|
create: {
|
|
name: admin.name,
|
|
username: admin.username,
|
|
email: admin.email,
|
|
role: "ADMIN",
|
|
password: await getPasswordHash(admin.password),
|
|
isActive: true,
|
|
},
|
|
})
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
await bootstrapAdmin(prisma)
|
|
} finally {
|
|
await prisma.$disconnect()
|
|
}
|
|
}
|
|
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
main().catch((error) => {
|
|
console.error(error)
|
|
process.exit(1)
|
|
})
|
|
}
|