feat: complete onboarding flow

This commit is contained in:
Zoe
2026-10-08 14:04:29 -05:00
parent fae05ebc4c
commit 61f9bb74b4
16 changed files with 684 additions and 297 deletions
+25 -3
View File
@@ -1,19 +1,41 @@
import { nanoid } from 'nanoid';
import * as z from 'zod';
import { invites } from '~~/drizzle/schema';
import { db } from '~~/server/lib/db';
const TOKEN_LENGTH = 32;
const HOUR_MS = 60 * 60 * 1000;
const MAX_USES = 1000;
const MAX_HOURS = 24 * 365;
/** Creates an invite link that lets one person register within a week. */
/** Creates an invite link that works for the given time and number of people. */
export default defineEventHandler(async (event) => {
await protectAdminRoute(event);
const result = await readValidatedBody(event, (body) =>
z
.object({
maxUses: z.int().min(1).max(MAX_USES),
expiresInHours: z.int().min(1).max(MAX_HOURS),
})
.safeParse(body),
);
if (!result.success) {
throw createError({
statusCode: 400,
message: result.error.issues[0]!.message,
});
}
const { maxUses, expiresInHours } = result.data;
const [invite] = await db.insert(invites)
.values({
token: nanoid(TOKEN_LENGTH),
createdBy: event.context.user!.id as string,
maxUses: 1,
expiresAt: new Date(Date.now() + INVITE_LIFETIME_MS),
maxUses,
expiresAt: new Date(Date.now() + expiresInHours * HOUR_MS),
})
.returning({
id: invites.id,
+7 -1
View File
@@ -1,6 +1,6 @@
import { sql } from 'drizzle-orm';
import * as z from 'zod';
import { users } from '~~/drizzle/schema';
import { INSTANCE_SETTINGS_ID, instanceSettings, users } from '~~/drizzle/schema';
import { auth } from '~~/lib/auth';
import { db } from '~~/server/lib/db';
@@ -59,6 +59,12 @@ export default defineEventHandler(async (event) => {
role: 'admin',
},
});
// A new instance starts invite only. The next setup step lets the
// admin pick, and if they never get there it is not left open.
await tx.insert(instanceSettings)
.values({ id: INSTANCE_SETTINGS_ID, registration: 'invite' })
.onConflictDoNothing();
});
} catch (error: any) {
const code = error?.body?.code as string | undefined;