You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import "dotenv/config";
|
|
import { eq } from "drizzle-orm";
|
|
import { db } from "../db";
|
|
import { user } from "../db/schema";
|
|
import { auth } from "../lib/auth";
|
|
|
|
function requireEnv(name: string): string {
|
|
const value = process.env[name]?.trim();
|
|
if (!value) throw new Error(`${name} is not set in .env`);
|
|
return value;
|
|
}
|
|
|
|
async function seedAdmin() {
|
|
// The username plugin stores usernames lowercased, so lookups must match that.
|
|
const displayUsername = requireEnv("SEED_ADMIN_USERNAME");
|
|
const username = displayUsername.toLowerCase();
|
|
const password = requireEnv("SEED_ADMIN_PASSWORD");
|
|
const name = process.env.SEED_ADMIN_NAME?.trim() || "Administrator";
|
|
const email = process.env.SEED_ADMIN_EMAIL?.trim() || `${username}@cleo.local`;
|
|
|
|
const [existing] = await db
|
|
.select({ id: user.id })
|
|
.from(user)
|
|
.where(eq(user.username, username))
|
|
.limit(1);
|
|
if (existing) {
|
|
console.log(`Admin "${username}" already exists, skipped.`);
|
|
return;
|
|
}
|
|
|
|
// Goes through Better Auth's internal adapter instead of the sign-up endpoint, which is
|
|
// closed by `disableSignUp`. Hashing with ctx.password keeps the format login expects.
|
|
const ctx = await auth.$context;
|
|
const created = await ctx.internalAdapter.createUser(
|
|
{ name, email, username, displayUsername },
|
|
{ method: "admin" },
|
|
);
|
|
await ctx.internalAdapter.linkAccount({
|
|
userId: created.id,
|
|
providerId: "credential",
|
|
accountId: created.id,
|
|
password: await ctx.password.hash(password),
|
|
});
|
|
|
|
console.log(`Admin "${username}" created.`);
|
|
}
|
|
|
|
seedAdmin()
|
|
.catch((error) => {
|
|
console.error(error);
|
|
process.exitCode = 1;
|
|
})
|
|
.finally(() => db.$client.end());
|