1- import { SessionData , Store } from "express-session"
1+ import { type SessionData , Store } from "express-session"
2+ import type { RedisClientType , RedisClusterType } from "redis"
23
34type Callback = ( _err ?: unknown , _data ?: any ) => any
45
@@ -8,15 +9,6 @@ function optionalCb(err: unknown, data: unknown, cb?: Callback) {
89 return data
910}
1011
11- interface NormalizedRedisClient {
12- get ( key : string ) : Promise < string | null >
13- set ( key : string , value : string , ttl ?: number ) : Promise < string | null >
14- expire ( key : string , ttl : number ) : Promise < number | boolean >
15- scanIterator ( match : string , count : number ) : AsyncIterable < string >
16- del ( key : string [ ] ) : Promise < number >
17- mget ( key : string [ ] ) : Promise < ( string | null ) [ ] >
18- }
19-
2012interface Serializer {
2113 parse ( s : string ) : SessionData | Promise < SessionData >
2214 stringify ( s : SessionData ) : string
@@ -27,17 +19,17 @@ interface RedisStoreOptions {
2719 prefix ?: string
2820 scanCount ?: number
2921 serializer ?: Serializer
30- ttl ?: number | { ( sess : SessionData ) : number }
22+ ttl ?: number | ( ( sess : SessionData ) => number )
3123 disableTTL ?: boolean
3224 disableTouch ?: boolean
3325}
3426
3527export class RedisStore extends Store {
36- client : NormalizedRedisClient
28+ client : RedisClientType | RedisClusterType
3729 prefix : string
3830 scanCount : number
3931 serializer : Serializer
40- ttl : number | { ( sess : SessionData ) : number }
32+ ttl : number | ( ( sess : SessionData ) => number )
4133 disableTTL : boolean
4234 disableTouch : boolean
4335
@@ -49,57 +41,7 @@ export class RedisStore extends Store {
4941 this . ttl = opts . ttl || 86400 // One day in seconds.
5042 this . disableTTL = opts . disableTTL || false
5143 this . disableTouch = opts . disableTouch || false
52- this . client = this . normalizeClient ( opts . client )
53- }
54-
55- // Create a redis and ioredis compatible client
56- private normalizeClient ( client : any ) : NormalizedRedisClient {
57- let isRedis = "scanIterator" in client || "masters" in client
58- let isRedisCluster = "masters" in client
59-
60- return {
61- get : ( key ) => client . get ( key ) ,
62- set : ( key , val , ttl ) => {
63- if ( ttl ) {
64- return isRedis
65- ? client . set ( key , val , { EX : ttl } )
66- : client . set ( key , val , "EX" , ttl )
67- }
68- return client . set ( key , val )
69- } ,
70- del : ( key ) => client . del ( key ) ,
71- expire : ( key , ttl ) => client . expire ( key , ttl ) ,
72- mget : ( keys ) => ( isRedis ? client . mGet ( keys ) : client . mget ( keys ) ) ,
73- scanIterator : ( match , count ) => {
74- // node-redis createCluster impl.
75- if ( isRedisCluster ) {
76- return ( async function * ( ) {
77- for ( const master of client . masters ) {
78- const nodeClient = await client . nodeClient ( master )
79-
80- for await ( const key of nodeClient . scanIterator ( {
81- COUNT : count ,
82- MATCH : match ,
83- } ) ) {
84- yield key
85- }
86- }
87- } ) ( )
88- }
89-
90- if ( isRedis ) return client . scanIterator ( { MATCH : match , COUNT : count } )
91-
92- // ioredis impl.
93- return ( async function * ( ) {
94- let [ c , xs ] = await client . scan ( "0" , "MATCH" , match , "COUNT" , count )
95- for ( let key of xs ) yield key
96- while ( c !== "0" ) {
97- ; [ c , xs ] = await client . scan ( c , "MATCH" , match , "COUNT" , count )
98- for ( let key of xs ) yield key
99- }
100- } ) ( )
101- } ,
102- }
44+ this . client = opts . client
10345 }
10446
10547 async get ( sid : string , cb ?: Callback ) {
@@ -115,16 +57,18 @@ export class RedisStore extends Store {
11557
11658 async set ( sid : string , sess : SessionData , cb ?: Callback ) {
11759 let key = this . prefix + sid
118- let ttl = this . _getTTL ( sess )
60+ let ttl = this . getTTL ( sess )
11961 try {
12062 if ( ttl > 0 ) {
12163 let val = this . serializer . stringify ( sess )
12264 if ( this . disableTTL ) await this . client . set ( key , val )
123- else await this . client . set ( key , val , ttl )
65+ else
66+ await this . client . set ( key , val , {
67+ expiration : { type : "EX" , value : ttl } ,
68+ } )
12469 return optionalCb ( null , null , cb )
125- } else {
126- return this . destroy ( sid , cb )
12770 }
71+ return this . destroy ( sid , cb )
12872 } catch ( err ) {
12973 return optionalCb ( err , null , cb )
13074 }
@@ -134,7 +78,7 @@ export class RedisStore extends Store {
13478 let key = this . prefix + sid
13579 if ( this . disableTouch || this . disableTTL ) return optionalCb ( null , null , cb )
13680 try {
137- await this . client . expire ( key , this . _getTTL ( sess ) )
81+ await this . client . expire ( key , this . getTTL ( sess ) )
13882 return optionalCb ( null , null , cb )
13983 } catch ( err ) {
14084 return optionalCb ( err , null , cb )
@@ -153,7 +97,7 @@ export class RedisStore extends Store {
15397
15498 async clear ( cb ?: Callback ) {
15599 try {
156- let keys = await this . _getAllKeys ( )
100+ let keys = await this . getAllKeys ( )
157101 if ( ! keys . length ) return optionalCb ( null , null , cb )
158102 await this . client . del ( keys )
159103 return optionalCb ( null , null , cb )
@@ -164,7 +108,7 @@ export class RedisStore extends Store {
164108
165109 async length ( cb ?: Callback ) {
166110 try {
167- let keys = await this . _getAllKeys ( )
111+ let keys = await this . getAllKeys ( )
168112 return optionalCb ( null , keys . length , cb )
169113 } catch ( err ) {
170114 return optionalCb ( err , null , cb )
@@ -174,7 +118,7 @@ export class RedisStore extends Store {
174118 async ids ( cb ?: Callback ) {
175119 let len = this . prefix . length
176120 try {
177- let keys = await this . _getAllKeys ( )
121+ let keys = await this . getAllKeys ( )
178122 return optionalCb (
179123 null ,
180124 keys . map ( ( k ) => k . substring ( len ) ) ,
@@ -188,10 +132,10 @@ export class RedisStore extends Store {
188132 async all ( cb ?: Callback ) {
189133 let len = this . prefix . length
190134 try {
191- let keys = await this . _getAllKeys ( )
135+ let keys = await this . getAllKeys ( )
192136 if ( keys . length === 0 ) return optionalCb ( null , [ ] , cb )
193137
194- let data = await this . client . mget ( keys )
138+ let data = await this . client . mGet ( keys )
195139 let results = data . reduce ( ( acc , raw , idx ) => {
196140 if ( ! raw ) return acc
197141 let sess = this . serializer . parse ( raw ) as any
@@ -205,13 +149,13 @@ export class RedisStore extends Store {
205149 }
206150 }
207151
208- private _getTTL ( sess : SessionData ) {
152+ private getTTL ( sess : SessionData ) {
209153 if ( typeof this . ttl === "function" ) {
210154 return this . ttl ( sess )
211155 }
212156
213157 let ttl
214- if ( sess && sess . cookie && sess . cookie . expires ) {
158+ if ( sess ? .cookie ? .expires ) {
215159 let ms = Number ( new Date ( sess . cookie . expires ) ) - Date . now ( )
216160 ttl = Math . ceil ( ms / 1000 )
217161 } else {
@@ -220,12 +164,34 @@ export class RedisStore extends Store {
220164 return ttl
221165 }
222166
223- private async _getAllKeys ( ) {
167+ private async getAllKeys ( ) {
224168 let pattern = this . prefix + "*"
225- let keys = [ ]
226- for await ( let key of this . client . scanIterator ( pattern , this . scanCount ) ) {
227- keys . push ( key )
169+ let set = new Set < string > ( )
170+ for await ( let keys of this . scanIterator ( pattern , this . scanCount ) ) {
171+ for ( let key of keys ) {
172+ set . add ( key )
173+ }
174+ }
175+ return set . size > 0 ? Array . from ( set ) : [ ]
176+ }
177+
178+ private scanIterator ( match : string , count : number ) {
179+ let client = this . client
180+
181+ if ( ! ( "masters" in client ) ) {
182+ return client . scanIterator ( { MATCH : match , COUNT : count } )
228183 }
229- return keys
184+
185+ return ( async function * ( ) {
186+ for ( let master of client . masters ) {
187+ let c = await client . nodeClient ( master )
188+ for await ( let keys of c . scanIterator ( {
189+ COUNT : count ,
190+ MATCH : match ,
191+ } ) ) {
192+ yield keys
193+ }
194+ }
195+ } ) ( )
230196 }
231197}
0 commit comments