mm_session 1.5.1 → 1.5.3

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/lib/store.js CHANGED
@@ -1,7 +1,7 @@
1
- const { CacheBase } = require('mm_cachebase');
1
+ const { Cache } = require('mm_cache');
2
2
 
3
3
  if (!$.cache) {
4
- $.cache = new CacheBase();
4
+ $.cache = new Cache();
5
5
  }
6
6
 
7
7
  /**
@@ -21,34 +21,17 @@ class Store {
21
21
  }
22
22
  }
23
23
 
24
- /**
25
- * 获取session ID
26
- * @param {object} ctx HTTP上下文
27
- * @returns {string} session ID
28
- */
29
- Store.prototype.getID = async function(ctx) {
30
- var header = ctx.request.header;
31
- var user_agent = header['user-agent'];
32
- if (!user_agent) {
33
- user_agent = 'mm';
34
- }
35
- var start_hash = user_agent.md5().substring(0, 32);
36
- var time_stamp = Date.parse(new Date()) / 1000;
37
- var uuid = (ctx.ip + '_' + time_stamp).aes_encode(start_hash);
38
- return uuid;
39
- };
40
-
41
24
  /**
42
25
  * 获取session缓存
43
26
  * @param {string} uuid 客户端唯一ID
44
27
  * @returns {object|null} session对象或null
45
28
  */
