feat: Implement comprehensive member management with user accounts, roles, and password handling for admin and mobile applications.
This commit is contained in:
@@ -55,11 +55,14 @@ async function createUserDirectly(opts: { email: string; name: string; password:
|
||||
return { id: userId }
|
||||
}
|
||||
|
||||
const nonEmptyString = (min = 2) =>
|
||||
z.preprocess((val) => (val === '' ? undefined : val), z.string().min(min).optional())
|
||||
|
||||
const MemberInput = z.object({
|
||||
name: z.string().min(2),
|
||||
betrieb: z.string().min(2),
|
||||
sparte: z.string().min(2),
|
||||
ort: z.string().min(2),
|
||||
betrieb: z.preprocess((v) => (v === '' ? undefined : v), z.string().optional()),
|
||||
sparte: z.preprocess((v) => (v === '' ? undefined : v), z.string().optional()),
|
||||
ort: z.preprocess((v) => (v === '' ? undefined : v), z.string().optional()),
|
||||
telefon: z.string().optional(),
|
||||
email: z.string().email(),
|
||||
status: z.enum(['aktiv', 'ruhend', 'ausgetreten']).default('aktiv'),
|
||||
@@ -375,7 +378,22 @@ export const membersRouter = router({
|
||||
* Update member (admin only)
|
||||
*/
|
||||
update: adminProcedure
|
||||
.input(z.object({ id: z.string(), data: MemberInput.partial().extend({ role: z.enum(['member', 'admin']).optional() }) }))
|
||||
.input(z.object({
|
||||
id: z.string(),
|
||||
data: z.object({
|
||||
name: nonEmptyString(),
|
||||
betrieb: nonEmptyString(),
|
||||
sparte: nonEmptyString(),
|
||||
ort: nonEmptyString(),
|
||||
telefon: z.string().optional(),
|
||||
email: z.preprocess((v) => (v === '' ? undefined : v), z.string().email().optional()),
|
||||
status: z.enum(['aktiv', 'ruhend', 'ausgetreten']).optional(),
|
||||
istAusbildungsbetrieb: z.boolean().optional(),
|
||||
seit: z.number().int().min(1900).max(2100).optional(),
|
||||
role: z.enum(['member', 'admin']).optional(),
|
||||
password: z.preprocess((val) => (val === '' ? undefined : val), z.string().min(8).optional()),
|
||||
}),
|
||||
}))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const { role, password, ...memberData } = input.data
|
||||
|
||||
@@ -524,19 +542,12 @@ export const membersRouter = router({
|
||||
where: { userId: targetUserId, providerId: 'credential' }
|
||||
})
|
||||
if (existingCredAccount) {
|
||||
const authHeaders = await getSanitizedHeaders()
|
||||
let nameForUpdate = memberData.name
|
||||
if (!nameForUpdate) {
|
||||
const existingUser = await ctx.prisma.user.findUnique({ where: { id: targetUserId }, select: { name: true } })
|
||||
nameForUpdate = existingUser?.name ?? undefined
|
||||
}
|
||||
await auth.api.updateUser({
|
||||
body: {
|
||||
userId: targetUserId,
|
||||
password,
|
||||
...(nameForUpdate && { name: nameForUpdate }),
|
||||
},
|
||||
headers: authHeaders,
|
||||
// Update password hash directly — auth.api.updateUser requires Better Auth admin role
|
||||
// which our custom UserRole system doesn't set, causing silent failures.
|
||||
const newHash = await hashPassword(password)
|
||||
await ctx.prisma.account.update({
|
||||
where: { id: existingCredAccount.id },
|
||||
data: { password: newHash },
|
||||
})
|
||||
} else {
|
||||
// No credential account — create one from scratch using Better Auth's own hash format
|
||||
|
||||
Reference in New Issue
Block a user