import type { Adapter, DatabaseSession, RegisteredDatabaseSessionAttributes, DatabaseUser, RegisteredDatabaseUserAttributes } from "lucia"; export class PrismaAdapter<_PrismaClient extends PrismaClient> implements Adapter { private sessionModel: PrismaModel; private userModel: PrismaModel; constructor(sessionModel: BasicPrismaModel, userModel: BasicPrismaModel) { this.sessionModel = sessionModel as any as PrismaModel; this.userModel = userModel as any as PrismaModel; } public async deleteSession(sessionId: string): Promise { try { await this.sessionModel.delete({ where: { id: sessionId } }); } catch { // ignore if session id is invalid } } public async deleteUserSessions(userId: string): Promise { await this.sessionModel.deleteMany({ where: { userId } }); } public async getSessionAndUser( sessionId: string ): Promise<[session: DatabaseSession | null, user: DatabaseUser | null]> { const userModelKey = this.userModel.name[0].toLowerCase() + this.userModel.name.slice(1); const result = await this.sessionModel.findUnique({ where: { id: sessionId }, include: { [userModelKey]: true } }); if (!result) return [null, null]; const userResult: UserSchema = result[ userModelKey as keyof typeof result ] as any as UserSchema; delete result[userModelKey as keyof typeof result]; return [transformIntoDatabaseSession(result), transformIntoDatabaseUser(userResult)]; } public async getUserSessions(userId: string): Promise { const result = await this.sessionModel.findMany({ where: { userId } }); return result.map(transformIntoDatabaseSession); } public async setSession(value: DatabaseSession): Promise { await this.sessionModel.create({ data: { id: value.id, userId: value.userId, expiresAt: value.expiresAt, ...value.attributes } }); } public async updateSessionExpiration(sessionId: string, expiresAt: Date): Promise { await this.sessionModel.update({ where: { id: sessionId }, data: { expiresAt } }); } public async deleteExpiredSessions(): Promise { await this.sessionModel.deleteMany({ where: { expiresAt: { lte: new Date() } } }); } } function transformIntoDatabaseSession(raw: SessionSchema): DatabaseSession { const { id, userId, expiresAt, ...attributes } = raw; return { id, userId, expiresAt, attributes }; } function transformIntoDatabaseUser(raw: UserSchema): DatabaseUser { const { id, ...attributes } = raw; return { id, attributes }; } interface PrismaClient { [K: string]: any; $connect: any; $transaction: any; } interface UserSchema extends RegisteredDatabaseUserAttributes { id: string; } interface SessionSchema extends RegisteredDatabaseSessionAttributes { id: string; userId: string; expiresAt: Date; } interface BasicPrismaModel { fields: any; findUnique: any; findMany: any; } type PrismaWhere<_Schema extends {}> = { [K in keyof _Schema]?: | _Schema[K] | { lte?: _Schema[K]; }; }; interface PrismaModel<_Schema extends {}> { name: string; findUnique: <_Included = {}>(options: { where: PrismaWhere<_Schema>; include?: Record; }) => Promise; findMany: (options?: { where: PrismaWhere<_Schema> }) => Promise<_Schema[]>; create: (options: { data: _Schema }) => Promise<_Schema>; delete: (options: { where: PrismaWhere<_Schema> }) => Promise; deleteMany: (options?: { where: PrismaWhere<_Schema> }) => Promise; update: (options: { data: Partial<_Schema>; where: PrismaWhere<_Schema> }) => Promise<_Schema>; }