oox 0.3.0-beta9 → 0.3.2

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 (47) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +29 -32
  3. package/app.js +131 -143
  4. package/bin/argv.js +63 -70
  5. package/bin/cli.js +57 -43
  6. package/bin/configurer.js +60 -62
  7. package/bin/loader.mjs +392 -279
  8. package/bin/proxy-import.js +12 -0
  9. package/bin/proxy-require.js +88 -0
  10. package/bin/register.js +46 -55
  11. package/bin/starter.js +63 -66
  12. package/index.js +155 -168
  13. package/index.mjs +4 -4
  14. package/logger.js +25 -40
  15. package/modules/http/index.js +286 -192
  16. package/modules/http/router.js +205 -0
  17. package/modules/http/utils.js +75 -73
  18. package/modules/index.js +86 -88
  19. package/modules/module.js +11 -16
  20. package/modules/socketio/client.js +97 -101
  21. package/modules/socketio/index.js +171 -168
  22. package/modules/socketio/server.js +188 -136
  23. package/modules/socketio/socket.js +1 -4
  24. package/package.json +14 -12
  25. package/types/app.d.ts +50 -51
  26. package/types/bin/argv.d.ts +8 -8
  27. package/types/bin/cli.d.ts +6 -2
  28. package/types/bin/configurer.d.ts +3 -1
  29. package/types/bin/proxy-import.d.ts +4 -0
  30. package/types/bin/proxy-require.d.ts +5 -0
  31. package/types/bin/register.d.ts +1 -1
  32. package/types/bin/starter.d.ts +5 -1
  33. package/types/index.d.ts +78 -76
  34. package/types/logger.d.ts +5 -4
  35. package/types/modules/http/index.d.ts +65 -47
  36. package/types/modules/http/router.d.ts +49 -0
  37. package/types/modules/http/utils.d.ts +14 -17
  38. package/types/modules/index.d.ts +24 -24
  39. package/types/modules/module.d.ts +11 -13
  40. package/types/modules/socketio/client.d.ts +23 -23
  41. package/types/modules/socketio/index.d.ts +37 -37
  42. package/types/modules/socketio/server.d.ts +44 -35
  43. package/types/modules/socketio/socket.d.ts +11 -11
  44. package/types/utils.d.ts +6 -6
  45. package/utils.js +57 -63
  46. package/bin/proxyer.js +0 -61
  47. package/types/bin/proxyer.d.ts +0 -1
