@domain.js/main 0.1.13 → 0.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/.eslintrc.js CHANGED
@@ -8,6 +8,7 @@ module.exports = {
8
8
  node: true,
9
9
  commonjs: true,
10
10
  es6: true,
11
+ jest: true,
11
12
  },
12
13
  globals: {
13
14
  // Your global variables (setting to false means it's not allowed to be reassigned)
package/.husky/pre-commit CHANGED
@@ -5,5 +5,4 @@ fi
5
5
  . "$(dirname "$0")/_/husky.sh"
6
6
 
7
7
  ts-node src/cli/index.ts loadDeps ./src/deps ts && npm run lint-staged && npm run build
8
- npm run build
9
8
  npm tst
@@ -1,18 +1,18 @@
1
- import _ from "lodash";
2
- import * as uuid from "uuid";
3
1
  import * as ajv from "ajv";
4
2
  import * as ajvFormats from "ajv-formats";
5
3
  import * as async from "async";
6
4
  import * as axios from "axios";
7
5
  import * as cronParser from "cron-parser";
8
6
  import humanInterval from "human-interval";
9
- import * as IORedis from "ioredis";
7
+ import IORedis from "ioredis";
8
+ import _ from "lodash";
10
9
  import LRU from "lru-cache";
10
+ import moment from "moment";
11
11
  import * as mysql from "mysql2";
12
12
  import * as Sequelize from "sequelize";
13
- import moment from "moment";
14
- import * as utils from "./utils";
13
+ import * as uuid from "uuid";
15
14
  import { errors } from "./basic-errors";
15
+ import * as utils from "./utils";
16
16
  /** npm packages injection */
17
17
  export interface Defaults {
18
18
  /**
package/dist/defaults.js CHANGED
@@ -23,21 +23,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.defaults = void 0;
26
- const lodash_1 = __importDefault(require("lodash"));
27
- const uuid = __importStar(require("uuid"));
28
26
  const ajv = __importStar(require("ajv"));
29
27
  const ajvFormats = __importStar(require("ajv-formats"));
30
28
  const async = __importStar(require("async"));
31
29
  const axios = __importStar(require("axios"));
32
30
  const cronParser = __importStar(require("cron-parser"));
33
31
  const human_interval_1 = __importDefault(require("human-interval"));
34
- const IORedis = __importStar(require("ioredis"));
32
+ const ioredis_1 = __importDefault(require("ioredis"));
33
+ const lodash_1 = __importDefault(require("lodash"));
35
34
  const lru_cache_1 = __importDefault(require("lru-cache"));
35
+ const moment_1 = __importDefault(require("moment"));
36
36
  const mysql = __importStar(require("mysql2"));
37
37
  const Sequelize = __importStar(require("sequelize"));
38
- const moment_1 = __importDefault(require("moment"));
39
- const utils = __importStar(require("./utils"));
38
+ const uuid = __importStar(require("uuid"));
40
39
  const basic_errors_1 = require("./basic-errors");
40
+ const utils = __importStar(require("./utils"));
41
41
  exports.defaults = {
42
42
  _: lodash_1.default,
43
43
  uuid,
@@ -47,7 +47,7 @@ exports.defaults = {
47
47
  axios,
48
48
  cronParser,
49
49
  humanInterval: human_interval_1.default,
50
- IORedis,
50
+ IORedis: ioredis_1.default,
51
51
  LRU: lru_cache_1.default,
52
52
  mysql,
53
53
  Sequelize,
@@ -1,30 +1,15 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
- Object.defineProperty(o, "default", { enumerable: true, value: v });
11
- }) : function(o, v) {
12
- o["default"] = v;
13
- });
14
- var __importStar = (this && this.__importStar) || function (mod) {
15
- if (mod && mod.__esModule) return mod;
16
- var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
- __setModuleDefault(result, mod);
19
- return result;
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
20
4
  };
21
5
  Object.defineProperty(exports, "__esModule", { value: true });
22
6
  exports.Deps = exports.Main = void 0;
23
- const process = __importStar(require("process"));
7
+ const process_1 = __importDefault(require("process"));
24
8
  function Graceful(info) {
25
9
  let exiting = false; // 是否正在退出
26
10
  const callbacks = [];
27
- const counter = ((count = 0) => {
11
+ const counter = ((init = 0) => {
12
+ let count = init;
28
13
  const decr = () => {
29
14
  count -= 1;
30
15
  };
@@ -72,11 +57,11 @@ function Graceful(info) {
72
57
  return;
73
58
  info("process exit check success, process exited");
74
59
  clearInterval(timer);
75
- process.exit(0);
60
+ process_1.default.exit(0);
76
61
  }, 1000);
77
62
  };
78
- process.on("SIGTERM", exitHandle);
79
- process.on("SIGINT", exitHandle);
63
+ process_1.default.on("SIGTERM", exitHandle);
64
+ process_1.default.on("SIGINT", exitHandle);
80
65
  /**
81
66
  * addListen function be called when process exit
82
67
  * @param listenner
@@ -1,6 +1,6 @@
1
1
  import _ from "lodash";
2
2
  import * as Sequelize from "sequelize";
3
- import { TModel, Params } from "./defines";
3
+ import { ModelBase, ModelStatic } from "../sequelize";
4
4
  import { Stats } from "./stats";
5
5
  import { Utils } from "./utils";
6
6
  export { Before } from "./Before";
@@ -24,14 +24,14 @@ export interface CreatorAndClientIp {
24
24
  * @returns modify, add, remove, list, stats five methods
25
25
  */
26
26
  export declare function Main(cnf: Cnf, deps: Deps, utils: ReturnType<typeof Utils>): {
27
- modify: (Model: TModel, model: Sequelize.Model<any, any>, params: Params, isAdmin?: boolean, _cols?: string[] | undefined) => Promise<Sequelize.Model<any, any>>;
28
- add: (Model: TModel, params: Params, isAdmin: boolean | undefined, _cols: string[] | undefined, { creatorId, clientIp }: CreatorAndClientIp) => Promise<any>;
27
+ modify: <T extends ModelBase<any, any>>(Model: ModelStatic<T>, model: T, params: Record<string, any>, isAdmin?: boolean, _cols?: string[] | undefined) => Promise<T>;
28
+ add: <T_1 extends ModelBase<any, any>>(Model: ModelStatic<T_1>, params: Record<string, any>, isAdmin: boolean | undefined, _cols: string[] | undefined, { creatorId, clientIp }: CreatorAndClientIp) => Promise<T_1>;
29
29
  remove: (model: Sequelize.Model, deletorId: UserId) => Promise<void | Sequelize.Model<any, any>>;
30
- list: (Model: TModel, params: Params, allowAttrs?: string[] | undefined, toJSON?: boolean | undefined) => Promise<{
30
+ list: <T_2 extends ModelBase<any, any>>(Model: ModelStatic<T_2>, params: Record<string, any>, allowAttrs?: string[] | undefined, toJSON?: boolean | undefined) => Promise<{
31
31
  count: number;
32
- rows: any[];
32
+ rows: T_2[];
33
33
  }>;
34
- stats: (Model: TModel, params: Params, where?: any, conf?: {
34
+ stats: <T_3 extends ModelBase<any, any>>(Model: ModelStatic<T_3>, params: Record<string, any>, where?: any, conf?: {
35
35
  dimensions?: Record<string, string> | undefined;
36
36
  metrics: Record<string, string>;
37
37
  pagination?: {
@@ -41,7 +41,7 @@ export declare function Main(cnf: Cnf, deps: Deps, utils: ReturnType<typeof Util
41
41
  } | undefined;
42
42
  } | undefined) => Promise<{
43
43
  count: number;
44
- rows: any;
44
+ rows: Record<string, string | number>[];
45
45
  }>;
46
46
  };
47
47
  export declare const Deps: string[];
@@ -116,7 +116,7 @@ function Main(cnf, deps, utils) {
116
116
  count = await Model.count(lodash_1.default.pick(opt, COUNT_OPT));
117
117
  if (Array.isArray(allowAttrs) && allowAttrs.length)
118
118
  opt.attributes = allowAttrs;
119
- const rows = (await Model.findAll(opt));
119
+ const rows = await Model.findAll(opt);
120
120
  if (toJSON) {
121
121
  for (let i = 0; i < rows.length; i += 1) {
122
122
  rows[i] = rows[i].toJSON();
@@ -1,13 +1,21 @@
1
1
  import _ from "lodash";
2
2
  import * as Sequelize from "sequelize";
3
- import { Params, ModelExtraAtts, TModel } from "./defines";
3
+ import { ModelBase, ModelStatic } from "../sequelize";
4
4
  import { Utils } from "./utils";
5
5
  interface Deps {
6
6
  _: typeof _;
7
7
  Sequelize: Pick<typeof Sequelize, "literal" | "and" | "fn">;
8
8
  }
9
- export declare function Stats(cnf: {}, deps: Deps, utils: ReturnType<typeof Utils>): (Model: TModel, params: Params, where?: any, conf?: ModelExtraAtts["stats"]) => Promise<{
9
+ export declare function Stats(cnf: {}, deps: Deps, utils: ReturnType<typeof Utils>): <T extends ModelBase<any, any>>(Model: ModelStatic<T>, params: Record<string, any>, where?: any, conf?: {
10
+ dimensions?: Record<string, string> | undefined;
11
+ metrics: Record<string, string>;
12
+ pagination?: {
13
+ maxResults: number;
14
+ maxStartIndex: number;
15
+ maxResultsLimit: number;
16
+ } | undefined;
17
+ } | undefined) => Promise<{
10
18
  count: number;
11
- rows: any;
19
+ rows: Record<string, string | number>[];
12
20
  }>;
13
21
  export {};
@@ -149,7 +149,7 @@ function Stats(cnf, deps, utils) {
149
149
  let count = 0;
150
150
  if (_ignoreTotal !== "yes")
151
151
  count = await statsCount(Model, opt, dims);
152
- const rows = await Model.findAll(opt);
152
+ const rows = (await Model.findAll(opt));
153
153
  for (const x of rows) {
154
154
  for (const met of metrics) {
155
155
  x[met] = x[met] ? Number(x[met]) : 0;
@@ -1,8 +1,8 @@
1
1
  import type { LoDashStatic } from "lodash";
2
+ import moment from "moment";
2
3
  import * as mysql from "mysql2";
3
4
  import * as Sequelize from "sequelize";
4
- import moment from "moment";
5
- import { ModelExtraAtts, TModel, Params } from "./defines";
5
+ import { ModelBase, ModelStatic } from "../sequelize";
6
6
  interface Cnf {
7
7
  rest: {
8
8
  relativeMaxRangeDays?: number;
@@ -19,11 +19,15 @@ interface Deps {
19
19
  };
20
20
  }
21
21
  export declare function Utils(cnf: Cnf, deps: Deps): Readonly<{
22
- pickParams: (params: any, cols: string[], Model: TModel, isAdmin: boolean) => {
22
+ pickParams: <T extends ModelBase<any, any>>(params: any, cols: string[], Model: ModelStatic<T>, isAdmin: boolean) => {
23
23
  [propName: string]: any;
24
24
  };
25
- findAllOpts: (Model: TModel, params: Params) => any;
26
- pageParams: (pagination: ModelExtraAtts["pagination"], params: Params) => {
25
+ findAllOpts: <T_1 extends ModelBase<any, any>>(Model: ModelStatic<T_1>, params: Record<string, any>) => any;
26
+ pageParams: (pagination: {
27
+ maxResults: number;
28
+ maxStartIndex: number;
29
+ maxResultsLimit: number;
30
+ } | undefined, params: Record<string, any>) => {
27
31
  offset: number;
28
32
  limit: number;
29
33
  };
@@ -199,6 +199,7 @@ function Utils(cnf, deps) {
199
199
  };
200
200
  const RELATIVE_RANGE_ERROR = errors.notAllowed(`相对时间跨度最多 ${RELATIVE_MAX_RANGE} 天`);
201
201
  // findOptFilter 的处理
202
+ // eslint-disable-next-line complexity
202
203
  const findOptFilter = (params, name, where, Op, col = name) => {
203
204
  let value;
204
205
  if (!params)
@@ -391,10 +392,12 @@ function Utils(cnf, deps) {
391
392
  const searchOptResII = searchOpt(x.model, params._searchs, params.q, x.as);
392
393
  if (searchOptResII)
393
394
  searchOrs.push(searchOptResII);
394
- x.where = includeWhere;
395
395
  // 以及关联资源允许返回的字段
396
- if (x.model.allowIncludeCols)
397
- x.attributes = x.model.allowIncludeCols;
396
+ let attributes;
397
+ if (x.model.allowIncludeCols && x.model.allowIncludeCols.length)
398
+ attributes = x.model.allowIncludeCols;
399
+ // 将过滤条件和查询的字段附加上去
400
+ Object.assign(x, { where: includeWhere, attributes });
398
401
  });
399
402
  }
400
403
  const ret = {
@@ -1,16 +1,85 @@
1
- import * as Sequelize from "sequelize";
1
+ import { Model, Options, Sequelize } from "sequelize";
2
2
  interface Cnf {
3
3
  sequelize: {
4
- [propName: string]: Sequelize.Options;
4
+ [propName: string]: Options;
5
5
  };
6
6
  }
7
7
  interface Deps {
8
8
  Sequelize: {
9
- Sequelize: typeof Sequelize.Sequelize;
9
+ Sequelize: typeof Sequelize;
10
10
  };
11
11
  }
12
+ declare type NonConstructorKeys<T> = {
13
+ [P in keyof T]: T[P] extends new () => any ? never : P;
14
+ }[keyof T];
15
+ declare type NonConstructor<T> = Pick<T, NonConstructorKeys<T>>;
16
+ export declare type ModelStatic<M extends ModelBase> = NonConstructor<typeof ModelBase> & (new () => M);
12
17
  export declare function Main(cnf: Cnf, deps: Deps): {
13
- [propName: string]: Sequelize.Sequelize;
18
+ [propName: string]: Sequelize;
14
19
  };
15
20
  export declare const Deps: string[];
21
+ /**
22
+ * Model 基类
23
+ */
24
+ export declare class ModelBase<Attrs extends {} = any, Attrs4Create extends {} = Attrs> extends Model<Attrs, Attrs4Create> {
25
+ /**
26
+ * 基于主键获取某条数据的Mode实例,自动维护内存级 cache
27
+ * @param pk 主键
28
+ */
29
+ static getByPk<M extends ModelBase>(this: ModelStatic<M>, pk: string | number): Promise<M | null>;
30
+ /**
31
+ * 基于主键获取某些数据的Mode实例列表,维持参数的顺序,自动维护内存级 cache
32
+ * @param pks 主键数组
33
+ */
34
+ static getByPks<M extends ModelBase>(this: ModelStatic<M>, pks: string[] | number[]): Promise<M[]>;
35
+ /** 允许过滤的字段, 对于某些隐私、敏感信息,应该禁止基于其过滤, 使用者反复尝试可以暴力破解敏感信息 */
36
+ static filterAttrs?: string[];
37
+ /** 新增资源的时候可以写入的列名称集合 */
38
+ static writableCols?: string[];
39
+ /** 编辑资源的时候可以写入的列名称集合 */
40
+ static editableCols?: string[];
41
+ /** 关联资源的时候允许被关联展示的列名称集合 */
42
+ static allowIncludeCols?: string[];
43
+ /** 编辑过程中,仅管理可以更改的列名称集合 */
44
+ static onlyAdminCols?: string[];
45
+ /** 列表查询时候分页控制参数 */
46
+ static pagination?: {
47
+ maxResults: number;
48
+ maxStartIndex: number;
49
+ maxResultsLimit: number;
50
+ };
51
+ /** 列表查询时候排序控制参数 */
52
+ static sort?: {
53
+ default: string;
54
+ allow: string[];
55
+ defaultDirection?: "DESC" | "ASC";
56
+ };
57
+ /** 关联资源设定, 除非要关联过滤,否则不要设置资源之间的关联关系 */
58
+ static includes?: {
59
+ [k: string]: {
60
+ as: string;
61
+ required: boolean;
62
+ model: typeof ModelBase;
63
+ };
64
+ };
65
+ /** 模糊搜索相关设定 */
66
+ static searchCols?: {
67
+ [k: string]: {
68
+ op: "=" | "LIKE";
69
+ match: string[];
70
+ };
71
+ };
72
+ /** 统计相关设定 */
73
+ static stats?: {
74
+ dimensions?: Record<string, string>;
75
+ metrics: Record<string, string>;
76
+ pagination?: {
77
+ maxResults: number;
78
+ maxStartIndex: number;
79
+ maxResultsLimit: number;
80
+ };
81
+ };
82
+ /** 联合唯一列名称集合,用来自动恢复软删除的资源 */
83
+ static unique?: string[];
84
+ }
16
85
  export {};
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Deps = exports.Main = void 0;
3
+ exports.ModelBase = exports.Deps = exports.Main = void 0;
4
+ const sequelize_1 = require("sequelize");
4
5
  function Main(cnf, deps) {
5
6
  // 这里之所以要注入 Sequelize 是为了保证项目自身可以灵活选择自己的 Sequelize 版本, 这样改公共模块就会更加稳定, 避免频繁升级
6
7
  const { sequelize: dbs } = cnf;
@@ -14,3 +15,49 @@ function Main(cnf, deps) {
14
15
  }
15
16
  exports.Main = Main;
16
17
  exports.Deps = ["Sequelize"];
18
+ /**
19
+ * Model 基类
20
+ */
21
+ class ModelBase extends sequelize_1.Model {
22
+ /**
23
+ * 基于主键获取某条数据的Mode实例,自动维护内存级 cache
24
+ * @param pk 主键
25
+ */
26
+ static getByPk(pk) {
27
+ return this.findByPk(pk);
28
+ }
29
+ /**
30
+ * 基于主键获取某些数据的Mode实例列表,维持参数的顺序,自动维护内存级 cache
31
+ * @param pks 主键数组
32
+ */
33
+ static async getByPks(pks) {
34
+ if (!Array.isArray(pks) || !pks.length)
35
+ return [];
36
+ //静态方法调用同一个类中的其他静态方法,可使用 this 关键字
37
+ // eslint-disable-next-line no-undef
38
+ const list = [];
39
+ for await (const x of pks) {
40
+ const item = await this.getByPk(x);
41
+ if (item)
42
+ list.push(item);
43
+ }
44
+ return list;
45
+ }
46
+ }
47
+ exports.ModelBase = ModelBase;
48
+ /** 新增资源的时候可以写入的列名称集合 */
49
+ ModelBase.writableCols = [];
50
+ /** 编辑资源的时候可以写入的列名称集合 */
51
+ ModelBase.editableCols = [];
52
+ /** 列表查询时候分页控制参数 */
53
+ ModelBase.pagination = {
54
+ maxResults: 10,
55
+ maxStartIndex: 50000,
56
+ maxResultsLimit: 5000,
57
+ };
58
+ /** 列表查询时候排序控制参数 */
59
+ ModelBase.sort = {
60
+ default: "id",
61
+ defaultDirection: "DESC",
62
+ allow: ["id"],
63
+ };
@@ -4,6 +4,7 @@ export interface Cnf {
4
4
  host?: string;
5
5
  bodyMaxBytes?: number;
6
6
  apisRoute?: string;
7
+ socket?: boolean;
7
8
  [propName: string]: any;
8
9
  }
9
10
  interface Sign {
@@ -28,6 +29,9 @@ export interface Profile {
28
29
  uuid?: string;
29
30
  token?: string;
30
31
  sign?: Sign;
32
+ isSocket?: boolean;
33
+ /** socket 的时候汇报自己的位置 */
34
+ position?: string;
31
35
  }
32
36
  export interface HttpCodes {
33
37
  [propName: string]: number;
@@ -35,9 +39,7 @@ export interface HttpCodes {
35
39
  export interface Domain {
36
40
  [propName: string]: (profile: Profile, params: any) => any | Domain;
37
41
  }
38
- export interface GetSchemaByPath {
39
- (methodPath: string): [any, any];
40
- }
42
+ export declare type GetSchemaByPath = (methodPath: string) => [any, any];
41
43
  export interface Err {
42
44
  message: string;
43
45
  code?: number | string;
@@ -1,8 +1,8 @@
1
1
  import * as restify from "restify";
2
+ import { Cnf, Domain, GetSchemaByPath, HttpCodes, Profile } from "./defines";
2
3
  import { Router } from "./router";
3
- import { Cnf, Domain, Profile, HttpCodes, GetSchemaByPath } from "./defines";
4
4
  interface Deps {
5
- routers(r: ReturnType<typeof Router>): void;
5
+ routers: (r: ReturnType<typeof Router>) => void;
6
6
  domain: Domain;
7
7
  httpCodes: HttpCodes;
8
8
  getSchemaByPath: GetSchemaByPath;
@@ -21,7 +21,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.Main = void 0;
23
23
  const restify = __importStar(require("restify"));
24
+ const socket_io_1 = require("socket.io");
24
25
  const router_1 = require("./router");
26
+ const socket_1 = require("./socket");
25
27
  const utils_1 = require("./utils");
26
28
  function Main(cnf, deps) {
27
29
  const utils = (0, utils_1.Utils)(cnf);
@@ -42,6 +44,11 @@ function Main(cnf, deps) {
42
44
  apisRoute: cnf.apisRoute,
43
45
  });
44
46
  routers(router);
47
+ // 根据需求起送socket服务
48
+ if (cnf.socket) {
49
+ const io = new socket_io_1.Server(server);
50
+ (0, socket_1.BridgeSocket)(io, domain);
51
+ }
45
52
  // Http server start
46
53
  return () => {
47
54
  server.listen(cnf.port || 8088, cnf.host || "127.0.0.1", () => {
@@ -1,13 +1,13 @@
1
1
  import * as restify from "restify";
2
+ import { Domain, GetSchemaByPath, HttpCodes, Profile } from "./defines";
2
3
  import { Utils } from "./utils";
3
- import { HttpCodes, Domain, Profile, GetSchemaByPath } from "./defines";
4
4
  interface Deps {
5
5
  domain: Domain;
6
6
  getSchemaByPath: GetSchemaByPath;
7
7
  utils: ReturnType<typeof Utils>;
8
8
  server: restify.Server;
9
9
  httpCodes: HttpCodes;
10
- makeProfileHook?(obj: Profile, req: restify.Request): any;
10
+ makeProfileHook?: (obj: Profile, req: restify.Request) => any;
11
11
  apisRoute?: string;
12
12
  swagger?: [any, any];
13
13
  }
@@ -71,6 +71,7 @@ function Router(deps) {
71
71
  next();
72
72
  });
73
73
  }
74
+ // eslint-disable-next-line max-params
74
75
  function register(verb, route, methodPath, code = 200, isList = false, handler, resHandler) {
75
76
  /**
76
77
  * 暂存起来,提供给apis接口来用
@@ -93,7 +94,7 @@ function Router(deps) {
93
94
  try {
94
95
  let results = await method(profile, params);
95
96
  res.header("X-ConsumedTime", Date.now() - profile.startedAt.valueOf());
96
- if (results == null)
97
+ if (results === null || results === undefined)
97
98
  results = "Ok";
98
99
  if (resHandler) {
99
100
  resHandler(results, res);
@@ -0,0 +1,3 @@
1
+ import { Server } from "socket.io";
2
+ import { Domain } from "./defines";
3
+ export declare function BridgeSocket(io: Server, domain: Domain): void;
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BridgeSocket = void 0;
4
+ const proxyIps = new Set(["127.0.0.1"]);
5
+ class MyError extends Error {
6
+ constructor(code, message, data) {
7
+ super(message);
8
+ this.code = code;
9
+ this.data = data;
10
+ }
11
+ }
12
+ const utils = {
13
+ /** 真实的连接请求端ip */
14
+ remoteIp(client) {
15
+ return client.handshake.address;
16
+ },
17
+ /**
18
+ * 获取客户端真实ip地址
19
+ */
20
+ clientIp(client) {
21
+ const { headers } = client.handshake;
22
+ const clientIp = headers["x-forwarded-for"] || headers["x-real-ip"] || utils.remoteIp(client);
23
+ if (Array.isArray(clientIp))
24
+ return clientIp[0];
25
+ return clientIp.split(",")[0];
26
+ },
27
+ /**
28
+ * 获取可信任的真实ip
29
+ */
30
+ realIp(client) {
31
+ const remoteIp = utils.remoteIp(client);
32
+ if (!proxyIps.has(remoteIp))
33
+ return remoteIp;
34
+ const realIp = client.handshake.headers["x-real-ip"] || remoteIp;
35
+ if (Array.isArray(realIp))
36
+ return realIp[0];
37
+ return realIp.split(",")[0];
38
+ },
39
+ };
40
+ const makeProfile = (client, token, params, extra) => {
41
+ const obj = {
42
+ token,
43
+ clientIp: utils.clientIp(client),
44
+ remoteIp: utils.remoteIp(client),
45
+ realIp: utils.realIp(client),
46
+ isSocket: true,
47
+ startedAt: new Date(),
48
+ userAgent: client.handshake.headers["user-agent"] || "Not captured",
49
+ requestId: client.id,
50
+ /** 客户端发布号 */
51
+ revision: params.revision,
52
+ /** 用户uuid 可以长期跨app */
53
+ uuid: params.uuid,
54
+ /** 额外信息,自由扩展 */
55
+ extra,
56
+ };
57
+ return obj;
58
+ };
59
+ function BridgeSocket(io, domain) {
60
+ const subscribe = domain["message.subscribe"];
61
+ const unsubscribe = domain["message.unsubscribe"];
62
+ if (!subscribe)
63
+ throw Error("要启用 socket 服务,必须要要有 message.subscribe 方法,用来处理 socket 订阅");
64
+ if (!unsubscribe)
65
+ throw Error("要启用 socket 服务,必须要要有 message.unsubscribe 方法,用来处理 socket 退订");
66
+ io.on("connection", (client) => {
67
+ console.log("[%s] connection: client.id: %s", new Date(), client.id);
68
+ client.on("init", async (token, params, extra) => {
69
+ console.log("[%s] socket.init: client.id: %s", new Date(), client.id);
70
+ if (!token) {
71
+ client.emit("initError", "Token lost");
72
+ return;
73
+ }
74
+ try {
75
+ Object.assign(client, { profile: makeProfile(client, token, params, extra) });
76
+ if (!client.profile)
77
+ throw new MyError("noAuth", "请先登录");
78
+ const session = await domain["auth.session"](client.profile, {});
79
+ // 创建消息监听函数
80
+ if (!client.listener)
81
+ client.listener = client.emit.bind(client);
82
+ // 向领域注册改用户的监听函数
83
+ domain["message.subscribe"](client.profile, client.listener);
84
+ client.emit("inited", session);
85
+ }
86
+ catch (e) {
87
+ if (e instanceof MyError) {
88
+ client.emit("internalError", e.message, e.code || "unknown");
89
+ return;
90
+ }
91
+ console.error(e);
92
+ }
93
+ });
94
+ client.use(async ([name, params, responseId], next) => {
95
+ if (name === "init")
96
+ return next();
97
+ const method = domain[name];
98
+ try {
99
+ if (!method)
100
+ throw new MyError("notFound", "不存在该领域方法");
101
+ if (!client.profile)
102
+ throw new MyError("noAuth", "请先执行 init");
103
+ const res = await method(client.profile, params);
104
+ if (responseId) {
105
+ client.emit("response", responseId, res);
106
+ }
107
+ }
108
+ catch (e) {
109
+ if (e instanceof Error) {
110
+ if (responseId) {
111
+ client.emit("responseError", responseId, e.code, e.message, e.data);
112
+ }
113
+ else {
114
+ client.emit(`${name}Error`, e.code, e.message, e.data);
115
+ }
116
+ }
117
+ else {
118
+ console.error(e);
119
+ }
120
+ }
121
+ return next();
122
+ });
123
+ // 掉线
124
+ client.on("disconnect", () => {
125
+ if (!client.profile)
126
+ return;
127
+ if (!client.listener)
128
+ return;
129
+ // 这里要取消对领域消息的监听
130
+ unsubscribe(client.profile, client.listener);
131
+ });
132
+ });
133
+ }
134
+ exports.BridgeSocket = BridgeSocket;
@@ -15,7 +15,7 @@ export declare function Utils(cnf: Cnf): {
15
15
  /**
16
16
  * 构造profile参数
17
17
  */
18
- makeProfile(req: restify.Request, method: string, customFn?: Function | undefined): Readonly<Profile>;
18
+ makeProfile<T extends {} = {}>(req: restify.Request, method: string, customFn?: ((obj: Profile, req: restify.Request) => T) | undefined): Profile & T;
19
19
  /**
20
20
  * 构造领域方法所需的 params 参数
21
21
  */