chore: seminarhof customer project
This commit is contained in:
51
packages/functions/src/lib/provision-client-login.ts
Normal file
51
packages/functions/src/lib/provision-client-login.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { Kysely } from 'kysely'
|
||||
import type { DB } from '../types/db.types.js'
|
||||
|
||||
/**
|
||||
* Ensures a login-capable client user exists for a client org, idempotently.
|
||||
*
|
||||
* Finds or creates a `user` row (role `client`) keyed by lower-cased email,
|
||||
* then ensures a `client_member` row links that user to the client as `organiser`.
|
||||
*
|
||||
* Reuse-by-email is intentional: one person who organises for several clients
|
||||
* gets one login with multiple `client_member` rows.
|
||||
* Returns the resolved `userId`.
|
||||
*/
|
||||
export async function provisionClientLogin(
|
||||
kysely: Kysely<DB>,
|
||||
{ clientId, email, name }: { clientId: string; email: string; name: string | null },
|
||||
): Promise<string> {
|
||||
const normalisedEmail = email.toLowerCase()
|
||||
|
||||
const existing = await kysely
|
||||
.selectFrom('user')
|
||||
.where('email', '=', normalisedEmail)
|
||||
.select('id')
|
||||
.executeTakeFirst()
|
||||
|
||||
const id = existing?.id ?? crypto.randomUUID()
|
||||
if (!existing) {
|
||||
const now = new Date().toISOString()
|
||||
await kysely
|
||||
.insertInto('user')
|
||||
.values({
|
||||
id,
|
||||
email: normalisedEmail,
|
||||
name: name ?? normalisedEmail,
|
||||
emailVerified: 0,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
role: 'client',
|
||||
locale: 'de',
|
||||
} as any)
|
||||
.execute()
|
||||
}
|
||||
|
||||
await kysely
|
||||
.insertInto('clientMember')
|
||||
.values({ clientId, userId: id, role: 'organiser' })
|
||||
.onConflict((oc) => oc.columns(['clientId', 'userId']).doNothing())
|
||||
.execute()
|
||||
|
||||
return id
|
||||
}
|
||||
Reference in New Issue
Block a user