@@ -1,192 +1,286 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HTTPConfig = void 0;
4
- const http = require("node:http");
5
- const node_querystring_1 = require("node:querystring");
6
- const utils_1 = require("./utils");
7
- const oox = require("../../index");
8
- const module_1 = require("../module");
9
- class HTTPConfig extends module_1.ModuleConfig {
10
- // listen port
11
- port = 0;
12
- // service path
13
- path = '/';
14
- // browser cross origin
15
- origin = '';
16
- }
17
- exports.HTTPConfig = HTTPConfig;
18
- class HTTPModule extends module_1.default {
19
- name = 'http';
20
- config = new HTTPConfig;
21
- server = null;
22
- setConfig(config) {
23
- Object.assign(this.config, config);
24
- if (!config.hasOwnProperty('port')) {
25
- this.config.port = oox.config.port;
26
- }
27
- if (!config.hasOwnProperty('origin')) {
28
- this.config.origin = oox.config.origin;
29
- }
30
- }
31
- getConfig() {
32
- return this.config;
33
- }
34
- /**
35
- * start http service
36
- */
37
- async serve() {
38
- await this.stop();
39
- const { port } = this.config;
40
- this.server = http.createServer(this.call.bind(this));
41
- this.server.listen(port);
42
- const address = this.server.address();
43
- if (!address || 'object' !== typeof address)
44
- throw new Error('Cannot read http server port');
45
- this.config.port = address.port;
46
- }
47
- /**
48
- * stop http service
49
- */
50
- stop() {
51
- if (this.server && this.server.listening)
52
- return new Promise((resolve, reject) => {
53
- this.server.close(function (error) {
54
- if (error)
55
- reject(error);
56
- else
57
- resolve();
58
- });
59
- });
60
- }
61
- /**
62
- * browser cross origin
63
- */
64
- cors(request, response) {
65
- // origin checking
66
- const origin = this.config.origin;
67
- const requestOrigin = request.headers.origin;
68
- if (origin && requestOrigin) {
69
- if (origin === '*' || origin === requestOrigin || Array.isArray(origin) && origin.includes(requestOrigin)) {
70
- response.setHeader('Access-Control-Allow-Origin', requestOrigin);
71
- response.setHeader('Vary', 'Origin');
72
- }
73
- else {
74
- response.statusCode = 403;
75
- response.end();
76
- return false;
77
- }
78
- response.setHeader('Access-Control-Max-Age', 3600);
79
- response.setHeader('Access-Control-Allow-Headers', 'x-caller,content-type');
80
- response.setHeader('Access-Control-Allow-Methods', '*');
81
- }
82
- if (request.method === 'OPTIONS') {
83
- response.statusCode = 204;
84
- response.end();
85
- return false;
86
- }
87
- return true;
88
- }
89
- /**
90
- * HTTP-RPC服务器请求监听方法
91
- */
92
- async call(request, response) {
93
- if (request.url !== this.config.path) {
94
- const error = {
95
- message: 'Invalid URL',
96
- stack: ''
97
- };
98
- Error.captureStackTrace(error);
99
- return this.respond(request, response, {
100
- success: false,
101
- error
102
- });
103
- }
104
- if (!this.cors(request, response))
105
- return;
106
- let body = Object.create(null);
107
- try {
108
- if ('GET' === request.method) {
109
- body = (0, node_querystring_1.parse)(request.url.split('?').pop());
110
- }
111
- else {
112
- body = await (0, utils_1.parseHTTPBody)(request);
113
- }
114
- if (!body || 'object' !== typeof body)
115
- throw new Error('Content Invalid');
116
- }
117
- catch (error) {
118
- return this.respond(request, response, {
119
- success: false,
120
- error: {
121
- message: error.message,
122
- stack: error.stack
123
- }
124
- });
125
- }
126
- // global unique id
127
- const traceId = String(request.headers['x-trace-id'] || '');
128
- // service name, required
129
- const caller = String(request.headers['x-caller'] || 'anonymous');
130
- // client ip or caller service ip
131
- const ip = String(request.headers['x-ip'] || request.socket.remoteAddress || '');
132
- // startup client ip
133
- const sourceIP = String(request.headers['x-real-ip'] || '');
134
- const { action = 'index', params = [] } = body;
135
- const context = oox.genContext({ traceId, caller, sourceIP, ip, callerId: '' });
136
- const format = await oox.call(action, params, context);
137
- this.respond(request, response, format);
138
- }
139
- /**
140
- * HTTP Response Catch
141
- */
142
- respond(request, response, format) {
143
- let formatString = '';
144
- try {
145
- formatString = JSON.stringify(format);
146
- }
147
- catch ({ message, stack }) {
148
- delete format.body;
149
- format.success = false;
150
- format.error = {
151
- message,
152
- stack
153
- };
154
- formatString = JSON.stringify(format);
155
- }
156
- response.setHeader('Content-Type', 'application/json');
157
- response.setHeader('Content-Length', Buffer.byteLength(formatString));
158
- response.end(formatString);
159
- }
160
- /**
161
- * HTTP RPC
162
- */
163
- async rpc(url, action, params, context) {
164
- if (!context || !context.traceId) {
165
- context = oox.getContext();
166
- }
167
- const { traceId, caller, sourceIP } = context;
168
- const headers = {
169
- 'Content-Type': 'application/json',
170
- 'x-trace-id': String(traceId),
171
- };
172
- if (caller)
173
- headers['x-caller'] = String(caller);
174
- if (sourceIP)
175
- headers['x-real-ip'] = sourceIP;
176
- // headers [ 'x-ip' ] = getIPAddress ( 4 ) [ 0 ]
177
- const format = await (0, utils_1.httpRequest)(url, {
178
- headers
179
- }, JSON.stringify({ action, params }));
180
- if ('string' === typeof format)
181
- throw new Error(format);
182
- const { error, body } = format;
183
- if (error) {
184
- const asyncError = new Error(error.message);
185
- throw asyncError;
186
- }
187
- else {
188
- return body;
189
- }
190
- }
191
- }
192
- exports.default = HTTPModule;
1
+ import * as http from 'node:http';
2
+ import * as https from 'node:https';
3
+ import { httpRequest, parseHTTPBody } from './utils.js';
4
+ import Router from './router.js';
5
+ import * as oox from '../../index.js';
6
+ import Module, { ModuleConfig } from '../module.js';
7
+ export class HTTPConfig extends ModuleConfig {
8
+ // listen port
9
+ port = 0;
10
+ // service path
11
+ path = '/';
12
+ // browser cross origin
13
+ origin = '';
14
+ // https options
15
+ ssl = {
16
+ // enable https
17
+ enabled: false,
18
+ // ssl certificate path
19
+ cert: '',
20
+ // ssl private key path
21
+ key: '',
22
+ // ssl ca path
23
+ ca: ''
24
+ };
25
+ }
26
+ export default class HTTPModule extends Module {
27
+ name = 'http';
28
+ config = new HTTPConfig;
29
+ server = null;
30
+ router = new Router();
31
+ getUrl() {
32
+ const { host } = oox.config;
33
+ const { port, path } = this.config;
34
+ const protocol = this.config.ssl.enabled ? `https:` : `http:`;
35
+ return `${protocol}//${host}:${port}${path}`;
36
+ }
37
+ // 注册GET路由
38
+ get(path, ...args) {
39
+ this.router.get(path, ...args);
40
+ return this;
41
+ }
42
+ // 注册POST路由
43
+ post(path, ...args) {
44
+ this.router.post(path, ...args);
45
+ return this;
46
+ }
47
+ // 注册PUT路由
48
+ put(path, ...args) {
49
+ this.router.put(path, ...args);
50
+ return this;
51
+ }
52
+ // 注册DELETE路由
53
+ delete(path, ...args) {
54
+ this.router.delete(path, ...args);
55
+ return this;
56
+ }
57
+ // 注册全局中间件
58
+ use(middleware) {
59
+ this.router.use(middleware);
60
+ return this;
61
+ }
62
+ setConfig(config) {
63
+ Object.assign(this.config, config);
64
+ if (!config.hasOwnProperty('port')) {
65
+ this.config.port = oox.config.port;
66
+ }
67
+ if (!config.hasOwnProperty('origin')) {
68
+ this.config.origin = oox.config.origin;
69
+ }
70
+ }
71
+ getConfig() {
72
+ return this.config;
73
+ }
74
+ /**
75
+ * start http service
76
+ */
77
+ async serve() {
78
+ await this.stop();
79
+ const { port, ssl } = this.config;
80
+ if (ssl.enabled) {
81
+ const fs = await import('node:fs');
82
+ const options = {
83
+ key: ssl.key ? fs.readFileSync(ssl.key) : null,
84
+ cert: ssl.cert ? fs.readFileSync(ssl.cert) : null,
85
+ ca: ssl.ca ? fs.readFileSync(ssl.ca) : null
86
+ };
87
+ if (!options.key || !options.cert) {
88
+ throw new Error('HTTPS enabled but missing key or cert');
89
+ }
90
+ this.server = https.createServer(options, this.requestHandler.bind(this));
91
+ }
92
+ else {
93
+ this.server = http.createServer(this.requestHandler.bind(this));
94
+ }
95
+ this.server.listen(port);
96
+ const address = this.server.address();
97
+ if (!address || 'object' !== typeof address)
98
+ throw new Error('Cannot read http server port');
99
+ this.config.port = address.port;
100
+ }
101
+ /**
102
+ * stop http service
103
+ */
104
+ stop() {
105
+ if (this.server && this.server.listening)
106
+ return new Promise((resolve, reject) => {
107
+ this.server.close(function (error) {
108
+ if (error)
109
+ reject(error);
110
+ else
111
+ resolve();
112
+ });
113
+ });
114
+ }
115
+ /**
116
+ * browser cross origin
117
+ */
118
+ cors(request, response) {
119
+ // origin checking
120
+ const origin = this.config.origin;
121
+ const requestOrigin = request.headers.origin;
122
+ if (origin && requestOrigin) {
123
+ if (origin === '*' ||
124
+ origin === requestOrigin ||
125
+ (Array.isArray(origin) && origin.includes(requestOrigin))) {
126
+ response.setHeader('Access-Control-Allow-Origin', requestOrigin);
127
+ response.setHeader('Vary', 'Origin');
128
+ }
129
+ else {
130
+ response.statusCode = 403;
131
+ response.end();
132
+ return false;
133
+ }
134
+ response.setHeader('Access-Control-Max-Age', 3600);
135
+ response.setHeader('Access-Control-Allow-Headers', 'x-caller,content-type');
136
+ response.setHeader('Access-Control-Allow-Methods', '*');
137
+ }
138
+ if (request.method === 'OPTIONS') {
139
+ response.statusCode = 204;
140
+ response.end();
141
+ return false;
142
+ }
143
+ return true;
144
+ }
145
+ async requestHandler(request, response) {
146
+ if (!this.cors(request, response))
147
+ return;
148
+ const url = new URL(request.url, `http://${request.headers.host || 'localhost'}`);
149
+ const method = request.method || 'GET';
150
+ // 尝试匹配路由
151
+ const matched = this.router.match(method, url.pathname);
152
+ if (matched) {
153
+ const req = this.router.createRequestObject(request, url);
154
+ const res = this.router.createResponseObject(response);
155
+ // 解析请求体
156
+ if (request.method !== 'GET' && request.method !== 'HEAD') {
157
+ try {
158
+ req.body = await parseHTTPBody(request);
159
+ }
160
+ catch (error) {
161
+ return this.respond(request, response, {
162
+ success: false,
163
+ error: {
164
+ message: error.message,
165
+ stack: error.stack
166
+ }
167
+ });
168
+ }
169
+ }
170
+ await this.router.handleRoute(matched.route, matched.params, req, res);
171
+ return;
172
+ }
173
+ // 回退到RPC调用
174
+ if (url.pathname === this.config.path) {
175
+ await this.call(request, response);
176
+ }
177
+ else {
178
+ const error = new Error('Invalid URL');
179
+ return this.respond(request, response, {
180
+ success: false,
181
+ error: {
182
+ message: error.message,
183
+ stack: error.stack
184
+ }
185
+ });
186
+ }
187
+ }
188
+ /**
189
+ * 从请求里获取调用的接口和参数
190
+ */
191
+ async getCallArgsFromRequest(request) {
192
+ const args = await parseHTTPBody(request);
193
+ if (!args || 'object' !== typeof args || !args.action)
194
+ throw new Error('Content Invalid');
195
+ else
196
+ return args;
197
+ }
198
+ /**
199
+ * HTTP-RPC服务器请求监听方法
200
+ */
201
+ async call(request, response) {
202
+ let callArgs = Object.create(null);
203
+ try {
204
+ callArgs = await this.getCallArgsFromRequest(request);
205
+ }
206
+ catch (error) {
207
+ return this.respond(request, response, {
208
+ success: false,
209
+ error: {
210
+ message: error.message,
211
+ stack: error.stack
212
+ }
213
+ });
214
+ }
215
+ // global unique id
216
+ const traceId = String(request.headers['x-trace-id'] || '');
217
+ // service name, required
218
+ const caller = String(request.headers['x-caller'] || 'anonymous');
219
+ // client ip or caller service ip
220
+ const ip = String(request.headers['x-ip'] || request.socket.remoteAddress || '');
221
+ // startup client ip
222
+ const sourceIP = String(request.headers['x-real-ip'] || '');
223
+ const { action, params = [] } = callArgs;
224
+ const context = oox.genContext({ traceId, caller, sourceIP, ip, callerId: '' });
225
+ const format = await oox.call(action, params, context);
226
+ this.respond(request, response, format);
227
+ }
228
+ /**
229
+ * HTTP Response Catch
230
+ */
231
+ respond(_request, response, returns) {
232
+ let returnsString = '';
233
+ if (!oox.config.errorStack && returns.error) {
234
+ // 不返回错误调用栈信息
235
+ delete returns.error.stack;
236
+ }
237
+ try {
238
+ returnsString = JSON.stringify(returns);
239
+ }
240
+ catch ({ message, stack }) {
241
+ delete returns.body;
242
+ returns.success = false;
243
+ returns.error = {
244
+ message
245
+ };
246
+ if (oox.config.errorStack) {
247
+ // 返回错误调用栈信息
248
+ returns.error.stack = stack;
249
+ }
250
+ returnsString = JSON.stringify(returns);
251
+ }
252
+ response.setHeader('Content-Type', 'application/json');
253
+ response.setHeader('Content-Length', Buffer.byteLength(returnsString));
254
+ response.end(returnsString);
255
+ }
256
+ /**
257
+ * HTTP RPC
258
+ */
259
+ async rpc(url, action, params, context) {
260
+ if (!context || !context.traceId) {
261
+ context = oox.getContext();
262
+ }
263
+ const { traceId, caller, sourceIP } = context;
264
+ const headers = {
265
+ 'Content-Type': 'application/json',
266
+ 'x-trace-id': String(traceId),
267
+ };
268
+ if (caller)
269
+ headers['x-caller'] = String(caller);
270
+ if (sourceIP)
271
+ headers['x-real-ip'] = sourceIP;
272
+ // headers [ 'x-ip' ] = getIPAddress ( 4 ) [ 0 ]
273
+ const format = await httpRequest(url, {
274
+ headers
275
+ }, JSON.stringify({ action, params }));
276
+ if ('string' === typeof format)
277
+ throw new Error(format);
278
+ const { success, error, body } = format;
279
+ if (success)
280
+ return body;
281
+ else if (error)
282
+ throw new Error(error.message);
283
+ else
284
+ throw new Error('[RPC] Unknown Error');
285
+ }
286
+ }