feat: bootstrap admin user on first run

When the user table is empty, init creates an admin via better-auth's
admin.createUser API. ADMIN_USERNAME / ADMIN_EMAIL / ADMIN_PASSWORD
configure it; password is generated and logged once if not provided.

Also silence the cosmetic async_hooks warning by marking it external.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 13:48:27 +02:00
parent 58ccce0d71
commit 5777e4910e
2 changed files with 35 additions and 2 deletions
+31 -1
View File
@@ -4,10 +4,12 @@ import { building, dev } from '$app/environment';
import { getAuth } from '$lib/auth/server';
import { getTextDirection } from '$lib/paraglide/runtime';
import { paraglideMiddleware } from '$lib/paraglide/server';
import { db } from '$lib/server/db';
import { getConfig } from '$lib/server/config';
import { svelteKitHandler } from 'better-auth/svelte-kit';
import { randomBytes } from 'node:crypto';
export const init: ServerInit = () => {
export const init: ServerInit = async () => {
const env = process.env;
const errors: string[] = [];
@@ -20,6 +22,34 @@ export const init: ServerInit = () => {
if (errors.length)
throw new Error(`Invalid environment:\n - ${errors.join('\n - ')}`);
// Bootstrap an admin if the user table is empty.
const userCount = db.$client.prepare('SELECT COUNT(*) AS c FROM user').get() as { c: number };
if (userCount.c > 0) return;
const username = env.ADMIN_USERNAME?.trim() || 'admin';
const email = env.ADMIN_EMAIL?.trim() || `${username}@example.com`;
const provided = env.ADMIN_PASSWORD?.trim();
const password = provided || randomBytes(18).toString('base64url');
await getAuth().api.createUser({
body: {
email,
name: username,
password,
role: 'admin',
data: { username, displayUsername: username, emailVerified: true }
}
});
const line = '━'.repeat(64);
if (provided) {
console.log(`${line}\nBootstrap admin "${username}" created (ADMIN_PASSWORD).\n${line}`);
} else {
console.log(
`${line}\nBootstrap admin created — sign in with these and rotate immediately:\n username: ${username}\n password: ${password}\nSet ADMIN_PASSWORD env to pick your own.\n${line}`
);
}
};
const handleBetterAuth: Handle = async ({ event, resolve }) => {
+4 -1
View File
@@ -5,11 +5,14 @@ import adapter from 'svelte-adapter-bun';
import { defineConfig } from 'vite';
export default defineConfig({
build: {
rollupOptions: { external: ['async_hooks'] }
},
plugins: [
tailwindcss(),
sveltekit({
adapter: adapter({
precompress:true
precompress: true
}),
compilerOptions: {
experimental: { async: true },