import { Collection , Db , ObjectID , IndexOptions } from 'mongodb' ; import { ConnectionInformations , CreateUserServicePassword , DatabaseInterface , User , Session , } from '@accounts/types' ; import { MongoSessions } from '@accounts/mongo-sessions' ; import { MongoServicePassword } from '@accounts/mongo-password' ; import { MongoServiceMagicLink } from '@accounts/mongo-magic-link' ; import { AccountsMongoOptions } from './types' ; const toMongoID = ( objectId ) => { if ( typeof objectId === 'string' ) { return new ObjectID ( objectId ) ; } return objectId ; } ; const defaultOptions = { collectionName : 'users' , timestamps : { createdAt : 'createdAt' , updatedAt : 'updatedAt' , } , convertUserIdToMongoObjectId : true , convertSessionIdToMongoObjectId : true , caseSensitiveUserName : true , dateProvider : ( date ? ) => ( date ? date . getTime ( ) : Date . now ( ) ) , } ; export class Mongo implements DatabaseInterface { private options : AccountsMongoOptions & typeof defaultOptions ; private db : Db ; private collection : Collection ; private sessions : MongoSessions ; private servicePassword : MongoServicePassword ; private serviceMagicLink : MongoServiceMagicLink ; constructor ( db , options = { } ) { this . options = { ... defaultOptions , ... options , timestamps : { ... defaultOptions . timestamps , ... options . timestamps } , } ; if ( ! db ) { throw new Error ( 'A database connection is required' ) ; } this . db = db ; this . collection = this . db . collection ( this . options . collectionName ) ; this . sessions = new MongoSessions ( { ... this . options , database : this . db } ) ; this . servicePassword = new MongoServicePassword ( { ... this . options , userCollectionName : this . options . collectionName , database : this . db , } ) ; this . serviceMagicLink = new MongoServiceMagicLink ( { ... this . options , userCollectionName : this . options . collectionName , database : this . db , } ) ; } param options public async setupIndexes ( options = { } ) { await this . sessions . setupIndexes ( options ) ; await this . servicePassword . setupIndexes ( options ) ; await this . serviceMagicLink . setupIndexes ( options ) ; } public async findUserById ( userId ) { return this . servicePassword . findUserById ( userId ) ; } public async createUser ( user ) { return this . servicePassword . createUser ( user ) ; } public async findUserByEmail ( email ) { return this . servicePassword . findUserByEmail ( email ) ; } public async findUserByUsername ( username ) { return this . servicePassword . findUserByUsername ( username ) ; } public async findPasswordHash ( userId ) { return this . servicePassword . findPasswordHash ( userId ) ; } public async findUserByEmailVerificationToken ( token ) { return this . servicePassword . findUserByEmailVerificationToken ( token ) ; } public async findUserByResetPasswordToken ( token ) { return this . servicePassword . findUserByResetPasswordToken ( token ) ; } public async addEmail ( userId , newEmail , verified ) { return this . servicePassword . addEmail ( userId , newEmail , verified ) ; } public async removeEmail ( userId , email ) { return this . servicePassword . removeEmail ( userId , email ) ; } public async verifyEmail ( userId , email ) { return this . servicePassword . verifyEmail ( userId , email ) ; } public async setUsername ( userId , newUsername ) { return this . servicePassword . setUsername ( userId , newUsername ) ; } public async setPassword ( userId , newPassword ) { return this . servicePassword . setPassword ( userId , newPassword ) ; } public async removeAllResetPasswordTokens ( userId ) { return this . servicePassword . removeAllResetPasswordTokens ( userId ) ; } public async addEmailVerificationToken ( userId , email , token ) { return this . servicePassword . addEmailVerificationToken ( userId , email , token ) ; } public async addResetPasswordToken ( userId , email , token , reason ) { return this . servicePassword . addResetPasswordToken ( userId , email , token , reason ) ; } public async addLoginToken ( userId , email , token ) { return this . serviceMagicLink . addLoginToken ( userId , email , token ) ; } public async findUserByLoginToken ( token ) { return this . serviceMagicLink . findUserByLoginToken ( token ) ; } public async removeAllLoginTokens ( userId ) { return this . serviceMagicLink . removeAllLoginTokens ( userId ) ; } public async createSession ( userId , token , connection = { } , extraData ? ) { return this . sessions . createSession ( userId , token , connection , extraData ) ; } public async findSessionById ( sessionId ) { return this . sessions . findSessionById ( sessionId ) ; } public async findSessionByToken ( token ) { return this . sessions . findSessionByToken ( token ) ; } public async updateSession ( sessionId , connection , newToken ? ) { return this . sessions . updateSession ( sessionId , connection , newToken ) ; } public async invalidateSession ( sessionId ) { return this . sessions . invalidateSession ( sessionId ) ; } public async invalidateAllSessions ( userId , excludedSessionIds ? ) { return this . sessions . invalidateAllSessions ( userId , excludedSessionIds ) ; } public async findUserByServiceId ( serviceName , serviceId ) { const user = await this . collection . findOne ( { [ `services.${ serviceName }.id` ] : serviceId , } ) ; if ( user ) { user . id = user . _id . toString ( ) ; } return user ; } public async setService ( userId , serviceName , service ) { const id = this . options . convertUserIdToMongoObjectId ? toMongoID ( userId ) : userId ; await this . collection . updateOne ( { _id : id } , { $set : { [ `services.${ serviceName }` ] : service , [ this . options . timestamps . updatedAt ] : this . options . dateProvider ( ) , } , } ) ; } public async unsetService ( userId , serviceName ) { const id = this . options . convertUserIdToMongoObjectId ? toMongoID ( userId ) : userId ; await this . collection . updateOne ( { _id : id } , { $set : { [ this . options . timestamps . updatedAt ] : this . options . dateProvider ( ) , } , $unset : { [ `services.${ serviceName }` ] : '' , } , } ) ; } public async setUserDeactivated ( userId , deactivated ) { const id = this . options . convertUserIdToMongoObjectId ? toMongoID ( userId ) : userId ; await this . collection . updateOne ( { _id : id } , { $set : { deactivated , [ this . options . timestamps . updatedAt ] : this . options . dateProvider ( ) , } , } ) ; } }