feat: setup and roles

This commit is contained in:
Zoe
2026-10-08 13:08:20 -05:00
parent 4bba529ba8
commit 7b0800882a
13 changed files with 3502 additions and 2 deletions
+91
View File
@@ -0,0 +1,91 @@
import { sql } from 'drizzle-orm';
import * as z from 'zod';
import { users } from '~~/drizzle/schema';
import { auth } from '~~/lib/auth';
import { db } from '~~/server/lib/db';
// any fixed number works, it only has to be the same for every request
const SETUP_LOCK_ID = 7_261_008;
/**
* Creates the first account of the instance as its admin.
*
* Public, because nobody can be signed in yet, and only works while there are
* no accounts at all. It does not go through sign up, so it also works when
* registration is turned off.
*/
export default defineEventHandler(async (event) => {
const result = await readValidatedBody(event, (body) =>
z
.object({
name: z.string().trim().min(1).max(128),
email: z.email(),
password: z.string().min(8).max(128),
})
.safeParse(body),
);
if (!result.success) {
throw createError({
statusCode: 400,
message: result.error.issues[0]!.message,
data: {
code: 'INVALID_BODY',
},
});
}
const { name, email, password } = result.data;
let alreadyDone = false;
try {
await db.transaction(async (tx) => {
// two people submitting the form at the same moment wait here, and
// the second one then finds the first one's account
await tx.execute(sql`select pg_advisory_xact_lock(${SETUP_LOCK_ID})`);
const existing = await tx.select({ id: users.id }).from(users).limit(1);
if (existing.length > 0) {
alreadyDone = true;
return;
}
await auth.api.createUser({
body: {
name,
email,
password,
role: 'admin',
},
});
});
} catch (error: any) {
const code = error?.body?.code as string | undefined;
if (code === undefined) {
console.error('Failed to create the admin account:', error);
}
throw createError({
statusCode: code === undefined ? 500 : 400,
statusMessage: 'Failed to set up the instance',
message: error?.body?.message ?? 'Failed to set up the instance',
data: {
code: code ?? 'INTERNAL_ERROR',
},
});
}
if (alreadyDone) {
throw createError({
statusCode: 409,
statusMessage: 'This instance is already set up',
data: {
code: 'SETUP_ALREADY_DONE',
},
});
}
return { ok: true };
});