import AbstractStore from "../store" ; import { RedisClient } from "redis" ; /**
 * Store implementation using Redis.
 *
 * The store takes a `RedisClient`, but does NOT take ownership of that
 * client. If you need to clean up, you must close the client yourself.
 */ export default class RedisStore extends AbstractStore { readonly client : RedisClient ; constructor ( client ) { super ( ) ; this . client = client ; } /** Fetch JSON from cache or using HTTP GET. */ async fetchCached ( cacheId , url ) { const key = `cache:${ cacheId }` ; const item = await new Promise ( ( resolve , reject ) => { this . client . get ( key , ( err , data ) => { err ? reject ( err ) : resolve ( data ) ; } ) ; } ) ; if ( item ) { return JSON . parse ( item ) ; } const { ttl , data } = await this . fetch ( url ) ; await new Promise < void > ( ( resolve , reject ) => { const json = JSON . stringify ( data ) ; this . client . psetex ( key , ttl , json , ( err ) => { err ? reject ( err ) : resolve ( ) ; } ) ; } ) ; return data ; } /** Generate and store a nonce. */ async createNonce ( email ) { const nonce = this . generateNonce ( email ) ; const key = `nonce:${ nonce }` ; await new Promise < void > ( ( resolve , reject ) => { this . client . psetex ( key , this . nonceTtl , email , ( err ) => { err ? reject ( err ) : resolve ( ) ; } ) ; } ) ; return nonce ; } /** Consume a nonce, and check if it's valid for the given email address. */ async consumeNonce ( nonce , email ) { const key = `nonce:${ nonce }` ; const item = await new Promise ( ( resolve , reject ) => { this . client . multi ( ) . get ( key ) . del ( key ) . exec ( ( err , replies ) => { err ? reject ( err ) : resolve ( replies [ 0 ] ) ; } ) ; } ) ; if ( item !== email ) { throw Error ( "Invalid or expired nonce" ) ; } } }