mm_api 1.1.3 → 1.1.5

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.
Files changed (2) hide show
  1. package/lib/api.js +100 -3
  2. package/package.json +4 -2
package/lib/api.js CHANGED
@@ -24,6 +24,9 @@ const Param = loadOptional("mm_param/lib/param.js"); // 是MM自带的参数机
24
24
  const mm_dao = loadOptional("mm_dao");
25
25
  const Dao = mm_dao ? mm_dao.Dao : null;
26
26
  const Sql = loadOptional("mm_sql/lib/sql.js"); // 是MM自带的sql机制,可以不使用
27
+ // 登录态存储:token 解析优先走 mm_state_store(用户状态)+ mm_dict(token→user_id 映射)
28
+ const mm_state_store = loadOptional("mm_state_store");
29
+ const mm_dict = loadOptional("mm_dict");
27
30
 
28
31
  /** param 实例缓存(模块级,替代原全局 param 模块列表) */
29
32
  const PARAM_CACHE = new Map();
@@ -322,7 +325,10 @@ Api.prototype._loadSql = function (file_path) {
322
325
  if (!cfg) {
323
326
  return null;
324
327
  }
325
- cfg.name = cfg.name || this.config.name;
328
+ // 实例名必须唯一:mm_mod 的 config 按类名+名称存入共享配置存储,
329
+ // 不同接口的 dao.json 可能同名(如多个 "cms_article"),直接沿用会互相覆盖,
330
+ // 因此强制以接口名命名 Dao 实例
331
+ cfg.name = this.config.name || cfg.name;
326
332
  cfg.config_file = p;
327
333
  cfg.dir = path.dirname(p);
328
334
  if (Dao) {
@@ -525,15 +531,37 @@ Api.prototype._getUserFromSession = function (ctx) {
525
531
  return null;
526
532
  };
527
533
 
534
+ /**
535
+ * 从上下文中提取 auth token(协议头优先,Cookie 兜底)
536
+ * @param {object} ctx 请求上下文
537
+ * @returns {string} token,无则返回空串
538
+ * @private
539
+ */
540
+ Api.prototype._getToken = function (ctx) {
541
+ var headers = (ctx && (ctx.headers || (ctx.request && ctx.request.headers))) || {};
542
+ var token = headers[this.config.token_key] || "";
543
+ if (!token && ctx) {
544
+ var cookies = ctx.cookies || (ctx.request && ctx.request.cookies);
545
+ if (cookies) {
546
+ token =
547
+ typeof cookies.get === "function"
548
+ ? cookies.get(this.config.token_key) || ""
549
+ : cookies[this.config.token_key] || "";
550
+ }
551
+ }
552
+ return String(token || "");
553
+ };
554
+
528
555
  /**
529
556
  * 从 token 中获取用户信息
557
+ * 解析顺序:缓存 → 用户状态存储(mm_dict 映射 / 解码 → mm_state_store)→ 数据库
530
558
  * @param {object} ctx 请求上下文
531
559
  * @param {object} db 数据管理器
532
560
  * @returns {object|null} 用户信息
533
561
  * @private
534
562
  */
535
563
  Api.prototype._getUserFromToken = async function (params, ctx, options) {
536
- var token = ctx.headers[this.config.token_key];
564
+ var token = this._getToken(ctx);
537
565
  if (!token) {
538
566
  return null;
539
567
  }
@@ -544,7 +572,76 @@ Api.prototype._getUserFromToken = async function (params, ctx, options) {
544
572
  return await this._processUserFromCache(u, ctx, options, token);
545
573
  }
546
574
  }
547
- return await this._getUserFromDb(token, ctx, options);
575
+ var o = await this._getUserFromState(token, ctx, options);
576
+ if (o) {
577
+ return o;
578
+ }
579
+ // 数据库兜底:仅当传入的是表模型管理器(有 new 方法)时才可查询
580
+ if (options && typeof options.new === "function") {
581
+ return await this._getUserFromDb(token, ctx, options);
582
+ }
583
+ return null;
584
+ };
585
+
586
+ /**
587
+ * 从用户状态存储获取用户信息
588
+ * token → user_id:mm_dict 映射,或 base64(JSON) 直接解码;
589
+ * user_id → 用户:mm_state_store 的 stateAdmin('user') 中 user_{id} 状态的 user 字段
590
+ * @param {string} token token
591
+ * @param {object} ctx 请求上下文
592
+ * @param {object} options 数据管理器
593
+ * @returns {object|null} 用户信息,未识别或状态中无用户资料返回 null
594
+ * @private
595
+ */
596
+ Api.prototype._getUserFromState = async function (token, ctx, options) {
597
+ if (!mm_state_store || !mm_dict) {
598
+ return null;
599
+ }
600
+ try {
601
+ // 1. token → user_id:字典映射,或 base64(JSON) 解码
602
+ var user_id = null;
603
+ try {
604
+ user_id = mm_dict.dictAdmin("token").get(token);
605
+ } catch (err) {
606
+ user_id = null;
607
+ }
608
+ if (!user_id) {
609
+ try {
610
+ var json = Buffer.from(token, "base64").toString("utf8");
611
+ if (/^\s*\{[\s\S]*\}\s*$/.test(json)) {
612
+ var obj = JSON.parse(json);
613
+ if (obj && obj.user_id !== undefined && obj.user_id !== null && obj.user_id !== "") {
614
+ user_id = String(obj.user_id);
615
+ }
616
+ }
617
+ } catch (err) {
618
+ user_id = null;
619
+ }
620
+ }
621
+ if (!user_id) {
622
+ return null;
623
+ }
624
+ // 2. user_id → 用户状态
625
+ var state = null;
626
+ try {
627
+ state = await mm_state_store.stateAdmin("user").get("user_" + user_id);
628
+ } catch (err) {
629
+ state = null;
630
+ }
631
+ if (!state || !state.user) {
632
+ return null;
633
+ }
634
+ var o = { ...state.user };
635
+ o.token = token;
636
+ o.user_id = String(user_id);
637
+ if (ctx && ctx.session) {
638
+ ctx.session.uuid = ctx.session.uuid || token;
639
+ ctx.session.user = o;
640
+ }
641
+ return o;
642
+ } catch (err) {
643
+ return null;
644
+ }
548
645
  };
549
646
 
550
647
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm_api",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "API框架,用于实现web api和mvc模式路由请求,负责配置路由路径、访问权限、校验机制;基于 mm_corejs,内置管理器池,无全局/原型扩展依赖",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -24,11 +24,13 @@
24
24
  "dependencies": {
25
25
  "mm_corejs": "^1.1.4",
26
26
  "mm_dao": "^1.8.2",
27
+ "mm_dict": "^1.0.3",
27
28
  "mm_helper": "^1.0.0",
28
29
  "mm_machine": "^3.5.4",
29
30
  "mm_param": "^1.1.0",
30
31
  "mm_ret": "^1.4.8",
31
- "mm_sql": "^1.8.0"
32
+ "mm_sql": "^1.8.0",
33
+ "mm_state_store": "^1.0.3"
32
34
  },
33
35
  "scripts": {
34
36
  "test": "node test.js && node --test tests/*.test.js"