1
- import { SessionData , Store } from "express-session"
1
+ import { type SessionData , Store } from "express-session"
2
+ import type { RedisClientType , RedisClusterType } from "redis"
2
3
3
4
type Callback = ( _err ?: unknown , _data ?: any ) => any
4
5
@@ -8,15 +9,6 @@ function optionalCb(err: unknown, data: unknown, cb?: Callback) {
8
9
return data
9
10
}
10
11
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
-
20
12
interface Serializer {
21
13
parse ( s : string ) : SessionData | Promise < SessionData >
22
14
stringify ( s : SessionData ) : string
@@ -27,17 +19,17 @@ interface RedisStoreOptions {
27
19
prefix ?: string
28
20
scanCount ?: number
29
21
serializer ?: Serializer
30
- ttl ?: number | { ( sess : SessionData ) : number }
22
+ ttl ?: number | ( ( sess : SessionData ) => number )
31
23
disableTTL ?: boolean
32
24
disableTouch ?: boolean
33
25
}
34
26
35
27
export class RedisStore extends Store {
36
- client : NormalizedRedisClient
28
+ client : RedisClientType | RedisClusterType
37
29
prefix : string
38
30
scanCount : number
39
31
serializer : Serializer
40
- ttl : number | { ( sess : SessionData ) : number }
32
+ ttl : number | ( ( sess : SessionData ) => number )
41
33
disableTTL : boolean
42
34
disableTouch : boolean
43
35
@@ -49,57 +41,7 @@ export class RedisStore extends Store {
49
41
this . ttl = opts . ttl || 86400 // One day in seconds.
50
42
this . disableTTL = opts . disableTTL || false
51
43
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
103
45
}
104
46
105
47
async get ( sid : string , cb ?: Callback ) {
@@ -115,16 +57,18 @@ export class RedisStore extends Store {
115
57
116
58
async set ( sid : string , sess : SessionData , cb ?: Callback ) {
117
59
let key = this . prefix + sid
118
- let ttl = this . _getTTL ( sess )
60
+ let ttl = this . getTTL ( sess )
119
61
try {
120
62
if ( ttl > 0 ) {
121
63
let val = this . serializer . stringify ( sess )
122
64
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
+ } )
124
69
return optionalCb ( null , null , cb )
125
- } else {
126
- return this . destroy ( sid , cb )
127
70
}
71
+ return this . destroy ( sid , cb )
128
72
} catch ( err ) {
129
73
return optionalCb ( err , null , cb )
130
74
}
@@ -134,7 +78,7 @@ export class RedisStore extends Store {
134
78
let key = this . prefix + sid
135
79
if ( this . disableTouch || this . disableTTL ) return optionalCb ( null , null , cb )
136
80
try {
137
- await this . client . expire ( key , this . _getTTL ( sess ) )
81
+ await this . client . expire ( key , this . getTTL ( sess ) )
138
82
return optionalCb ( null , null , cb )
139
83
} catch ( err ) {
140
84
return optionalCb ( err , null , cb )
@@ -153,7 +97,7 @@ export class RedisStore extends Store {
153
97
154
98
async clear ( cb ?: Callback ) {
155
99
try {
156
- let keys = await this . _getAllKeys ( )
100
+ let keys = await this . getAllKeys ( )
157
101
if ( ! keys . length ) return optionalCb ( null , null , cb )
158
102
await this . client . del ( keys )
159
103
return optionalCb ( null , null , cb )
@@ -164,7 +108,7 @@ export class RedisStore extends Store {
164
108
165
109
async length ( cb ?: Callback ) {
166
110
try {
167
- let keys = await this . _getAllKeys ( )
111
+ let keys = await this . getAllKeys ( )
168
112
return optionalCb ( null , keys . length , cb )
169
113
} catch ( err ) {
170
114
return optionalCb ( err , null , cb )
@@ -174,7 +118,7 @@ export class RedisStore extends Store {
174
118
async ids ( cb ?: Callback ) {
175
119
let len = this . prefix . length
176
120
try {
177
- let keys = await this . _getAllKeys ( )
121
+ let keys = await this . getAllKeys ( )
178
122
return optionalCb (
179
123
null ,
180
124
keys . map ( ( k ) => k . substring ( len ) ) ,
@@ -188,10 +132,10 @@ export class RedisStore extends Store {
188
132
async all ( cb ?: Callback ) {
189
133
let len = this . prefix . length
190
134
try {
191
- let keys = await this . _getAllKeys ( )
135
+ let keys = await this . getAllKeys ( )
192
136
if ( keys . length === 0 ) return optionalCb ( null , [ ] , cb )
193
137
194
- let data = await this . client . mget ( keys )
138
+ let data = await this . client . mGet ( keys )
195
139
let results = data . reduce ( ( acc , raw , idx ) => {
196
140
if ( ! raw ) return acc
197
141
let sess = this . serializer . parse ( raw ) as any
@@ -205,13 +149,13 @@ export class RedisStore extends Store {
205
149
}
206
150
}
207
151
208
- private _getTTL ( sess : SessionData ) {
152
+ private getTTL ( sess : SessionData ) {
209
153
if ( typeof this . ttl === "function" ) {
210
154
return this . ttl ( sess )
211
155
}
212
156
213
157
let ttl
214
- if ( sess && sess . cookie && sess . cookie . expires ) {
158
+ if ( sess ? .cookie ? .expires ) {
215
159
let ms = Number ( new Date ( sess . cookie . expires ) ) - Date . now ( )
216
160
ttl = Math . ceil ( ms / 1000 )
217
161
} else {
@@ -220,12 +164,34 @@ export class RedisStore extends Store {
220
164
return ttl
221
165
}
222
166
223
- private async _getAllKeys ( ) {
167
+ private async getAllKeys ( ) {
224
168
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 } )
228
183
}
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
+ } ) ( )
230
196
}
231
197
}
0 commit comments