@randajan/koa-io-session 1.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -129
- package/dist/cjs/index.cjs +360 -199
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/esm/index.mjs +358 -197
- package/dist/esm/index.mjs.map +4 -4
- package/package.json +3 -4
package/dist/esm/index.mjs
CHANGED
|
@@ -1,111 +1,116 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/class/SessionBridge.js
|
|
2
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
2
3
|
import { ServerResponse } from "http";
|
|
3
|
-
import
|
|
4
|
-
import { solid as solid2 } from "@randajan/props";
|
|
4
|
+
import { solid as solid2, virtual } from "@randajan/props";
|
|
5
5
|
|
|
6
6
|
// src/tools.js
|
|
7
7
|
import crypto from "crypto";
|
|
8
8
|
var generateUid = (len = 16) => crypto.randomBytes(len).toString("base64url").slice(0, len);
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
var formatState = (session2, maxAge, prevTTL, defaultTTL) => {
|
|
23
|
-
const ttl = maxAge ?? prevTTL ?? defaultTTL;
|
|
24
|
-
const expiresAt = Date.now() + ttl;
|
|
25
|
-
return { session: session2, expiresAt, ttl };
|
|
9
|
+
var is = (type, any) => typeof any === type;
|
|
10
|
+
var valid = (type, any, req = false, msg = "argument") => {
|
|
11
|
+
if (any == null) {
|
|
12
|
+
if (!req) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
throw new Error(`${msg} require typeof '${type}'`);
|
|
16
|
+
}
|
|
17
|
+
if (is(type, any)) {
|
|
18
|
+
return any;
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`${msg} is not typeof '${type}'`);
|
|
26
21
|
};
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
solid(this, "event", new EventEmitter(eventEmitterOpt));
|
|
22
|
+
var validRange = (min, max, any, req = false, msg = "argument") => {
|
|
23
|
+
const num = valid("number", any, req, msg);
|
|
24
|
+
if (num == null) {
|
|
25
|
+
return;
|
|
32
26
|
}
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
if (num < min) {
|
|
28
|
+
throw new Error(`${msg} must be greater than ${min}`);
|
|
35
29
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (!d) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (Date.now() < d.expiresAt) {
|
|
42
|
-
return d.session;
|
|
43
|
-
}
|
|
44
|
-
this.delete(sid);
|
|
30
|
+
if (num > max) {
|
|
31
|
+
throw new Error(`${msg} must be less than ${max}`);
|
|
45
32
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return
|
|
33
|
+
return num;
|
|
34
|
+
};
|
|
35
|
+
var validInterval = (any, req = false, msg = "argument") => {
|
|
36
|
+
return validRange(10, 2147483647, any, req, msg);
|
|
37
|
+
};
|
|
38
|
+
var validObject = (any, req = false, msg = "argument") => {
|
|
39
|
+
const obj = valid("object", any, req, msg);
|
|
40
|
+
if (obj == null) {
|
|
41
|
+
return;
|
|
55
42
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const d = super.get(sid);
|
|
59
|
-
if (session2 == null) {
|
|
60
|
-
return !d || this.destroy(sid);
|
|
61
|
-
}
|
|
62
|
-
super.set(sid, formatState(session2, maxAge, d?.ttl, defaultTTL));
|
|
63
|
-
this.event.emit("set", this, sid, !d);
|
|
64
|
-
return true;
|
|
43
|
+
if (!Array.isArray(obj)) {
|
|
44
|
+
return obj;
|
|
65
45
|
}
|
|
66
|
-
|
|
67
|
-
|
|
46
|
+
throw new Error(`${msg} must be object, not array`);
|
|
47
|
+
};
|
|
48
|
+
var validStore = (store) => {
|
|
49
|
+
const missing = [];
|
|
50
|
+
if (!is("function", store?.get)) {
|
|
51
|
+
missing.push("get()");
|
|
68
52
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
super.delete(sid);
|
|
72
|
-
this.event.emit("destroy", this, sid);
|
|
73
|
-
}
|
|
74
|
-
return true;
|
|
53
|
+
if (!is("function", store?.set)) {
|
|
54
|
+
missing.push("set()");
|
|
75
55
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
let cleared = 0;
|
|
79
|
-
for (const [sid, d] of this.entries()) {
|
|
80
|
-
if (now < d.expiresAt) {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (this.destroy(sid)) {
|
|
84
|
-
cleared++;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (cleared) {
|
|
88
|
-
this.event.emit("cleanup", this, cleared);
|
|
89
|
-
}
|
|
90
|
-
return cleared;
|
|
56
|
+
if (!is("function", store?.destroy)) {
|
|
57
|
+
missing.push("destroy()");
|
|
91
58
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (!interval) {
|
|
95
|
-
interval = defaultTTL / 10;
|
|
96
|
-
}
|
|
97
|
-
const tid = setInterval(() => {
|
|
98
|
-
this.cleanup();
|
|
99
|
-
}, interval);
|
|
100
|
-
return (_) => clearInterval(tid);
|
|
59
|
+
if (!is("function", store?.on)) {
|
|
60
|
+
missing.push("on()");
|
|
101
61
|
}
|
|
62
|
+
if (missing.length) {
|
|
63
|
+
throw new TypeError(`store is missing required API: ${missing.join(", ")}`);
|
|
64
|
+
}
|
|
65
|
+
return store;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// src/httpSession.js
|
|
69
|
+
import session from "koa-session";
|
|
70
|
+
|
|
71
|
+
// src/wrappers.js
|
|
72
|
+
var wrapStore = (store) => {
|
|
73
|
+
return {
|
|
74
|
+
get: store.get.bind(store),
|
|
75
|
+
set: store.set.bind(store),
|
|
76
|
+
destroy: store.destroy.bind(store)
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
var wrapExternalKey = (opt, onSet) => {
|
|
80
|
+
const { externalKey: base, key, signed } = opt;
|
|
81
|
+
const get = base?.get ? (ctx) => base.get(ctx) : (ctx) => ctx.cookies.get(key, { signed });
|
|
82
|
+
const setRaw = base?.set ? (ctx, sid) => base.set(ctx, sid) : (ctx, sid) => ctx.cookies.set(key, sid, opt);
|
|
83
|
+
const set = typeof onSet != "function" ? setRaw : (ctx, sid) => {
|
|
84
|
+
setRaw(ctx, sid);
|
|
85
|
+
onSet(ctx, sid);
|
|
86
|
+
};
|
|
87
|
+
return { get, set };
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/httpSession.js
|
|
91
|
+
var createKoaSession = (opt, app, onSet) => {
|
|
92
|
+
const store = wrapStore(opt.store);
|
|
93
|
+
const externalKey = wrapExternalKey(opt, onSet);
|
|
94
|
+
const koaSession = session({ ...opt, store, externalKey }, app);
|
|
95
|
+
return [koaSession, externalKey];
|
|
96
|
+
};
|
|
97
|
+
var createClientCookie = (opt) => {
|
|
98
|
+
const { key, maxAge, signed, path, secure, sameSite, httpOnly } = opt;
|
|
99
|
+
return wrapExternalKey({
|
|
100
|
+
key,
|
|
101
|
+
signed,
|
|
102
|
+
maxAge,
|
|
103
|
+
path: path ?? "/",
|
|
104
|
+
secure,
|
|
105
|
+
sameSite,
|
|
106
|
+
httpOnly: httpOnly ?? true,
|
|
107
|
+
overwrite: true
|
|
108
|
+
});
|
|
102
109
|
};
|
|
103
110
|
|
|
104
111
|
// src/socketSession.js
|
|
105
112
|
import { solids } from "@randajan/props";
|
|
106
|
-
import { createQueue } from "@randajan/queue";
|
|
107
113
|
var sidLocks = /* @__PURE__ */ new Map();
|
|
108
|
-
var touchQueues = /* @__PURE__ */ new Map();
|
|
109
114
|
var createSessionCtx = (sessionId, session2, socket) => solids({ session: session2 }, { sessionId, socket });
|
|
110
115
|
var createSessionHash = (session2) => {
|
|
111
116
|
try {
|
|
@@ -141,155 +146,311 @@ var withLock = async (task, socket, ...args) => {
|
|
|
141
146
|
}
|
|
142
147
|
}
|
|
143
148
|
};
|
|
144
|
-
var
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return existing;
|
|
148
|
-
}
|
|
149
|
-
const queue = createQueue(async (touchMaxAge) => {
|
|
150
|
-
try {
|
|
151
|
-
await store.touch(sid, touchMaxAge);
|
|
152
|
-
} catch {
|
|
153
|
-
}
|
|
154
|
-
if (!queue.isPending) {
|
|
155
|
-
touchQueues.delete(sid);
|
|
156
|
-
}
|
|
157
|
-
}, { pass: "last", softMs: 1e3, hardMs: 5e3 });
|
|
158
|
-
touchQueues.set(sid, queue);
|
|
159
|
-
return queue;
|
|
160
|
-
};
|
|
161
|
-
var scheduleTouch = (sid, store, maxAge) => {
|
|
162
|
-
if (typeof store?.touch !== "function") {
|
|
163
|
-
return;
|
|
149
|
+
var applyOnMissing = (onMissing) => {
|
|
150
|
+
if (onMissing instanceof Error) {
|
|
151
|
+
throw onMissing;
|
|
164
152
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const queue = touchQueues.get(sid);
|
|
170
|
-
if (!queue) {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
queue.flush();
|
|
174
|
-
touchQueues.delete(sid);
|
|
175
|
-
return true;
|
|
153
|
+
if (is("function", onMissing)) {
|
|
154
|
+
return onMissing();
|
|
155
|
+
}
|
|
156
|
+
return onMissing;
|
|
176
157
|
};
|
|
177
|
-
var runSessionHandler = async (socket, handler,
|
|
158
|
+
var runSessionHandler = async (socket, handler, store, onMissing) => {
|
|
178
159
|
const sid = socket.sessionId;
|
|
179
|
-
const { store, maxAge } = opt;
|
|
180
160
|
const current = await store.get(sid);
|
|
181
|
-
if (!
|
|
182
|
-
|
|
161
|
+
if (!current) {
|
|
162
|
+
return applyOnMissing(onMissing);
|
|
183
163
|
}
|
|
184
164
|
const session2 = current;
|
|
185
165
|
const sessionCtx = createSessionCtx(sid, session2, socket);
|
|
186
166
|
const originalHash = createSessionHash(sessionCtx.session);
|
|
187
167
|
const result = await handler(sessionCtx, socket);
|
|
188
168
|
if (sessionCtx.session == null) {
|
|
189
|
-
clearTouchQueue(sid);
|
|
190
169
|
await store.destroy(sid);
|
|
191
170
|
return result;
|
|
192
171
|
}
|
|
193
|
-
|
|
194
|
-
throw new TypeError("sessionCtx.session must be an object or null");
|
|
195
|
-
}
|
|
172
|
+
sessionCtx.session = validObject(sessionCtx.session, false, "session");
|
|
196
173
|
if (isSessionHashChanged(originalHash, sessionCtx.session)) {
|
|
197
|
-
|
|
198
|
-
await store.set(sid, sessionCtx.session, maxAge);
|
|
199
|
-
} else {
|
|
200
|
-
scheduleTouch(sid, store, maxAge);
|
|
174
|
+
await store.set(sid, sessionCtx.session);
|
|
201
175
|
}
|
|
202
176
|
return result;
|
|
203
177
|
};
|
|
204
|
-
var applySessionHandler = async (socket, handler,
|
|
178
|
+
var applySessionHandler = async (socket, handler, store, onMissing) => {
|
|
205
179
|
if (typeof handler !== "function") {
|
|
206
180
|
throw new TypeError("socket.withSession(handler) requires a function");
|
|
207
181
|
}
|
|
208
182
|
if (!socket.sessionId) {
|
|
209
|
-
|
|
183
|
+
return applyOnMissing(onMissing);
|
|
210
184
|
}
|
|
211
|
-
return withLock(runSessionHandler, socket, handler,
|
|
185
|
+
return withLock(runSessionHandler, socket, handler, store, onMissing);
|
|
212
186
|
};
|
|
213
187
|
|
|
214
|
-
// src/
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
188
|
+
// src/class/SessionStore.js
|
|
189
|
+
import { solid } from "@randajan/props";
|
|
190
|
+
import { EventEmitter } from "events";
|
|
191
|
+
|
|
192
|
+
// src/const.js
|
|
193
|
+
var ms = {
|
|
194
|
+
s: (v = 1) => v * 1e3,
|
|
195
|
+
m: (v = 1) => ms.s(v * 60),
|
|
196
|
+
h: (v = 1) => ms.m(v * 60),
|
|
197
|
+
d: (v = 1) => ms.h(v * 24),
|
|
198
|
+
w: (v = 1) => ms.d(v * 7),
|
|
199
|
+
M: (v = 1) => ms.d(v * 30),
|
|
200
|
+
y: (v = 1) => ms.d(v * 365)
|
|
201
|
+
};
|
|
202
|
+
var _customOptKeys = /* @__PURE__ */ new Set([
|
|
203
|
+
"store",
|
|
204
|
+
"autoCleanup",
|
|
205
|
+
"autoCleanupMs",
|
|
206
|
+
"clientKey",
|
|
207
|
+
"clientMaxAge",
|
|
208
|
+
"clientAlwaysRoll"
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
// src/class/SessionStore.js
|
|
212
|
+
var formatState = (session2, maxAge, prevTTL, maxAgeDefault) => {
|
|
213
|
+
const ttl = maxAge ?? prevTTL ?? maxAgeDefault;
|
|
214
|
+
const expiresAt = Date.now() + ttl;
|
|
215
|
+
return { session: session2, expiresAt, ttl };
|
|
216
|
+
};
|
|
217
|
+
var SessionStore = class extends Map {
|
|
218
|
+
constructor(opt = {}) {
|
|
219
|
+
super();
|
|
220
|
+
const maxAge = validRange(ms.s(), ms.y(), opt.maxAge, false, "maxAge") ?? ms.M();
|
|
221
|
+
const autoCleanup = valid("boolean", opt.autoCleanup, false, "autoCleanup") ?? true;
|
|
222
|
+
const autoCleanupMs = validInterval(opt.autoCleanupMs, false, "autoCleanupMs") ?? Math.max(ms.s(), Math.min(ms.h(), maxAge / 10));
|
|
223
|
+
solid(this, "maxAge", maxAge);
|
|
224
|
+
solid(this, "event", new EventEmitter());
|
|
225
|
+
if (!autoCleanup) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
setInterval((_) => this.cleanup(), autoCleanupMs);
|
|
219
229
|
}
|
|
220
|
-
|
|
221
|
-
|
|
230
|
+
on(eventName, callback) {
|
|
231
|
+
return this.event.on(eventName, callback);
|
|
222
232
|
}
|
|
223
|
-
|
|
224
|
-
|
|
233
|
+
get(sid) {
|
|
234
|
+
const d = super.get(sid);
|
|
235
|
+
if (!d) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (Date.now() < d.expiresAt) {
|
|
239
|
+
return d.session;
|
|
240
|
+
}
|
|
241
|
+
this.delete(sid);
|
|
225
242
|
}
|
|
226
|
-
|
|
227
|
-
|
|
243
|
+
set(sid, session2, maxAge) {
|
|
244
|
+
const d = super.get(sid);
|
|
245
|
+
if (session2 == null) {
|
|
246
|
+
return !d || this.destroy(sid);
|
|
247
|
+
}
|
|
248
|
+
super.set(sid, formatState(session2, maxAge, d?.ttl, this.maxAge));
|
|
249
|
+
this.event.emit("set", this, sid, !d);
|
|
250
|
+
return true;
|
|
228
251
|
}
|
|
229
|
-
|
|
230
|
-
|
|
252
|
+
delete(sid) {
|
|
253
|
+
return this.destroy(sid);
|
|
231
254
|
}
|
|
232
|
-
|
|
233
|
-
|
|
255
|
+
destroy(sid) {
|
|
256
|
+
if (this.has(sid)) {
|
|
257
|
+
super.delete(sid);
|
|
258
|
+
this.event.emit("destroy", this, sid);
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
cleanup() {
|
|
263
|
+
const now = Date.now();
|
|
264
|
+
let cleared = 0;
|
|
265
|
+
for (const [sid, d] of this.entries()) {
|
|
266
|
+
if (now < d.expiresAt) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (this.destroy(sid)) {
|
|
270
|
+
cleared++;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (cleared) {
|
|
274
|
+
this.event.emit("cleanup", this, cleared);
|
|
275
|
+
}
|
|
276
|
+
return cleared;
|
|
234
277
|
}
|
|
235
278
|
};
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
279
|
+
|
|
280
|
+
// src/formatOptions.js
|
|
281
|
+
var pickKoaOpt = (rawOpt) => {
|
|
282
|
+
const koaOpt = {};
|
|
283
|
+
for (const key in rawOpt) {
|
|
284
|
+
if (_customOptKeys.has(key)) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
koaOpt[key] = rawOpt[key];
|
|
288
|
+
}
|
|
289
|
+
koaOpt.key = valid("string", koaOpt.key, false, "key") ?? generateUid(12);
|
|
290
|
+
koaOpt.maxAge = validRange(ms.s(), ms.y(), koaOpt.maxAge, false, "maxAge") ?? ms.M();
|
|
291
|
+
koaOpt.signed = valid("boolean", koaOpt.signed, false, "signed") ?? true;
|
|
292
|
+
koaOpt.store = validStore(rawOpt.store || new SessionStore(rawOpt));
|
|
293
|
+
return koaOpt;
|
|
294
|
+
};
|
|
295
|
+
var formatOptions = (opt = {}) => {
|
|
296
|
+
opt = validObject(opt, true, "options");
|
|
297
|
+
const koaOpt = pickKoaOpt(opt);
|
|
298
|
+
const clientKey = valid("string", opt.clientKey) ?? `${koaOpt.key}.cid`;
|
|
299
|
+
const clientMaxAge = validInterval(opt.clientMaxAge, false, "clientMaxAge") ?? ms.y();
|
|
300
|
+
const clientAlwaysRoll = valid("boolean", opt.clientAlwaysRoll, false, "clientAlwaysRoll") ?? true;
|
|
301
|
+
const clientOpt = { ...koaOpt, key: clientKey, maxAge: clientMaxAge };
|
|
302
|
+
return {
|
|
303
|
+
koaOpt,
|
|
304
|
+
clientOpt,
|
|
305
|
+
clientAlwaysRoll
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// src/class/Bridge.js
|
|
310
|
+
import { solids as solids2 } from "@randajan/props";
|
|
311
|
+
var Bridge = class {
|
|
312
|
+
constructor(opt = {}) {
|
|
313
|
+
const { onSet, onDelete } = opt;
|
|
314
|
+
solids2(this, {
|
|
315
|
+
onSet,
|
|
316
|
+
onDelete,
|
|
317
|
+
s2c: /* @__PURE__ */ new Map(),
|
|
318
|
+
c2s: /* @__PURE__ */ new Map()
|
|
263
319
|
});
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
await next();
|
|
269
|
-
});
|
|
270
|
-
io.on("connection", (socket) => {
|
|
271
|
-
const sid = socket.sessionId;
|
|
272
|
-
if (sid) {
|
|
273
|
-
socket.join(`sessionId:${sid}`);
|
|
320
|
+
}
|
|
321
|
+
set(cid, sid) {
|
|
322
|
+
if (!cid || !sid) {
|
|
323
|
+
return false;
|
|
274
324
|
}
|
|
275
|
-
|
|
276
|
-
|
|
325
|
+
const byCid = this.deleteByCid(cid, sid);
|
|
326
|
+
const bySid = this.deleteBySid(sid, cid);
|
|
327
|
+
if (!byCid && !bySid) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
this.c2s.set(cid, sid);
|
|
331
|
+
this.s2c.set(sid, cid);
|
|
332
|
+
this.onSet({ clientId: cid, sessionId: sid });
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
getByCid(cid) {
|
|
336
|
+
return this.c2s.get(cid);
|
|
337
|
+
}
|
|
338
|
+
getBySid(sid) {
|
|
339
|
+
return this.s2c.get(sid);
|
|
340
|
+
}
|
|
341
|
+
deleteBySid(sid, skipIf) {
|
|
342
|
+
const cid = this.getBySid(sid);
|
|
343
|
+
if (!cid) {
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
if (skipIf && cid == skipIf) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
this.s2c.delete(sid);
|
|
350
|
+
this.c2s.delete(cid);
|
|
351
|
+
this.onDelete({ clientId: cid, sessionId: sid });
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
deleteByCid(cid, skipIf) {
|
|
355
|
+
const sid = this.getByCid(cid);
|
|
277
356
|
if (!sid) {
|
|
278
|
-
return;
|
|
357
|
+
return true;
|
|
279
358
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
359
|
+
if (skipIf && sid == skipIf) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
this.c2s.delete(cid);
|
|
363
|
+
this.s2c.delete(sid);
|
|
364
|
+
this.onDelete({ clientId: cid, sessionId: sid });
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
// src/class/SessionBridge.js
|
|
370
|
+
var SessionBridge = class extends EventEmitter2 {
|
|
371
|
+
constructor(app, io, opt = {}) {
|
|
372
|
+
super();
|
|
373
|
+
if (!app.keys) {
|
|
374
|
+
app.keys = Array(6).fill().map(() => generateUid(12));
|
|
375
|
+
}
|
|
376
|
+
const o = formatOptions(opt);
|
|
377
|
+
const { store } = o.koaOpt;
|
|
378
|
+
const brg = new Bridge({
|
|
379
|
+
onSet: (pair) => this.emit("sessionStart", pair),
|
|
380
|
+
onDelete: (pair) => this.emit("sessionEnd", pair)
|
|
381
|
+
});
|
|
382
|
+
const cc = createClientCookie(o.clientOpt);
|
|
383
|
+
const [koaSession, sc] = createKoaSession(o.koaOpt, app, (ctx, sid) => {
|
|
384
|
+
const cid = cc.get(ctx);
|
|
385
|
+
brg.set(cid, sid);
|
|
386
|
+
});
|
|
387
|
+
const regenerateSid = async (ctx, cid, reqSid) => {
|
|
388
|
+
if (cid == null || reqSid == null) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const brgSid = brg.getByCid(cid);
|
|
392
|
+
if (brgSid == reqSid) {
|
|
393
|
+
return;
|
|
394
|
+
} else if (brgSid) {
|
|
395
|
+
sc.set(ctx, brgSid);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (brg.getBySid(reqSid)) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (!await store.get(reqSid)) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
brg.set(cid, reqSid);
|
|
405
|
+
};
|
|
406
|
+
app.use(koaSession);
|
|
407
|
+
app.use(async (ctx, next) => {
|
|
408
|
+
let cid = cc.get(ctx);
|
|
409
|
+
const sid = sc.get(ctx);
|
|
410
|
+
if (!cid) {
|
|
411
|
+
cc.set(ctx, cid = generateUid(24));
|
|
412
|
+
} else if (o.clientAlwaysRoll) {
|
|
413
|
+
cc.set(ctx, cid);
|
|
414
|
+
}
|
|
415
|
+
await regenerateSid(ctx, cid, sid);
|
|
416
|
+
solid2(ctx, "clientId", cid);
|
|
417
|
+
virtual(ctx, "sessionId", (_) => brg.getByCid(cid));
|
|
418
|
+
await next();
|
|
419
|
+
});
|
|
420
|
+
io.use(async (socket, next) => {
|
|
421
|
+
const req = socket.request;
|
|
422
|
+
const res = req.res ?? socket.response ?? new ServerResponse(req);
|
|
423
|
+
const ctx = app.createContext(req, res);
|
|
424
|
+
await koaSession(ctx, async () => {
|
|
425
|
+
});
|
|
426
|
+
const cid = cc.get(ctx);
|
|
427
|
+
const sid = sc.get(ctx);
|
|
428
|
+
await regenerateSid(ctx, cid, sid);
|
|
429
|
+
solid2(socket, "clientId", cid);
|
|
430
|
+
virtual(socket, "sessionId", (_) => brg.getByCid(cid));
|
|
431
|
+
solid2(socket, "withSession", async function(handler, onMissing) {
|
|
432
|
+
const onm = arguments.length > 1 ? onMissing : new Error("Session missing");
|
|
433
|
+
return applySessionHandler(socket, handler, store, onm);
|
|
434
|
+
}, false);
|
|
435
|
+
await next();
|
|
436
|
+
});
|
|
437
|
+
store.on("destroy", (_store, sid) => {
|
|
438
|
+
if (!sid) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
brg.deleteBySid(sid);
|
|
442
|
+
});
|
|
443
|
+
solid2(this, "store", store);
|
|
444
|
+
}
|
|
286
445
|
};
|
|
287
446
|
|
|
288
447
|
// src/index.js
|
|
289
|
-
var
|
|
448
|
+
var bridgeSession = (app, io, opt = {}) => new SessionBridge(app, io, opt);
|
|
449
|
+
var index_default = bridgeSession;
|
|
290
450
|
export {
|
|
451
|
+
SessionBridge,
|
|
291
452
|
SessionStore,
|
|
292
|
-
|
|
453
|
+
bridgeSession,
|
|
293
454
|
index_default as default,
|
|
294
455
|
generateUid
|
|
295
456
|
};
|