Spaces:
Sleeping
Sleeping
| import { getCoupledCookieHash, refreshSessionCookie } from "$lib/server/auth"; | |
| import { collections } from "$lib/server/database"; | |
| import { ObjectId } from "mongodb"; | |
| import { DEFAULT_SETTINGS } from "$lib/types/Settings"; | |
| import { error, type Cookies } from "@sveltejs/kit"; | |
| import crypto from "crypto"; | |
| import { sha256 } from "$lib/utils/sha256"; | |
| import { addWeeks } from "date-fns"; | |
| import { logger } from "$lib/server/logger"; | |
| import type { AuthProvider } from "$lib/types/User"; | |
| import { encryptToken } from "$lib/server/tokenEncryption"; | |
| export interface UserUpdateData { | |
| authProvider: AuthProvider; | |
| authId: string; | |
| username?: string; | |
| name: string; | |
| email?: string; | |
| avatarUrl?: string; | |
| isAdmin?: boolean; | |
| isEarlyAccess?: boolean; | |
| passwordHash?: string; | |
| recoveryKeyHash?: string; | |
| } | |
| export async function updateUserSession(params: { | |
| userData: UserUpdateData; | |
| locals: App.Locals; | |
| cookies: Cookies; | |
| userAgent?: string; | |
| ip?: string; | |
| hfAccessToken?: string; | |
| }) { | |
| const { userData, locals, cookies, userAgent, ip, hfAccessToken } = params; | |
| logger.info( | |
| { | |
| authProvider: userData.authProvider, | |
| authId: userData.authId, | |
| login_username: userData.username, | |
| login_name: userData.name, | |
| login_email: userData.email, | |
| }, | |
| "user login" | |
| ); | |
| logger.debug( | |
| { | |
| isAdmin: userData.isAdmin, | |
| isEarlyAccess: userData.isEarlyAccess, | |
| authId: userData.authId, | |
| }, | |
| `Updating user ${userData.authId}` | |
| ); | |
| // check if user already exists (by authProvider + authId, or legacy hfUserId for HF) | |
| const existingUser = | |
| userData.authProvider === "huggingface" | |
| ? await collections.users.findOne({ | |
| $or: [ | |
| { authProvider: "huggingface", authId: userData.authId }, | |
| { hfUserId: userData.authId }, // Legacy lookup | |
| ], | |
| }) | |
| : await collections.users.findOne({ | |
| authProvider: userData.authProvider, | |
| authId: userData.authId, | |
| }); | |
| let userId = existingUser?._id; | |
| // update session cookie on login | |
| const previousSessionId = locals.sessionId; | |
| const secretSessionId = crypto.randomUUID(); | |
| const sessionId = await sha256(secretSessionId); | |
| if (await collections.sessions.findOne({ sessionId })) { | |
| error(500, "Session ID collision"); | |
| } | |
| locals.sessionId = sessionId; | |
| // Get cookie hash if coupling is enabled | |
| const coupledCookieHash = await getCoupledCookieHash({ type: "svelte", value: cookies }); | |
| const userUpdateData = { | |
| username: userData.username, | |
| name: userData.name, | |
| avatarUrl: userData.avatarUrl, | |
| isAdmin: userData.isAdmin, | |
| isEarlyAccess: userData.isEarlyAccess, | |
| ...(userData.authProvider === "huggingface" ? { hfUserId: userData.authId } : {}), | |
| ...(userData.passwordHash ? { passwordHash: userData.passwordHash } : {}), | |
| ...(userData.recoveryKeyHash ? { recoveryKeyHash: userData.recoveryKeyHash } : {}), | |
| }; | |
| if (existingUser) { | |
| // Update existing user | |
| await collections.users.updateOne( | |
| { _id: existingUser._id }, | |
| { | |
| $set: { | |
| ...userUpdateData, | |
| authProvider: userData.authProvider, | |
| authId: userData.authId, | |
| email: userData.email, | |
| updatedAt: new Date(), | |
| }, | |
| } | |
| ); | |
| // remove previous session if it exists and add new one | |
| 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), | |
| ...(coupledCookieHash ? { coupledCookieHash } : {}), | |
| }); | |
| } else { | |
| // user doesn't exist yet, create a new one | |
| const { insertedId } = await collections.users.insertOne({ | |
| _id: new ObjectId(), | |
| createdAt: new Date(), | |
| updatedAt: new Date(), | |
| username: userUpdateData.username, | |
| name: userUpdateData.name, | |
| avatarUrl: userUpdateData.avatarUrl, | |
| isAdmin: userUpdateData.isAdmin, | |
| isEarlyAccess: userUpdateData.isEarlyAccess, | |
| authProvider: userData.authProvider, | |
| authId: userData.authId, | |
| email: userData.email, | |
| ...(userData.authProvider === "huggingface" ? { hfUserId: userData.authId } : {}), | |
| ...(userData.passwordHash ? { passwordHash: userData.passwordHash } : {}), | |
| ...(userData.recoveryKeyHash ? { recoveryKeyHash: userData.recoveryKeyHash } : {}), | |
| }); | |
| 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), | |
| ...(coupledCookieHash ? { coupledCookieHash } : {}), | |
| }); | |
| // move pre-existing settings to new user | |
| const { matchedCount } = await collections.settings.updateOne( | |
| { sessionId: previousSessionId }, | |
| { | |
| $set: { userId, updatedAt: new Date() }, | |
| $unset: { sessionId: "" }, | |
| } | |
| ); | |
| if (!matchedCount) { | |
| // if no settings found for user, create default settings | |
| await collections.settings.insertOne({ | |
| userId, | |
| updatedAt: new Date(), | |
| createdAt: new Date(), | |
| ...DEFAULT_SETTINGS, | |
| }); | |
| } | |
| } | |
| // refresh session cookie | |
| refreshSessionCookie(cookies, secretSessionId); | |
| // migrate pre-existing conversations | |
| await collections.conversations.updateMany( | |
| { sessionId: previousSessionId }, | |
| { | |
| $set: { userId }, | |
| $unset: { sessionId: "" }, | |
| } | |
| ); | |
| // Manage stored Hugging Face tokens based on auth provider | |
| if (userData.authProvider === "huggingface" && hfAccessToken) { | |
| const encryptedToken = encryptToken(hfAccessToken); | |
| await collections.userTokens.updateOne( | |
| { userId, provider: "huggingface" }, | |
| { | |
| $set: { | |
| encryptedToken, | |
| updatedAt: new Date(), | |
| }, | |
| $setOnInsert: { | |
| _id: new ObjectId(), | |
| userId, | |
| provider: "huggingface", | |
| createdAt: new Date(), | |
| }, | |
| }, | |
| { upsert: true } | |
| ); | |
| } else { | |
| await collections.userTokens.deleteOne({ userId, provider: "huggingface" }); | |
| } | |
| return { userId }; | |
| } | |