46
- Store.prototype.get = async function(uuid) {
29
+ Store.prototype.get = async function (uuid) {
47
30
  if (!$.cache.has(this.key + uuid)) return undefined;
48
31
  // 我们正在解码数据来自我们的srote, 我们假定它是在储存之前
49
32
  var val = await $.cache.get(this.key + uuid);
50
33
  if (val) {
51
- if (typeof(val) == 'string') {
34
+ if (typeof (val) == 'string') {
52
35
  return JSON.parse(val);
53
36
  }
54
37
  return val;
@@ -59,44 +42,32 @@ Store.prototype.get = async function(uuid) {
59
42
 
60
43
  /**
61
44
  * 设置session到缓存
45
+ * @param {string} uuid session ID
62
46
  * @param {object} session session对象
63
- * @param {object} options 配置选项
64
- * @param {string} options.uuid 客户端唯一表示ID
65
- * @param {number} options.max_age 最大寿命
66
- * @param {object} ctx HTTP请求上下文
47
+ * @param {number} max_age 最大寿命(秒)
67
48
  * @returns {string} session ID
68
49
  */
69
- Store.prototype.set = async function(session, {
70
- uuid,
71
- max_age
72
- } = {}, ctx) {
73
- let session_uuid = uuid;
74
- let session_max_age = max_age;
75
-
76
- if (!session_uuid) {
77
- session_uuid = await this.getID(ctx);
78
- }
79
- if (!session_max_age) {
80
- session_max_age = 7200;
81
- }
82
-
50
+ Store.prototype.set = async function (uuid, session, max_age = 7200) {
83
51
  let session_str = session;
84
52
  try {
85
- if (typeof(session_str) == 'object') {
53
+ console.log(' - Store.set接收到的session对象:', session);
54
+ if (typeof (session_str) == 'object') {
86
55
  session_str = JSON.stringify(session_str);
56
+ console.log(' - JSON序列化后的session:', session_str);
87
57
  }
88
- await $.cache.set(this.key + session_uuid, session_str, session_max_age);
58
+
59
+ await $.cache.set(this.key + uuid, session_str, max_age);
89
60
  } catch (err) {
90
61
  $.log.debug('Set session error:', err);
91
62
  }
92
- return session_uuid;
63
+ return uuid;
93
64
  };
94
65
 
95
66
  /**
96
- * 销毁缓存
67
+ * 删除缓存
97
68
  * @param {string} uuid 客户端唯一ID
98
69
  */
99
- Store.prototype.destroy = function(uuid) {
70
+ Store.prototype.del = function (uuid) {
100
71
  $.cache.del(this.key + uuid);
101
72
  };
102
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm_session",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "这是超级美眉session函数模块,用于web服务端session缓存",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -29,12 +29,11 @@
29
29
  "url": "https://gitee.com/qiuwenwu91/mm_session/issues"
30
30
  },
31
31
  "homepage": "https://gitee.com/qiuwenwu91/mm_session#readme",
32
- "dependencies": {
33
- "mm_cachebase": "^1.9.2"
34
- },
35
32
  "devDependencies": {
36
33
  "eslint-plugin-jsdoc": "^61.5.0",
37
- "eslint-plugin-naming-convention": "^0.1.3",
38
- "mm_eslint": "^1.0.3"
34
+ "mm_eslint": "^1.0.8"
35
+ },
36
+ "dependencies": {
37
+ "mm_cache": "^1.4.7"
39
38
  }
40
39
  }
package/test.js CHANGED
@@ -1,190 +1,328 @@
1
- const { Session, Store } = require('./index.js');
1
+ 'use strict';
2
2
 
3
- String.prototype.aes_encode = function(key) {
4
- // 简单的AES编码模拟
5
- return Buffer.from(this).toString('base64') + '_' + key.substring(0, 8);
6
- };
3
+ const { session, Session, Store } = require('./index.js');
7
4
 
8
- // 模拟Koa应用
9
- class MockKoaApp {
10
- constructor() {
11
- this.middlewares = [];
12
- }
13
-
14
- use(middleware) {
15
- this.middlewares.push(middleware);
16
- }
17
-
18
- async handleRequest(ctx) {
19
- // 执行中间件链
20
- const executeMiddleware = async (index) => {
21
- if (index < this.middlewares.length) {
22
- const middleware = this.middlewares[index];
23
- await middleware(ctx, () => executeMiddleware(index + 1));
24
- }
25
- };
26
-
27
- await executeMiddleware(0);
28
- }
29
- }
30
-
31
- // 模拟Koa的上下文对象
5
+ /**
6
+ * 模拟Koa上下文对象
7
+ * @class MockContext
8
+ * @param {object} headers HTTP请求头
9
+ * @param {object} cookies cookie数据
10
+ */
32
11
  class MockContext {
33
- constructor() {
34
- this.cookies = new MockCookies();
35
- this.headers = {};
12
+ /**
13
+ * 构造函数
14
+ * @param {object} headers HTTP请求头
15
+ * @param {object} cookies cookie数据
16
+ */
17
+ constructor(headers = {}, cookies = {}) {
18
+ this.headers = headers;
19
+ this._cookies = cookies;
36
20
  this.ip = '127.0.0.1';
37
- this.request = {
38
- header: {
39
- 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
21
+ this.body = null;
22
+ this.status = null;
23
+
24
+ // 模拟cookies对象
25
+ this.cookies = {
26
+ get: (name) => {
27
+ return this._cookies[name] || null;
28
+ },
29
+ set: (name, value, options = {}) => {
30
+ this._cookies[name] = value;
40
31
  }
41
32
  };
42
- this.session = null;
43
33
  }
44
34
  }
45
35
 
46
- // 模拟Koa的cookies对象
47
- class MockCookies {
48
- constructor() {
49
- this._cookies = new Map();
50
- }
36
+ /**
37
+ * 模拟Koa next函数
38
+ * @returns {Promise<void>} 空Promise
39
+ */
40
+ const mock_next = () => Promise.resolve();
51
41
 
52
- get(key, options) {
53
- return this._cookies.get(key);
54
- }
42
+ /**
43
+ * 测试基本session中间件功能
44
+ * @returns {Promise<void>} 测试结果
45
+ */
46
+ async function testBasicSession() {
47
+ console.log('1. 测试基本session中间件功能');
48
+ try {
49
+ const session_mw = session();
50
+ const ctx = new MockContext();
51
+
52
+ await session_mw(ctx, mock_next);
55
53
 
56
- set(key, value, options) {
57
- if (value === null) {
58
- this._cookies.delete(key);
54
+ if (ctx.session) {
55
+ console.log('✓ session对象创建成功');
56
+ console.log('✓ session ID:', ctx.session.getId());
59
57
  } else {
60
- this._cookies.set(key, value);
58
+ console.log('✗ session对象创建失败');
61
59
  }
60
+ } catch (err) {
61
+ console.log('✗ 测试失败:', err.message);
62
62
  }
63
63
  }
64
64
 
65
- // 测试session模块
66
- async function testSession() {
67
- console.log('开始测试session模块...\n');
68
-
65
+ /**
66
+ * 测试session数据读写功能
67
+ * @returns {Promise<void>} 测试结果
68
+ */
69
+ async function testSessionDataRW() {
70
+ console.log('\n2. 测试session数据读写功能');
69
71
  try {
70
- // 测试1: 创建Session实例
71
- console.log('测试1: 创建Session实例');
72
- const session = new Session({
73
- key: 'test_session',
74
- max_age: 3600
72
+ const session_mw = session();
73
+ const ctx = new MockContext();
74
+
75
+ await session_mw(ctx, async () => {
76
+ // 在中间件中设置session数据
77
+ ctx.session.user_id = 123;
78
+ ctx.session.username = 'testuser';
79
+ ctx.session.is_logged_in = true;
75
80
  });
76
- console.log('✅ Session实例创建成功\n');
77
81
 
78
- // 测试2: 创建Koa应用并注册中间件
79
- console.log('测试2: 创建Koa应用并注册中间件');
80
- const app = new MockKoaApp();
81
-
82
- // 使用Koa标准方式注册session中间件
83
- app.use(session.middleware());
84
-
85
- // 添加一个业务中间件来测试session功能
86
- app.use(async (ctx, next) => {
87
- // 测试session数据操作
88
- if (!ctx.session.user_id) {
89
- ctx.session.user_id = 123;
90
- ctx.session.username = 'test_user';
91
- }
92
-
93
- await next();
82
+ console.log('✓ session数据设置成功');
83
+ console.log(' - user_id:', ctx.session.user_id);
84
+ console.log(' - username:', ctx.session.username);
85
+ console.log(' - is_logged_in:', ctx.session.is_logged_in);
86
+ } catch (err) {
87
+ console.log('✗ 测试失败:', err.message);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * 创建测试session
93
+ * @param {Function} session_mw session中间件
94
+ * @returns {Promise<object>} 包含session ID和上下文的对象
95
+ */
96
+ async function createTestSession(session_mw) {
97
+ const ctx1 = new MockContext();
98
+
99
+ // 使用标准的中间件调用方式,确保保存逻辑被执行
100
+ await session_mw(ctx1, async () => {
101
+ // 在next()函数中设置session数据
102
+ ctx1.session.test_data = 'persistent_data';
103
+ // 强制标记session为已修改状态
104
+ ctx1.session._modified = true;
105
+
106
+ // 调试:检查session对象在设置数据后的状态
107
+ console.log(' - 第一次请求session对象状态:', {
108
+ uuid: ctx1.session.uuid,
109
+ test_data: ctx1.session.test_data,
110
+ _modified: ctx1.session._modified,
111
+ _is_new: ctx1.session._is_new
94
112
  });
95
113
 
96
- console.log('✅ Koa应用创建成功,中间件注册完成\n');
114
+ // 调试:检查session是否被标记为已修改
115
+ console.log(' - session._modified标志:', ctx1.session._modified);
116
+ });
97
117
 
98
- // 测试3: 测试新session创建
99
- console.log('测试3: 测试新session创建');
100
- const ctx1 = new MockContext();
101
-
102
- await app.handleRequest(ctx1);
103
-
104
- // 等待session保存完成(模拟异步保存过程)
105
- await new Promise(resolve => setTimeout(resolve, 50));
106
-
107
- if (ctx1.session) {
108
- console.log('✅ session创建成功');
109
- console.log(' session对象:', typeof ctx1.session);
110
- console.log(' user_id:', ctx1.session.user_id);
111
- console.log(' username:', ctx1.session.username);
112
- } else {
113
- throw new Error('session创建失败');
118
+ // 调试:检查第一次请求后session是否被正确保存
119
+ console.log(' - 第一次请求后检查session._modified标志:', ctx1.session._modified);
120
+
121
+ const session_id = ctx1.session.getId();
122
+ console.log('✓ 第一次请求session ID:', session_id);
123
+
124
+ return { ctx1, session_id };
125
+ }
126
+
127
+ /**
128
+ * 验证session恢复结果
129
+ * @param {object} ctx2 第二次请求的上下文
130
+ * @param {string} session_id session ID
131
+ * @returns {Promise<boolean>} 恢复是否成功
132
+ */
133
+ async function verifySessionRecover(ctx2, session_id) {
134
+ if (ctx2.session && ctx2.session.test_data === 'persistent_data') {
135
+ console.log('✓ session数据恢复成功');
136
+ return true;
137
+ } else {
138
+ console.log('✗ session数据恢复失败');
139
+ console.log(' - ctx2.session:', ctx2.session);
140
+ if (ctx2.session) {
141
+ console.log(' - test_data:', ctx2.session.test_data);
142
+ console.log(' - session ID是否匹配:', ctx2.session.uuid === session_id);
143
+
144
+ // 调试:直接检查存储中是否有数据
145
+ const store = new Store();
146
+ const stored_data = await store.get(session_id);
147
+ console.log(' - 存储中的数据:', stored_data);
148
+
149
+ // 调试:检查第一次请求后存储中的数据
150
+ const stored_data_first = await store.get(session_id);
151
+ console.log(' - 第一次请求后存储中的数据:', stored_data_first);
114
152
  }
153
+ return false;
154
+ }
155
+ }
115
156
 
116
- // 测试4: 测试session保存和读取
117
- console.log('\n测试4: 测试session保存和读取');
157
+ /**
158
+ * 测试从cookie中恢复session
159
+ * @returns {Promise<void>} 测试结果
160
+ */
161
+ async function testSessionRecovery() {
162
+ console.log('\n3. 测试从cookie中恢复session');
163
+ try {
164
+ const session_mw = session();
118
165
 
119
- // 检查cookie是否设置
120
- const sessionCookie = ctx1.cookies.get('test_session');
121
- if (sessionCookie) {
122
- console.log('✅ session保存成功,cookie已设置');
123
- console.log(' session ID:', sessionCookie);
124
- } else {
125
- console.log('⚠️ session保存但cookie未设置');
126
- }
166
+ // 创建测试session
167
+ const { session_id } = await createTestSession(session_mw);
127
168
 
128
- // 测试5: 测试session读取(模拟有cookie的情况)
129
- console.log('\n测试5: 测试session读取');
169
+ // 第二次请求使用相同的session ID(通过cookie传递)
130
170
  const ctx2 = new MockContext();
131
-
132
- // 设置cookie模拟已有session
133
- if (sessionCookie) {
134
- ctx2.cookies.set('test_session', sessionCookie);
135
- }
136
-
137
- await app.handleRequest(ctx2);
138
-
139
- if (ctx2.session) {
140
- console.log('✅ session读取成功');
141
- console.log(' session对象存在');
142
- console.log(' user_id:', ctx2.session.user_id);
143
- console.log(' username:', ctx2.session.username);
171
+ ctx2.cookies['mm:uuid'] = session_id;
172
+
173
+ await session_mw(ctx2, mock_next);
174
+
175
+ // 验证恢复结果
176
+ await verifySessionRecover(ctx2, session_id);
177
+ } catch (err) {
178
+ console.log('✗ 测试失败:', err.message);
179
+ }
180
+ }
181
+
182
+ /**
183
+ * 测试session刷新功能
184
+ * @returns {Promise<void>} 测试结果
185
+ */
186
+ async function testSessionRefresh() {
187
+ console.log('\n4. 测试session刷新功能');
188
+ try {
189
+ const session_mw = session();
190
+ const ctx = new MockContext();
191
+
192
+ await session_mw(ctx, async () => {
193
+ ctx.session.original_data = 'original';
194
+ ctx.session.refresh(); // 刷新session
195
+ });
196
+
197
+ console.log('✓ session刷新功能正常');
198
+ } catch (err) {
199
+ console.log('✗ 测试失败:', err.message);
200
+ }
201
+ }
202
+
203
+ /**
204
+ * 测试自定义配置
205
+ * @returns {Promise<void>} 测试结果
206
+ */
207
+ async function testCustomConfig() {
208
+ console.log('\n5. 测试自定义配置');
209
+ try {
210
+ const custom_cfg = {
211
+ key_prefix: 'custom:session',
212
+ cookie_token: 'custom_token',
213
+ max_age: 3600 // 1小时
214
+ };
215
+
216
+ const session_mw = session(custom_cfg);
217
+ const ctx = new MockContext();
218
+
219
+ await session_mw(ctx, mock_next);
220
+
221
+ console.log('✓ 自定义配置生效');
222
+ console.log(' - session ID长度:', ctx.session.getId().length);
223
+ } catch (err) {
224
+ console.log('✗ 测试失败:', err.message);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * 测试Session类直接使用
230
+ * @returns {Promise<void>} 测试结果
231
+ */
232
+ async function testSessionClass() {
233
+ console.log('\n6. 测试Session类直接使用');
234
+ try {
235
+ const sess = new Session();
236
+ const ctx = new MockContext();
237
+
238
+ const session_obj = await sess.get(ctx);
239
+ console.log('✓ Session类get方法正常');
240
+ console.log(' - 获取的session对象:', session_obj);
241
+
242
+ // 测试保存session
243
+ await sess.save(ctx, session_obj);
244
+ console.log('✓ Session类save方法正常');
245
+
246
+ // 测试生成session ID
247
+ const new_sess_id = sess.genId(ctx);
248
+ console.log('✓ Session类genId方法正常');
249
+ console.log(' - 生成的session ID:', new_sess_id);
250
+ } catch (err) {
251
+ console.log('✗ 测试失败:', err.message);
252
+ }
253
+ }
254
+
255
+ /**
256
+ * 测试Store类
257
+ * @returns {Promise<void>} 测试结果
258
+ */
259
+ async function testStoreClass() {
260
+ console.log('\n7. 测试Store类');
261
+ try {
262
+ const store = new Store('test_prefix');
263
+ const test_data = { key: 'value', number: 42 };
264
+
265
+ // 测试设置数据
266
+ await store.set('test_key', test_data, 60);
267
+ console.log('✓ Store类set方法正常');
268
+
269
+ // 测试获取数据
270
+ const ret_data = await store.get('test_key');
271
+ if (ret_data && ret_data.key === 'value') {
272
+ console.log('✓ Store类get方法正常');
273
+ console.log(' - 获取的数据:', ret_data);
144
274
  } else {
145
- console.log('⚠️ session读取失败(可能是新session)');
275
+ console.log('✗ Store类get方法异常');
146
276
  }
147
277
 
148
- // 测试6: 测试Store类
149
- console.log('\n测试6: 测试Store类');
150
- const store = new Store('test');
151
-
152
- // 模拟获取session ID
153
- const sessionId = await store.getID(ctx1);
154
- console.log('✅ Store类测试成功');
155
- console.log(' 生成的session ID:', sessionId);
156
-
157
- // 测试7: 测试session销毁
158
- console.log('\n测试7: 测试session销毁');
159
- const ctx3 = new MockContext();
160
-
161
- // 添加一个中间件来测试session销毁
162
- const destroyApp = new MockKoaApp();
163
- destroyApp.use(session.middleware());
164
- destroyApp.use(async (ctx, next) => {
165
- // 设置session为null触发销毁
166
- ctx.session = null;
167
- await next();
168
- });
169
-
170
- await destroyApp.handleRequest(ctx3);
171
- console.log('✅ session销毁逻辑测试完成');
278
+ // 测试删除数据
279
+ store.del('test_key');
280
+ console.log('✓ Store类del方法正常');
281
+ } catch (err) {
282
+ console.log('✗ 测试失败:', err.message);
283
+ }
284
+ }
172
285
 
173
- console.log('\n🎉 所有测试通过!session模块功能正常。');
286
+ /**
287
+ * 测试session中间件
288
+ */
289
+ async function testSession() {
290
+ console.log('=== 开始测试session中间件 ===\n');
174
291
 
175
- } catch (error) {
176
- console.error('❌ 测试失败:', error.message);
177
- console.error(error.stack);
292
+ await testBasicSession();
293
+ await testSessionDataRW();
294
+ await testSessionRecovery();
295
+ await testSessionRefresh();
296
+ await testCustomConfig();
297
+ await testSessionClass();
298
+ await testStoreClass();
299
+
300
+ console.log('\n=== session中间件测试完成 ===');
301
+ }
302
+
303
+ /**
304
+ * 运行测试
305
+ */
306
+ async function runTests() {
307
+ try {
308
+ await testSession();
309
+ } catch (err) {
310
+ console.error('测试执行出错:', err);
178
311
  }
179
312
  }
180
313
 
181
- // 运行测试
314
+ // 如果直接运行此文件,则执行测试
182
315
  if (require.main === module) {
183
- testSession().catch(console.error);
316
+ runTests();
184
317
  }
185
318
 
319
+ setTimeout(() => {
320
+ process.exit(0);
321
+ }, 5000);
322
+
186
323
  module.exports = {
187
324
  MockContext,
188
- MockCookies,
189
- testSession
325
+ mock_next,
326
+ testSession,
327
+ runTests
190
328
  };