feat: complete admin invite and user creation and recovery flows

This commit is contained in:
Zoe
2026-10-08 14:24:47 -05:00
parent 61f9bb74b4
commit b6f4d85ed7
23 changed files with 4430 additions and 110 deletions
@@ -0,0 +1,12 @@
import { eq } from 'drizzle-orm';
import { recoveryLinks } from '~~/drizzle/schema';
import { db } from '~~/server/lib/db';
/** Cancels the recovery link of a user, if there is one. */
export default defineEventHandler(async (event) => {
await protectAdminRoute(event);
await db.delete(recoveryLinks).where(eq(recoveryLinks.userId, getRouterParam(event, 'id')!));
return { ok: true };
});
@@ -0,0 +1,44 @@
import * as z from 'zod';
import { db } from '~~/server/lib/db';
/**
* Makes a one-time link the user can set a new password with, or their first
* one if the account was created by an admin and never had a password. The link is
* only returned here, afterwards nobody can read it back, admins included.
*/
export default defineEventHandler(async (event) => {
await protectAdminRoute(event);
const result = await readValidatedBody(event, (body) =>
z
.object({
// also turn two-factor off and sign every device out
resetSecurity: z.boolean(),
})
.safeParse(body),
);
if (!result.success) {
throw createError({
statusCode: 400,
message: result.error.issues[0]!.message,
});
}
const id = getRouterParam(event, 'id')!;
const account = await db.query.users.findFirst({
where: {
id,
},
});
if (!account) {
throw createError({
statusCode: 404,
statusMessage: 'User not found',
});
}
return await createRecoveryLink(id, result.data.resetSecurity);
});