@jayfong/x-server 2.57.1 → 2.58.1

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.
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.ctxStorage = void 0;
5
+ var _nodeAsync_hooks = require("node:async_hooks");
6
+ const ctxStorage = exports.ctxStorage = new _nodeAsync_hooks.AsyncLocalStorage();
@@ -10,6 +10,7 @@ var vae = _interopRequireWildcard(require("vtils/vae"));
10
10
  var _validator = require("vtils/validator");
11
11
  var _http_error = require("../core/http_error");
12
12
  var _dispose = require("../services/dispose");
13
+ var _ctx_storage = require("./ctx_storage");
13
14
  var _http_redirect = require("./http_redirect");
14
15
  var _server = require("./server");
15
16
  _validator.yup.setLocale((0, _validator.getZhCN)({
@@ -25,10 +26,11 @@ class Handler {
25
26
  if (Handler.hooks.length) {
26
27
  await Promise.all(Handler.hooks.map(hook => hook(this.options, data, ctx)));
27
28
  }
29
+ ctx.setHeader('x-trace-id', ctx.traceId);
28
30
  if (this.options.requestMethod === 'WS') {
29
- return this.handleWs(data, ctx);
31
+ return _ctx_storage.ctxStorage.run(ctx, () => this.handleWs(data, ctx));
30
32
  }
31
- return this.handleHttp(data, ctx);
33
+ return _ctx_storage.ctxStorage.run(ctx, () => this.handleHttp(data, ctx));
32
34
  };
33
35
  this.handleHttp = async (data, ctx) => {
34
36
  // 请求数据解析
@@ -7,7 +7,8 @@ var _interopRequireWildcard2 = _interopRequireDefault(require("@babel/runtime/he
7
7
  var _nodeProcess = _interopRequireDefault(require("node:process"));
8
8
  var _fastify = _interopRequireDefault(require("fastify"));
9
9
  var _vtils = require("vtils");
10
- var _x = require("../x");
10
+ var _x = require("vtils/x");
11
+ var _x2 = require("../x");
11
12
  var _http_error = require("./http_error");
12
13
  var _http_method = require("./http_method");
13
14
  class Server {
@@ -66,7 +67,7 @@ class Server {
66
67
  await Promise.resolve().then(() => (0, _interopRequireWildcard2.default)(require('.x/hooks')));
67
68
  }
68
69
  applyServices() {
69
- _x.x.register(...(this.options.services || []));
70
+ _x2.x.register(...(this.options.services || []));
70
71
  }
71
72
  applyPlugins() {
72
73
  const plugins = this.options.plugins || [];
@@ -76,7 +77,7 @@ class Server {
76
77
  }
77
78
  async applyRoutes() {
78
79
  await this.fastify.register(async fastify => {
79
- const appUrl = _x.x.env.APP_URL.replace(/\/+$/, '');
80
+ const appUrl = _x2.x.env.APP_URL.replace(/\/+$/, '');
80
81
  const routeMap = (0, _vtils.keyBy)(this.routes, item => item.path);
81
82
  const handleRoute = async (item, req, res, path) => {
82
83
  const handlerOptions = item.handler.options;
@@ -86,6 +87,7 @@ class Server {
86
87
  path != null ? path : isWS ? res.url : req.url}`;
87
88
  if (isWS) {
88
89
  await item.handler.handle(undefined, {
90
+ traceId: (0, _x.cuid2)(),
89
91
  url: url,
90
92
  headers: res.headers,
91
93
  setHeader: _vtils.noop,
@@ -113,6 +115,7 @@ class Server {
113
115
  ...req.body,
114
116
  ...files
115
117
  }, {
118
+ traceId: (0, _x.cuid2)(),
116
119
  url,
117
120
  headers: req.headers,
118
121
  setHeader: (k, v) => res.header(k, v),
@@ -168,11 +171,11 @@ class Server {
168
171
  url: '/$',
169
172
  handler: async req => {
170
173
  const payload = req.body;
171
- if (payload && typeof payload === 'object' && _x.x.env.APP_TOKEN && payload.token === _x.x.env.APP_TOKEN) {
174
+ if (payload && typeof payload === 'object' && _x2.x.env.APP_TOKEN && payload.token === _x2.x.env.APP_TOKEN) {
172
175
  if (payload.type === 'ping') {
173
176
  return 'ping:success';
174
177
  } else if (payload.type === 'updateEnv') {
175
- Object.assign(_x.x.env, payload.data);
178
+ Object.assign(_x2.x.env, payload.data);
176
179
 
177
180
  // 更新静态文件服务
178
181
  this.applyStatic();
@@ -183,8 +186,8 @@ class Server {
183
186
  });
184
187
  }
185
188
  async applyStatic() {
186
- if (Array.isArray(_x.x.env.APP_STATIC_CONTENT)) {
187
- const staticContent = _x.x.env.APP_STATIC_CONTENT;
189
+ if (Array.isArray(_x2.x.env.APP_STATIC_CONTENT)) {
190
+ const staticContent = _x2.x.env.APP_STATIC_CONTENT;
188
191
  for (const item of staticContent) {
189
192
  if (!item.method) {
190
193
  item.method = 'GET';
@@ -212,7 +215,7 @@ class Server {
212
215
  await Promise.resolve().then(() => (0, _interopRequireWildcard2.default)(require('.x/crons')));
213
216
  }
214
217
  async applyAutoClose() {
215
- _x.x.dispose.add(() => this.close());
218
+ _x2.x.dispose.add(() => this.close());
216
219
  }
217
220
  }
218
221
  exports.Server = Server;
@@ -30,6 +30,7 @@ class CorsPlugin {
30
30
  fastify.register(_cors.default, {
31
31
  origin: (origin, cb) => allowAll ? cb(null, true) : cb(null, origin ? check(origin) : true),
32
32
  maxAge: (0, _vtils.ms)(this.options.ttl, true),
33
+ exposedHeaders: ['x-trace-id'],
33
34
  ...(0, _vtils.omitStrict)(this.options, ['allow', 'ttl'])
34
35
  });
35
36
  }
@@ -74,9 +74,15 @@ class LogService {
74
74
  return this.getLogFile(new Date());
75
75
  }
76
76
  parseLogLineText(text, keysMap = {}) {
77
- const [time, level, title, ...desc] = text.split(/\t+/);
77
+ const segments = text.split(/\t+/);
78
+ // 没有 traceId 的注入一下
79
+ if (segments[1].length < 10) {
80
+ segments.splice(1, 0, '');
81
+ }
82
+ const [time, traceId, level, title, ...desc] = segments;
78
83
  return {
79
84
  time: time,
85
+ traceId: traceId,
80
86
  level: level,
81
87
  title: title,
82
88
  desc: desc.reduce((res, item, index) => {
@@ -91,7 +97,7 @@ class LogService {
91
97
  };
92
98
  }
93
99
  log(payload) {
94
- const getContent = () => `${[(0, _date.formatDate)(new Date(), 'yyyy-mm-dd hh:ii:ss'), payload.level, payload.title, ...(!payload.desc ? [] : Array.isArray(payload.desc) ? payload.desc : Object.keys(payload.desc).map(key => `${key}:${payload.desc[key]}`))].map(item => String(item).replace(/[\r\n\t]+/g, ' ')).join('\t')}`;
100
+ const getContent = () => `${[(0, _date.formatDate)(new Date(), 'yyyy-mm-dd hh:ii:ss'), ...(!_x.x.ctx ? [] : [_x.x.ctx.traceId]), payload.level, payload.title, ...(!payload.desc ? [] : Array.isArray(payload.desc) ? payload.desc : Object.keys(payload.desc).map(key => `${key}:${payload.desc[key]}`))].map(item => String(item).replace(/[\r\n\t]+/g, ' ')).join('\t')}`;
95
101
  if (payload.writer === 'console') {
96
102
  console[payload.level === 'error' ? 'error' : 'log'](getContent());
97
103
  } else {
package/lib/_cjs/x.js CHANGED
@@ -6,6 +6,7 @@ exports.x = void 0;
6
6
  var _os = _interopRequireDefault(require("os"));
7
7
  var _path = _interopRequireDefault(require("path"));
8
8
  var _fsExtra = _interopRequireDefault(require("fs-extra"));
9
+ var _ctx_storage = require("./core/ctx_storage");
9
10
  var _db_value = require("./services/db_value");
10
11
  var _dispose = require("./services/dispose");
11
12
  var _emoji = require("./services/emoji");
@@ -15,6 +16,9 @@ const x = exports.x = {
15
16
  appId: env.APP_ID,
16
17
  env: env,
17
18
  dataDir: _path.default.join(_os.default.homedir(), `.xs/${env.APP_ID}`),
19
+ get ctx() {
20
+ return _ctx_storage.ctxStorage.getStore();
21
+ },
18
22
  register: (...services) => {
19
23
  for (const service of services) {
20
24
  // @ts-ignore
@@ -0,0 +1,4 @@
1
+ /// <reference types="node" />
2
+ import { AsyncLocalStorage } from 'node:async_hooks';
3
+ import { XHandler } from './types';
4
+ export declare const ctxStorage: AsyncLocalStorage<XHandler.Context>;
@@ -0,0 +1,2 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ export const ctxStorage = new AsyncLocalStorage();
@@ -4,6 +4,7 @@ import * as vae from 'vtils/vae';
4
4
  import { getZhCN, yup } from 'vtils/validator';
5
5
  import { HttpError } from "../core/http_error";
6
6
  import { DisposeService } from "../services/dispose";
7
+ import { ctxStorage } from "./ctx_storage";
7
8
  import { HttpRedirect } from "./http_redirect";
8
9
  import { Server } from "./server";
9
10
  yup.setLocale(getZhCN({
@@ -19,10 +20,11 @@ export class Handler {
19
20
  if (Handler.hooks.length) {
20
21
  await Promise.all(Handler.hooks.map(hook => hook(this.options, data, ctx)));
21
22
  }
23
+ ctx.setHeader('x-trace-id', ctx.traceId);
22
24
  if (this.options.requestMethod === 'WS') {
23
- return this.handleWs(data, ctx);
25
+ return ctxStorage.run(ctx, () => this.handleWs(data, ctx));
24
26
  }
25
- return this.handleHttp(data, ctx);
27
+ return ctxStorage.run(ctx, () => this.handleHttp(data, ctx));
26
28
  };
27
29
  this.handleHttp = async (data, ctx) => {
28
30
  // 请求数据解析
@@ -1,6 +1,7 @@
1
1
  import process from 'node:process';
2
2
  import Fastify from 'fastify';
3
3
  import { base64UrlDecode, castArray, keyBy, noop, rot13 } from 'vtils';
4
+ import { cuid2 } from 'vtils/x';
4
5
  import { x } from "../x";
5
6
  import { HttpError } from "./http_error";
6
7
  import { HandlerMethodToHttpMethod } from "./http_method";
@@ -80,6 +81,7 @@ export class Server {
80
81
  path != null ? path : isWS ? res.url : req.url}`;
81
82
  if (isWS) {
82
83
  await item.handler.handle(undefined, {
84
+ traceId: cuid2(),
83
85
  url: url,
84
86
  headers: res.headers,
85
87
  setHeader: noop,
@@ -107,6 +109,7 @@ export class Server {
107
109
  ...req.body,
108
110
  ...files
109
111
  }, {
112
+ traceId: cuid2(),
110
113
  url,
111
114
  headers: req.headers,
112
115
  setHeader: (k, v) => res.header(k, v),
@@ -63,6 +63,10 @@ export declare namespace XHandler {
63
63
  interface ExtraContext {
64
64
  }
65
65
  interface Context extends ExtraContext {
66
+ /**
67
+ * 当前请求的跟踪 ID
68
+ */
69
+ traceId: string;
66
70
  /**
67
71
  * 当前请求的地址
68
72
  */
@@ -26,6 +26,7 @@ export class CorsPlugin {
26
26
  fastify.register(FastifyCors, {
27
27
  origin: (origin, cb) => allowAll ? cb(null, true) : cb(null, origin ? check(origin) : true),
28
28
  maxAge: ms(this.options.ttl, true),
29
+ exposedHeaders: ['x-trace-id'],
29
30
  ...omitStrict(this.options, ['allow', 'ttl'])
30
31
  });
31
32
  }
@@ -10,6 +10,7 @@ export type LogServicePayload = {
10
10
  desc?: LogServiceDesc;
11
11
  };
12
12
  export type LogServiceLogItem<K extends string = string> = Merge<LogServicePayload, {
13
+ traceId: string;
13
14
  time: string;
14
15
  desc: Record<K, any>;
15
16
  }>;
@@ -69,9 +69,15 @@ export class LogService {
69
69
  return this.getLogFile(new Date());
70
70
  }
71
71
  parseLogLineText(text, keysMap = {}) {
72
- const [time, level, title, ...desc] = text.split(/\t+/);
72
+ const segments = text.split(/\t+/);
73
+ // 没有 traceId 的注入一下
74
+ if (segments[1].length < 10) {
75
+ segments.splice(1, 0, '');
76
+ }
77
+ const [time, traceId, level, title, ...desc] = segments;
73
78
  return {
74
79
  time: time,
80
+ traceId: traceId,
75
81
  level: level,
76
82
  title: title,
77
83
  desc: desc.reduce((res, item, index) => {
@@ -86,7 +92,7 @@ export class LogService {
86
92
  };
87
93
  }
88
94
  log(payload) {
89
- const getContent = () => `${[formatDate(new Date(), 'yyyy-mm-dd hh:ii:ss'), payload.level, payload.title, ...(!payload.desc ? [] : Array.isArray(payload.desc) ? payload.desc : Object.keys(payload.desc).map(key => `${key}:${payload.desc[key]}`))].map(item => String(item).replace(/[\r\n\t]+/g, ' ')).join('\t')}`;
95
+ const getContent = () => `${[formatDate(new Date(), 'yyyy-mm-dd hh:ii:ss'), ...(!x.ctx ? [] : [x.ctx.traceId]), payload.level, payload.title, ...(!payload.desc ? [] : Array.isArray(payload.desc) ? payload.desc : Object.keys(payload.desc).map(key => `${key}:${payload.desc[key]}`))].map(item => String(item).replace(/[\r\n\t]+/g, ' ')).join('\t')}`;
90
96
  if (payload.writer === 'console') {
91
97
  console[payload.level === 'error' ? 'error' : 'log'](getContent());
92
98
  } else {
package/lib/x.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ import { XHandler } from './core/types';
2
3
  import { BaseService } from './services/base';
3
4
  import './services/db_value';
4
5
  import './services/dispose';
@@ -8,6 +9,7 @@ export interface X {
8
9
  readonly appId: string;
9
10
  readonly env: NodeJS.ProcessEnv;
10
11
  readonly dataDir: string;
12
+ readonly ctx: XHandler.Context;
11
13
  readonly register: (...services: BaseService[]) => void;
12
14
  }
13
15
  export declare const x: X;
package/lib/x.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import os from 'os';
2
2
  import path from 'path';
3
3
  import fs from 'fs-extra';
4
+ import { ctxStorage } from "./core/ctx_storage";
4
5
  import { DbValueService } from "./services/db_value";
5
6
  import { DisposeService } from "./services/dispose";
6
7
  import { EmojiService } from "./services/emoji";
@@ -10,6 +11,9 @@ export const x = {
10
11
  appId: env.APP_ID,
11
12
  env: env,
12
13
  dataDir: path.join(os.homedir(), `.xs/${env.APP_ID}`),
14
+ get ctx() {
15
+ return ctxStorage.getStore();
16
+ },
13
17
  register: (...services) => {
14
18
  for (const service of services) {
15
19
  // @ts-ignore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jayfong/x-server",
3
- "version": "2.57.1",
3
+ "version": "2.58.1",
4
4
  "license": "ISC",
5
5
  "sideEffects": false,
6
6
  "main": "lib/_cjs/index.js",