| import { refreshSessionCookie } from "$lib/server/auth"; |
| import { collections } from "$lib/server/database"; |
| import { ObjectId } from "mongodb"; |
| import { DEFAULT_SETTINGS } from "$lib/types/Settings"; |
| import { z } from "zod"; |
| import type { UserinfoResponse } from "openid-client"; |
| import { error, type Cookies } from "@sveltejs/kit"; |
| import crypto from "crypto"; |
| import { sha256 } from "$lib/utils/sha256"; |
| import { addWeeks } from "date-fns"; |
| import { OIDConfig } from "$lib/server/auth"; |
|
|
| export async function updateUser(params: { |
| userData: UserinfoResponse; |
| locals: App.Locals; |
| cookies: Cookies; |
| userAgent?: string; |
| ip?: string; |
| }) { |
| const { userData, locals, cookies, userAgent, ip } = params; |
|
|
| |
| |
| if (!userData.preferred_username && userData.upn) { |
| userData.preferred_username = userData.upn as string; |
| } |
|
|
| const { |
| preferred_username: username, |
| name, |
| email, |
| picture: avatarUrl, |
| sub: hfUserId, |
| } = z |
| .object({ |
| preferred_username: z.string().optional(), |
| name: z.string(), |
| picture: z.string().optional(), |
| sub: z.string(), |
| email: z.string().email().optional(), |
| }) |
| .setKey(OIDConfig.NAME_CLAIM, z.string()) |
| .refine((data) => data.preferred_username || data.email, { |
| message: "Either preferred_username or email must be provided by the provider.", |
| }) |
| .transform((data) => ({ |
| ...data, |
| name: data[OIDConfig.NAME_CLAIM], |
| })) |
| .parse(userData) as { |
| preferred_username?: string; |
| email?: string; |
| picture?: string; |
| sub: string; |
| name: string; |
| } & Record<string, string>; |
|
|
| |
| |
|
|
| |
| const existingUser = await collections.users.findOne({ hfUserId }); |
| let userId = existingUser?._id; |
|
|
| |
| const previousSessionId = locals.sessionId; |
| const secretSessionId = crypto.randomUUID(); |
| const sessionId = await sha256(secretSessionId); |
|
|
| if (await collections.sessions.findOne({ sessionId })) { |
| throw error(500, "Session ID collision"); |
| } |
|
|
| locals.sessionId = sessionId; |
|
|
| if (existingUser) { |
| |
| await collections.users.updateOne( |
| { _id: existingUser._id }, |
| { $set: { username, name, avatarUrl } } |
| ); |
|
|
| |
| await collections.sessions.deleteOne({ sessionId: previousSessionId }); |
| await collections.sessions.insertOne({ |
| _id: new ObjectId(), |
| sessionId: locals.sessionId, |
| userId: existingUser._id, |
| createdAt: new Date(), |
| updatedAt: new Date(), |
| userAgent, |
| ip, |
| expiresAt: addWeeks(new Date(), 2), |
| }); |
| } else { |
| |
| const { insertedId } = await collections.users.insertOne({ |
| _id: new ObjectId(), |
| createdAt: new Date(), |
| updatedAt: new Date(), |
| username, |
| name, |
| email, |
| avatarUrl, |
| hfUserId, |
| }); |
|
|
| userId = insertedId; |
|
|
| await collections.sessions.insertOne({ |
| _id: new ObjectId(), |
| sessionId: locals.sessionId, |
| userId, |
| createdAt: new Date(), |
| updatedAt: new Date(), |
| userAgent, |
| ip, |
| expiresAt: addWeeks(new Date(), 2), |
| }); |
|
|
| |
| const { matchedCount } = await collections.settings.updateOne( |
| { sessionId: previousSessionId }, |
| { |
| $set: { userId, updatedAt: new Date() }, |
| $unset: { sessionId: "" }, |
| } |
| ); |
|
|
| if (!matchedCount) { |
| |
| await collections.settings.insertOne({ |
| userId, |
| ethicsModalAcceptedAt: new Date(), |
| updatedAt: new Date(), |
| createdAt: new Date(), |
| ...DEFAULT_SETTINGS, |
| }); |
| } |
| } |
|
|
| |
| refreshSessionCookie(cookies, secretSessionId); |
|
|
| |
| await collections.conversations.updateMany( |
| { sessionId: previousSessionId }, |
| { |
| $set: { userId }, |
| $unset: { sessionId: "" }, |
| } |
| ); |
| } |
|
|