dynapm 1.0.12 → 1.0.14

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/dist/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  var __webpack_modules__ = {
3
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/buffer-util.js": function(module, __unused_webpack_exports, __webpack_require__) {
3
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/buffer-util.js" (module, __unused_rspack_exports, __webpack_require__) {
4
4
  const { EMPTY_BUFFER } = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js");
5
5
  const FastBuffer = Buffer[Symbol.species];
6
6
  function concat(list, totalLength) {
@@ -61,13 +61,13 @@ var __webpack_modules__ = {
61
61
  };
62
62
  } catch (e) {}
63
63
  },
64
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js": function(module) {
64
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js" (module) {
65
65
  const BINARY_TYPES = [
66
66
  'nodebuffer',
67
67
  'arraybuffer',
68
68
  'fragments'
69
69
  ];
70
- const hasBlob = 'undefined' != typeof Blob;
70
+ const hasBlob = "u" > typeof Blob;
71
71
  if (hasBlob) BINARY_TYPES.push('blob');
72
72
  module.exports = {
73
73
  BINARY_TYPES,
@@ -82,7 +82,7 @@ var __webpack_modules__ = {
82
82
  NOOP: ()=>{}
83
83
  };
84
84
  },
85
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/event-target.js": function(module, __unused_webpack_exports, __webpack_require__) {
85
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/event-target.js" (module, __unused_rspack_exports, __webpack_require__) {
86
86
  const { kForOnEventAttribute, kListener } = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js");
87
87
  const kCode = Symbol('kCode');
88
88
  const kData = Symbol('kData');
@@ -227,7 +227,7 @@ var __webpack_modules__ = {
227
227
  else listener.call(thisArg, event);
228
228
  }
229
229
  },
230
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/extension.js": function(module, __unused_webpack_exports, __webpack_require__) {
230
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/extension.js" (module, __unused_rspack_exports, __webpack_require__) {
231
231
  const { tokenChars } = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/validation.js");
232
232
  function push(dest, name, elem) {
233
233
  if (void 0 === dest[name]) dest[name] = [
@@ -249,54 +249,51 @@ var __webpack_modules__ = {
249
249
  let i = 0;
250
250
  for(; i < header.length; i++){
251
251
  code = header.charCodeAt(i);
252
- if (void 0 === extensionName) {
253
- if (-1 === end && 1 === tokenChars[code]) {
254
- if (-1 === start) start = i;
255
- } else if (0 !== i && (0x20 === code || 0x09 === code)) {
256
- if (-1 === end && -1 !== start) end = i;
257
- } else if (0x3b === code || 0x2c === code) {
258
- if (-1 === start) throw new SyntaxError(`Unexpected character at index ${i}`);
259
- if (-1 === end) end = i;
260
- const name = header.slice(start, end);
261
- if (0x2c === code) {
262
- push(offers, name, params);
263
- params = Object.create(null);
264
- } else extensionName = name;
265
- start = end = -1;
266
- } else throw new SyntaxError(`Unexpected character at index ${i}`);
267
- } else if (void 0 === paramName) {
268
- if (-1 === end && 1 === tokenChars[code]) {
269
- if (-1 === start) start = i;
270
- } else if (0x20 === code || 0x09 === code) {
271
- if (-1 === end && -1 !== start) end = i;
272
- } else if (0x3b === code || 0x2c === code) {
273
- if (-1 === start) throw new SyntaxError(`Unexpected character at index ${i}`);
274
- if (-1 === end) end = i;
275
- push(params, header.slice(start, end), true);
276
- if (0x2c === code) {
277
- push(offers, extensionName, params);
278
- params = Object.create(null);
279
- extensionName = void 0;
280
- }
281
- start = end = -1;
282
- } else if (0x3d === code && -1 !== start && -1 === end) {
283
- paramName = header.slice(start, i);
284
- start = end = -1;
285
- } else throw new SyntaxError(`Unexpected character at index ${i}`);
286
- } else if (isEscaping) {
252
+ if (void 0 === extensionName) if (-1 === end && 1 === tokenChars[code]) {
253
+ if (-1 === start) start = i;
254
+ } else if (0 !== i && (0x20 === code || 0x09 === code)) {
255
+ if (-1 === end && -1 !== start) end = i;
256
+ } else if (0x3b === code || 0x2c === code) {
257
+ if (-1 === start) throw new SyntaxError(`Unexpected character at index ${i}`);
258
+ if (-1 === end) end = i;
259
+ const name = header.slice(start, end);
260
+ if (0x2c === code) {
261
+ push(offers, name, params);
262
+ params = Object.create(null);
263
+ } else extensionName = name;
264
+ start = end = -1;
265
+ } else throw new SyntaxError(`Unexpected character at index ${i}`);
266
+ else if (void 0 === paramName) if (-1 === end && 1 === tokenChars[code]) {
267
+ if (-1 === start) start = i;
268
+ } else if (0x20 === code || 0x09 === code) {
269
+ if (-1 === end && -1 !== start) end = i;
270
+ } else if (0x3b === code || 0x2c === code) {
271
+ if (-1 === start) throw new SyntaxError(`Unexpected character at index ${i}`);
272
+ if (-1 === end) end = i;
273
+ push(params, header.slice(start, end), true);
274
+ if (0x2c === code) {
275
+ push(offers, extensionName, params);
276
+ params = Object.create(null);
277
+ extensionName = void 0;
278
+ }
279
+ start = end = -1;
280
+ } else if (0x3d === code && -1 !== start && -1 === end) {
281
+ paramName = header.slice(start, i);
282
+ start = end = -1;
283
+ } else throw new SyntaxError(`Unexpected character at index ${i}`);
284
+ else if (isEscaping) {
287
285
  if (1 !== tokenChars[code]) throw new SyntaxError(`Unexpected character at index ${i}`);
288
286
  if (-1 === start) start = i;
289
287
  else if (!mustUnescape) mustUnescape = true;
290
288
  isEscaping = false;
291
- } else if (inQuotes) {
292
- if (1 === tokenChars[code]) {
293
- if (-1 === start) start = i;
294
- } else if (0x22 === code && -1 !== start) {
295
- inQuotes = false;
296
- end = i;
297
- } else if (0x5c === code) isEscaping = true;
298
- else throw new SyntaxError(`Unexpected character at index ${i}`);
299
- } else if (0x22 === code && 0x3d === header.charCodeAt(i - 1)) inQuotes = true;
289
+ } else if (inQuotes) if (1 === tokenChars[code]) {
290
+ if (-1 === start) start = i;
291
+ } else if (0x22 === code && -1 !== start) {
292
+ inQuotes = false;
293
+ end = i;
294
+ } else if (0x5c === code) isEscaping = true;
295
+ else throw new SyntaxError(`Unexpected character at index ${i}`);
296
+ else if (0x22 === code && 0x3d === header.charCodeAt(i - 1)) inQuotes = true;
300
297
  else if (-1 === end && 1 === tokenChars[code]) {
301
298
  if (-1 === start) start = i;
302
299
  } else if (-1 !== start && (0x20 === code || 0x09 === code)) {
@@ -352,7 +349,7 @@ var __webpack_modules__ = {
352
349
  parse
353
350
  };
354
351
  },
355
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/limiter.js": function(module) {
352
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/limiter.js" (module) {
356
353
  const kDone = Symbol('kDone');
357
354
  const kRun = Symbol('kRun');
358
355
  class Limiter {
@@ -380,7 +377,7 @@ var __webpack_modules__ = {
380
377
  }
381
378
  module.exports = Limiter;
382
379
  },
383
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/permessage-deflate.js": function(module, __unused_webpack_exports, __webpack_require__) {
380
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/permessage-deflate.js" (module, __unused_rspack_exports, __webpack_require__) {
384
381
  const zlib = __webpack_require__("zlib");
385
382
  const bufferUtil = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/buffer-util.js");
386
383
  const Limiter = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/limiter.js");
@@ -575,10 +572,7 @@ var __webpack_modules__ = {
575
572
  }
576
573
  function inflateOnData(chunk) {
577
574
  this[kTotalLength] += chunk.length;
578
- if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) {
579
- this[kBuffers].push(chunk);
580
- return;
581
- }
575
+ if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) return void this[kBuffers].push(chunk);
582
576
  this[kError] = new RangeError('Max payload size exceeded');
583
577
  this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
584
578
  this[kError][kStatusCode] = 1009;
@@ -587,15 +581,12 @@ var __webpack_modules__ = {
587
581
  }
588
582
  function inflateOnError(err) {
589
583
  this[kPerMessageDeflate]._inflate = null;
590
- if (this[kError]) {
591
- this[kCallback](this[kError]);
592
- return;
593
- }
584
+ if (this[kError]) return void this[kCallback](this[kError]);
594
585
  err[kStatusCode] = 1007;
595
586
  this[kCallback](err);
596
587
  }
597
588
  },
598
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/receiver.js": function(module, __unused_webpack_exports, __webpack_require__) {
589
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/receiver.js" (module, __unused_rspack_exports, __webpack_require__) {
599
590
  const { Writable } = __webpack_require__("stream");
600
591
  const PerMessageDeflate = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/permessage-deflate.js");
601
592
  const { BINARY_TYPES, EMPTY_BUFFER, kStatusCode, kWebSocket } = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js");
@@ -818,10 +809,7 @@ var __webpack_modules__ = {
818
809
  data = this.consume(this._payloadLength);
819
810
  if (this._masked && (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0) unmask(data, this._mask);
820
811
  }
821
- if (this._opcode > 0x07) {
822
- this.controlMessage(data, cb);
823
- return;
824
- }
812
+ if (this._opcode > 0x07) return void this.controlMessage(data, cb);
825
813
  if (this._compressed) {
826
814
  this._state = INFLATING;
827
815
  this.decompress(data, cb);
@@ -945,7 +933,7 @@ var __webpack_modules__ = {
945
933
  }
946
934
  module.exports = Receiver;
947
935
  },
948
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/sender.js": function(module, __unused_webpack_exports, __webpack_require__) {
936
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/sender.js" (module, __unused_rspack_exports, __webpack_require__) {
949
937
  const { Duplex } = __webpack_require__("stream");
950
938
  const { randomFillSync } = __webpack_require__("crypto");
951
939
  const PerMessageDeflate = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/permessage-deflate.js");
@@ -999,13 +987,12 @@ var __webpack_modules__ = {
999
987
  offset = 6;
1000
988
  }
1001
989
  let dataLength;
1002
- if ('string' == typeof data) {
1003
- if ((!options.mask || skipMasking) && void 0 !== options[kByteLength]) dataLength = options[kByteLength];
1004
- else {
1005
- data = Buffer.from(data);
1006
- dataLength = data.length;
1007
- }
1008
- } else {
990
+ if ('string' == typeof data) if ((!options.mask || skipMasking) && void 0 !== options[kByteLength]) dataLength = options[kByteLength];
991
+ else {
992
+ data = Buffer.from(data);
993
+ dataLength = data.length;
994
+ }
995
+ else {
1009
996
  dataLength = data.length;
1010
997
  merge = options.mask && options.readOnly && !skipMasking;
1011
998
  }
@@ -1054,19 +1041,18 @@ var __webpack_modules__ = {
1054
1041
  close(code, data, mask, cb) {
1055
1042
  let buf;
1056
1043
  if (void 0 === code) buf = EMPTY_BUFFER;
1057
- else if ('number' == typeof code && isValidStatusCode(code)) {
1058
- if (void 0 !== data && data.length) {
1059
- const length = Buffer.byteLength(data);
1060
- if (length > 123) throw new RangeError('The message must not be greater than 123 bytes');
1061
- buf = Buffer.allocUnsafe(2 + length);
1062
- buf.writeUInt16BE(code, 0);
1063
- if ('string' == typeof data) buf.write(data, 2);
1064
- else buf.set(data, 2);
1065
- } else {
1066
- buf = Buffer.allocUnsafe(2);
1067
- buf.writeUInt16BE(code, 0);
1068
- }
1069
- } else throw new TypeError('First argument must be a valid error code number');
1044
+ else if ('number' == typeof code && isValidStatusCode(code)) if (void 0 !== data && data.length) {
1045
+ const length = Buffer.byteLength(data);
1046
+ if (length > 123) throw new RangeError('The message must not be greater than 123 bytes');
1047
+ buf = Buffer.allocUnsafe(2 + length);
1048
+ buf.writeUInt16BE(code, 0);
1049
+ if ('string' == typeof data) buf.write(data, 2);
1050
+ else buf.set(data, 2);
1051
+ } else {
1052
+ buf = Buffer.allocUnsafe(2);
1053
+ buf.writeUInt16BE(code, 0);
1054
+ }
1055
+ else throw new TypeError('First argument must be a valid error code number');
1070
1056
  const options = {
1071
1057
  [kByteLength]: buf.length,
1072
1058
  fin: true,
@@ -1111,16 +1097,15 @@ var __webpack_modules__ = {
1111
1097
  readOnly,
1112
1098
  rsv1: false
1113
1099
  };
1114
- if (isBlob(data)) {
1115
- if (this._state !== DEFAULT) this.enqueue([
1116
- this.getBlobData,
1117
- data,
1118
- false,
1119
- options,
1120
- cb
1121
- ]);
1122
- else this.getBlobData(data, false, options, cb);
1123
- } else if (this._state !== DEFAULT) this.enqueue([
1100
+ if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
1101
+ this.getBlobData,
1102
+ data,
1103
+ false,
1104
+ options,
1105
+ cb
1106
+ ]);
1107
+ else this.getBlobData(data, false, options, cb);
1108
+ else if (this._state !== DEFAULT) this.enqueue([
1124
1109
  this.dispatch,
1125
1110
  data,
1126
1111
  false,
@@ -1154,16 +1139,15 @@ var __webpack_modules__ = {
1154
1139
  readOnly,
1155
1140
  rsv1: false
1156
1141
  };
1157
- if (isBlob(data)) {
1158
- if (this._state !== DEFAULT) this.enqueue([
1159
- this.getBlobData,
1160
- data,
1161
- false,
1162
- options,
1163
- cb
1164
- ]);
1165
- else this.getBlobData(data, false, options, cb);
1166
- } else if (this._state !== DEFAULT) this.enqueue([
1142
+ if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
1143
+ this.getBlobData,
1144
+ data,
1145
+ false,
1146
+ options,
1147
+ cb
1148
+ ]);
1149
+ else this.getBlobData(data, false, options, cb);
1150
+ else if (this._state !== DEFAULT) this.enqueue([
1167
1151
  this.dispatch,
1168
1152
  data,
1169
1153
  false,
@@ -1208,16 +1192,15 @@ var __webpack_modules__ = {
1208
1192
  readOnly,
1209
1193
  rsv1
1210
1194
  };
1211
- if (isBlob(data)) {
1212
- if (this._state !== DEFAULT) this.enqueue([
1213
- this.getBlobData,
1214
- data,
1215
- this._compress,
1216
- opts,
1217
- cb
1218
- ]);
1219
- else this.getBlobData(data, this._compress, opts, cb);
1220
- } else if (this._state !== DEFAULT) this.enqueue([
1195
+ if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
1196
+ this.getBlobData,
1197
+ data,
1198
+ this._compress,
1199
+ opts,
1200
+ cb
1201
+ ]);
1202
+ else this.getBlobData(data, this._compress, opts, cb);
1203
+ else if (this._state !== DEFAULT) this.enqueue([
1221
1204
  this.dispatch,
1222
1205
  data,
1223
1206
  this._compress,
@@ -1248,10 +1231,7 @@ var __webpack_modules__ = {
1248
1231
  });
1249
1232
  }
1250
1233
  dispatch(data, compress, options, cb) {
1251
- if (!compress) {
1252
- this.sendFrame(Sender.frame(data, options), cb);
1253
- return;
1254
- }
1234
+ if (!compress) return void this.sendFrame(Sender.frame(data, options), cb);
1255
1235
  const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1256
1236
  this._bufferedBytes += options[kByteLength];
1257
1237
  this._state = DEFLATING;
@@ -1302,7 +1282,7 @@ var __webpack_modules__ = {
1302
1282
  sender.onerror(err);
1303
1283
  }
1304
1284
  },
1305
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/stream.js": function(module, __unused_webpack_exports, __webpack_require__) {
1285
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/stream.js" (module, __unused_rspack_exports, __webpack_require__) {
1306
1286
  __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js");
1307
1287
  const { Duplex } = __webpack_require__("stream");
1308
1288
  function emitClose(stream) {
@@ -1356,12 +1336,9 @@ var __webpack_modules__ = {
1356
1336
  if (terminateOnDestroy) ws.terminate();
1357
1337
  };
1358
1338
  duplex._final = function(callback) {
1359
- if (ws.readyState === ws.CONNECTING) {
1360
- ws.once('open', function() {
1361
- duplex._final(callback);
1362
- });
1363
- return;
1364
- }
1339
+ if (ws.readyState === ws.CONNECTING) return void ws.once('open', function() {
1340
+ duplex._final(callback);
1341
+ });
1365
1342
  if (null === ws._socket) return;
1366
1343
  if (ws._socket._writableState.finished) {
1367
1344
  callback();
@@ -1377,12 +1354,9 @@ var __webpack_modules__ = {
1377
1354
  if (ws.isPaused) ws.resume();
1378
1355
  };
1379
1356
  duplex._write = function(chunk, encoding, callback) {
1380
- if (ws.readyState === ws.CONNECTING) {
1381
- ws.once('open', function() {
1382
- duplex._write(chunk, encoding, callback);
1383
- });
1384
- return;
1385
- }
1357
+ if (ws.readyState === ws.CONNECTING) return void ws.once('open', function() {
1358
+ duplex._write(chunk, encoding, callback);
1359
+ });
1386
1360
  ws.send(chunk, callback);
1387
1361
  };
1388
1362
  duplex.on('end', duplexOnEnd);
@@ -1391,7 +1365,7 @@ var __webpack_modules__ = {
1391
1365
  }
1392
1366
  module.exports = createWebSocketStream;
1393
1367
  },
1394
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/subprotocol.js": function(module, __unused_webpack_exports, __webpack_require__) {
1368
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/subprotocol.js" (module, __unused_rspack_exports, __webpack_require__) {
1395
1369
  const { tokenChars } = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/validation.js");
1396
1370
  function parse(header) {
1397
1371
  const protocols = new Set();
@@ -1423,7 +1397,7 @@ var __webpack_modules__ = {
1423
1397
  parse
1424
1398
  };
1425
1399
  },
1426
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/validation.js": function(module, __unused_webpack_exports, __webpack_require__) {
1400
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/validation.js" (module, __unused_rspack_exports, __webpack_require__) {
1427
1401
  const { isUtf8 } = __webpack_require__("buffer");
1428
1402
  const { hasBlob } = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js");
1429
1403
  const tokenChars = [
@@ -1599,7 +1573,7 @@ var __webpack_modules__ = {
1599
1573
  };
1600
1574
  } catch (e) {}
1601
1575
  },
1602
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js": function(module, __unused_webpack_exports, __webpack_require__) {
1576
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js" (module, __unused_rspack_exports, __webpack_require__) {
1603
1577
  const EventEmitter = __webpack_require__("events");
1604
1578
  const http = __webpack_require__("http");
1605
1579
  const { Duplex } = __webpack_require__("stream");
@@ -1686,10 +1660,9 @@ var __webpack_modules__ = {
1686
1660
  this._removeListeners();
1687
1661
  this._removeListeners = this._server = null;
1688
1662
  }
1689
- if (this.clients) {
1690
- if (this.clients.size) this._shouldEmitClose = true;
1691
- else process.nextTick(emitClose, this);
1692
- } else process.nextTick(emitClose, this);
1663
+ if (this.clients) if (this.clients.size) this._shouldEmitClose = true;
1664
+ else process.nextTick(emitClose, this);
1665
+ else process.nextTick(emitClose, this);
1693
1666
  } else {
1694
1667
  const server = this._server;
1695
1668
  this._removeListeners();
@@ -1734,10 +1707,7 @@ var __webpack_modules__ = {
1734
1707
  });
1735
1708
  return;
1736
1709
  }
1737
- if (!this.shouldHandle(req)) {
1738
- abortHandshake(socket, 400);
1739
- return;
1740
- }
1710
+ if (!this.shouldHandle(req)) return void abortHandshake(socket, 400);
1741
1711
  const secWebSocketProtocol = req.headers['sec-websocket-protocol'];
1742
1712
  let protocols = new Set();
1743
1713
  if (void 0 !== secWebSocketProtocol) try {
@@ -1769,13 +1739,10 @@ var __webpack_modules__ = {
1769
1739
  secure: !!(req.socket.authorized || req.socket.encrypted),
1770
1740
  req
1771
1741
  };
1772
- if (2 === this.options.verifyClient.length) {
1773
- this.options.verifyClient(info, (verified, code, message, headers)=>{
1774
- if (!verified) return abortHandshake(socket, code || 401, message, headers);
1775
- this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
1776
- });
1777
- return;
1778
- }
1742
+ if (2 === this.options.verifyClient.length) return void this.options.verifyClient(info, (verified, code, message, headers)=>{
1743
+ if (!verified) return abortHandshake(socket, code || 401, message, headers);
1744
+ this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
1745
+ });
1779
1746
  if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
1780
1747
  }
1781
1748
  this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
@@ -1860,7 +1827,7 @@ var __webpack_modules__ = {
1860
1827
  } else abortHandshake(socket, code, message, headers);
1861
1828
  }
1862
1829
  },
1863
- "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js": function(module, __unused_webpack_exports, __webpack_require__) {
1830
+ "./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js" (module, __unused_rspack_exports, __webpack_require__) {
1864
1831
  const EventEmitter = __webpack_require__("events");
1865
1832
  const https = __webpack_require__("https");
1866
1833
  const http = __webpack_require__("http");
@@ -1911,14 +1878,12 @@ var __webpack_modules__ = {
1911
1878
  this._isServer = false;
1912
1879
  this._redirects = 0;
1913
1880
  if (void 0 === protocols) protocols = [];
1914
- else if (!Array.isArray(protocols)) {
1915
- if ('object' == typeof protocols && null !== protocols) {
1916
- options = protocols;
1917
- protocols = [];
1918
- } else protocols = [
1919
- protocols
1920
- ];
1921
- }
1881
+ else if (!Array.isArray(protocols)) if ('object' == typeof protocols && null !== protocols) {
1882
+ options = protocols;
1883
+ protocols = [];
1884
+ } else protocols = [
1885
+ protocols
1886
+ ];
1922
1887
  initAsClient(this, address, protocols, options);
1923
1888
  } else {
1924
1889
  this._autoPong = options.autoPong;
@@ -2043,10 +2008,7 @@ var __webpack_modules__ = {
2043
2008
  mask = void 0;
2044
2009
  }
2045
2010
  if ('number' == typeof data) data = data.toString();
2046
- if (this.readyState !== WebSocket.OPEN) {
2047
- sendAfterClose(this, data, cb);
2048
- return;
2049
- }
2011
+ if (this.readyState !== WebSocket.OPEN) return void sendAfterClose(this, data, cb);
2050
2012
  if (void 0 === mask) mask = !this._isServer;
2051
2013
  this._sender.ping(data || EMPTY_BUFFER, mask, cb);
2052
2014
  }
@@ -2060,10 +2022,7 @@ var __webpack_modules__ = {
2060
2022
  mask = void 0;
2061
2023
  }
2062
2024
  if ('number' == typeof data) data = data.toString();
2063
- if (this.readyState !== WebSocket.OPEN) {
2064
- sendAfterClose(this, data, cb);
2065
- return;
2066
- }
2025
+ if (this.readyState !== WebSocket.OPEN) return void sendAfterClose(this, data, cb);
2067
2026
  if (void 0 === mask) mask = !this._isServer;
2068
2027
  this._sender.pong(data || EMPTY_BUFFER, mask, cb);
2069
2028
  }
@@ -2079,10 +2038,7 @@ var __webpack_modules__ = {
2079
2038
  options = {};
2080
2039
  }
2081
2040
  if ('number' == typeof data) data = data.toString();
2082
- if (this.readyState !== WebSocket.OPEN) {
2083
- sendAfterClose(this, data, cb);
2084
- return;
2085
- }
2041
+ if (this.readyState !== WebSocket.OPEN) return void sendAfterClose(this, data, cb);
2086
2042
  const opts = {
2087
2043
  binary: 'string' != typeof data,
2088
2044
  mask: !this._isServer,
@@ -2221,9 +2177,8 @@ var __webpack_modules__ = {
2221
2177
  } else invalidUrlMessage = 'The URL\'s protocol must be one of "ws:", "wss:", "http:", "https:", or "ws+unix:"';
2222
2178
  if (invalidUrlMessage) {
2223
2179
  const err = new SyntaxError(invalidUrlMessage);
2224
- if (0 === websocket._redirects) throw err;
2225
- emitErrorAndClose(websocket, err);
2226
- return;
2180
+ if (0 !== websocket._redirects) return void emitErrorAndClose(websocket, err);
2181
+ throw err;
2227
2182
  }
2228
2183
  const defaultPort = isSecure ? 443 : 80;
2229
2184
  const key = randomBytes(16).toString('base64');
@@ -2256,10 +2211,8 @@ var __webpack_modules__ = {
2256
2211
  }
2257
2212
  opts.headers['Sec-WebSocket-Protocol'] = protocols.join(',');
2258
2213
  }
2259
- if (opts.origin) {
2260
- if (opts.protocolVersion < 13) opts.headers['Sec-WebSocket-Origin'] = opts.origin;
2261
- else opts.headers.Origin = opts.origin;
2262
- }
2214
+ if (opts.origin) if (opts.protocolVersion < 13) opts.headers['Sec-WebSocket-Origin'] = opts.origin;
2215
+ else opts.headers.Origin = opts.origin;
2263
2216
  if (parsedUrl.username || parsedUrl.password) opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
2264
2217
  if (isIpcUrl) {
2265
2218
  const parts = opts.path.split(':');
@@ -2303,10 +2256,7 @@ var __webpack_modules__ = {
2303
2256
  const location = res.headers.location;
2304
2257
  const statusCode = res.statusCode;
2305
2258
  if (location && opts.followRedirects && statusCode >= 300 && statusCode < 400) {
2306
- if (++websocket._redirects > opts.maxRedirects) {
2307
- abortHandshake(websocket, req, 'Maximum redirects exceeded');
2308
- return;
2309
- }
2259
+ if (++websocket._redirects > opts.maxRedirects) return void abortHandshake(websocket, req, 'Maximum redirects exceeded');
2310
2260
  req.abort();
2311
2261
  let addr;
2312
2262
  try {
@@ -2324,26 +2274,16 @@ var __webpack_modules__ = {
2324
2274
  if (websocket.readyState !== WebSocket.CONNECTING) return;
2325
2275
  req = websocket._req = null;
2326
2276
  const upgrade = res.headers.upgrade;
2327
- if (void 0 === upgrade || 'websocket' !== upgrade.toLowerCase()) {
2328
- abortHandshake(websocket, socket, 'Invalid Upgrade header');
2329
- return;
2330
- }
2277
+ if (void 0 === upgrade || 'websocket' !== upgrade.toLowerCase()) return void abortHandshake(websocket, socket, 'Invalid Upgrade header');
2331
2278
  const digest = createHash('sha1').update(key + GUID).digest('base64');
2332
- if (res.headers['sec-websocket-accept'] !== digest) {
2333
- abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');
2334
- return;
2335
- }
2279
+ if (res.headers['sec-websocket-accept'] !== digest) return void abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');
2336
2280
  const serverProt = res.headers['sec-websocket-protocol'];
2337
2281
  let protError;
2338
- if (void 0 !== serverProt) {
2339
- if (protocolSet.size) {
2340
- if (!protocolSet.has(serverProt)) protError = 'Server sent an invalid subprotocol';
2341
- } else protError = 'Server sent a subprotocol but none was requested';
2342
- } else if (protocolSet.size) protError = 'Server sent no subprotocol';
2343
- if (protError) {
2344
- abortHandshake(websocket, socket, protError);
2345
- return;
2346
- }
2282
+ if (void 0 !== serverProt) if (protocolSet.size) {
2283
+ if (!protocolSet.has(serverProt)) protError = 'Server sent an invalid subprotocol';
2284
+ } else protError = 'Server sent a subprotocol but none was requested';
2285
+ else if (protocolSet.size) protError = 'Server sent no subprotocol';
2286
+ if (protError) return void abortHandshake(websocket, socket, protError);
2347
2287
  if (serverProt) websocket._protocol = serverProt;
2348
2288
  const secWebSocketExtensions = res.headers['sec-websocket-extensions'];
2349
2289
  if (void 0 !== secWebSocketExtensions) {
@@ -2524,38 +2464,35 @@ var __webpack_modules__ = {
2524
2464
  }
2525
2465
  }
2526
2466
  },
2527
- buffer: function(module) {
2467
+ buffer (module) {
2528
2468
  module.exports = require("buffer");
2529
2469
  },
2530
- crypto: function(module) {
2470
+ crypto (module) {
2531
2471
  module.exports = require("crypto");
2532
2472
  },
2533
- events: function(module) {
2473
+ events (module) {
2534
2474
  module.exports = require("events");
2535
2475
  },
2536
- http: function(module) {
2476
+ http (module) {
2537
2477
  module.exports = require("http");
2538
2478
  },
2539
- https: function(module) {
2479
+ https (module) {
2540
2480
  module.exports = require("https");
2541
2481
  },
2542
- net: function(module) {
2482
+ net (module) {
2543
2483
  module.exports = require("net");
2544
2484
  },
2545
- stream: function(module) {
2485
+ stream (module) {
2546
2486
  module.exports = require("stream");
2547
2487
  },
2548
- tls: function(module) {
2488
+ tls (module) {
2549
2489
  module.exports = require("tls");
2550
2490
  },
2551
- url: function(module) {
2491
+ url (module) {
2552
2492
  module.exports = require("url");
2553
2493
  },
2554
- zlib: function(module) {
2494
+ zlib (module) {
2555
2495
  module.exports = require("zlib");
2556
- },
2557
- "uWebSockets.js": function(module) {
2558
- module.exports = import("uWebSockets.js");
2559
2496
  }
2560
2497
  };
2561
2498
  var __webpack_module_cache__ = {};
@@ -2569,12 +2506,8 @@ function __webpack_require__(moduleId) {
2569
2506
  return module.exports;
2570
2507
  }
2571
2508
  (()=>{
2572
- __webpack_require__.n = function(module) {
2573
- var getter = module && module.__esModule ? function() {
2574
- return module['default'];
2575
- } : function() {
2576
- return module;
2577
- };
2509
+ __webpack_require__.n = (module)=>{
2510
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
2578
2511
  __webpack_require__.d(getter, {
2579
2512
  a: getter
2580
2513
  });
@@ -2582,7 +2515,7 @@ function __webpack_require__(moduleId) {
2582
2515
  };
2583
2516
  })();
2584
2517
  (()=>{
2585
- __webpack_require__.d = function(exports1, definition) {
2518
+ __webpack_require__.d = (exports1, definition)=>{
2586
2519
  for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
2587
2520
  enumerable: true,
2588
2521
  get: definition[key]
@@ -2590,9 +2523,7 @@ function __webpack_require__(moduleId) {
2590
2523
  };
2591
2524
  })();
2592
2525
  (()=>{
2593
- __webpack_require__.o = function(obj, prop) {
2594
- return Object.prototype.hasOwnProperty.call(obj, prop);
2595
- };
2526
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
2596
2527
  })();
2597
2528
  var __webpack_exports__ = {};
2598
2529
  (()=>{
@@ -2640,19 +2571,20 @@ var __webpack_exports__ = {};
2640
2571
  }
2641
2572
  async start(service) {
2642
2573
  let lock = this.startingLocks.get(service.name);
2643
- if (lock) try {
2574
+ while(lock)try {
2644
2575
  await lock;
2645
2576
  const isRunning = await this.isRunning(service);
2646
- if (!isRunning) {
2647
- this.startingLocks.delete(service.name);
2648
- return this.start(service);
2649
- }
2650
- return;
2577
+ if (isRunning) return;
2578
+ this.startingLocks.delete(service.name);
2579
+ break;
2651
2580
  } catch {
2652
2581
  this.startingLocks.delete(service.name);
2582
+ break;
2653
2583
  }
2654
2584
  lock = (async ()=>{
2655
2585
  try {
2586
+ const alreadyRunning = await this.isRunning(service);
2587
+ if (alreadyRunning) return void console.log(`[${service.name}] 服务已在运行,跳过启动`);
2656
2588
  console.log(`[${service.name}] 正在启动...`);
2657
2589
  const result = await this.executor.execute(service.commands.start, {
2658
2590
  cwd: service.commands.cwd,
@@ -2683,29 +2615,375 @@ var __webpack_exports__ = {};
2683
2615
  service._state.status = 'offline';
2684
2616
  }
2685
2617
  }
2618
+ const external_uWebSockets_js_namespaceObject = require("uWebSockets.js");
2619
+ var external_uWebSockets_js_default = /*#__PURE__*/ __webpack_require__.n(external_uWebSockets_js_namespaceObject);
2686
2620
  const external_node_net_namespaceObject = require("node:net");
2687
2621
  var external_node_net_default = /*#__PURE__*/ __webpack_require__.n(external_node_net_namespaceObject);
2622
+ const external_node_url_namespaceObject = require("node:url");
2688
2623
  const external_node_http_namespaceObject = require("node:http");
2689
2624
  var external_node_http_default = /*#__PURE__*/ __webpack_require__.n(external_node_http_namespaceObject);
2690
- const external_node_https_namespaceObject = require("node:https");
2691
- var external_node_https_default = /*#__PURE__*/ __webpack_require__.n(external_node_https_namespaceObject);
2692
- const external_node_url_namespaceObject = require("node:url");
2693
2625
  __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/stream.js");
2694
2626
  __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/receiver.js");
2695
2627
  __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/sender.js");
2696
2628
  var websocket = __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js");
2697
2629
  __webpack_require__("./node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js");
2698
2630
  const wrapper = websocket;
2631
+ class AdminApiHandler {
2632
+ config;
2633
+ logger;
2634
+ hostnameRoutes;
2635
+ portRoutes;
2636
+ serviceManager;
2637
+ constructor(config, logger, hostnameRoutes, portRoutes, serviceManager){
2638
+ this.config = config;
2639
+ this.logger = logger;
2640
+ this.hostnameRoutes = hostnameRoutes;
2641
+ this.portRoutes = portRoutes;
2642
+ this.serviceManager = serviceManager;
2643
+ }
2644
+ isIpAllowed(ip) {
2645
+ if (!this.config.adminApi?.allowedIps || 0 === this.config.adminApi.allowedIps.length) return true;
2646
+ return this.config.adminApi.allowedIps.includes(ip);
2647
+ }
2648
+ isAuthenticated(authHeader) {
2649
+ if (!this.config.adminApi?.authToken) return true;
2650
+ if (!authHeader) return false;
2651
+ const token = authHeader.replace('Bearer ', '');
2652
+ return token === this.config.adminApi.authToken;
2653
+ }
2654
+ getServiceUptime(service) {
2655
+ if ('online' === service._state.status && service._state.startTime) return service._state.totalUptime + (Date.now() - service._state.startTime);
2656
+ return service._state.totalUptime;
2657
+ }
2658
+ findServiceMapping(serviceName) {
2659
+ for (const mapping of this.hostnameRoutes.values())if (mapping.service.name === serviceName) return mapping;
2660
+ for (const mapping of this.portRoutes.values())if (mapping.service.name === serviceName) return mapping;
2661
+ }
2662
+ handleAdminApi(res, req) {
2663
+ const ip = req.getHeader('x-forwarded-for')?.split(',')[0]?.trim() || req.getHeader('cf-connecting-ip') || '127.0.0.1';
2664
+ if (!this.isIpAllowed(ip)) {
2665
+ res.cork(()=>{
2666
+ res.writeStatus('403 Forbidden');
2667
+ res.end('Forbidden');
2668
+ });
2669
+ this.logger.warn({
2670
+ msg: `🚫 [Admin API] 拒绝访问: ${ip}`
2671
+ });
2672
+ return;
2673
+ }
2674
+ const authHeader = req.getHeader('authorization');
2675
+ if (!this.isAuthenticated(authHeader)) return void res.cork(()=>{
2676
+ res.writeStatus('401 Unauthorized');
2677
+ res.writeHeader('WWW-Authenticate', 'Bearer');
2678
+ res.end('Unauthorized');
2679
+ });
2680
+ const url = req.getUrl();
2681
+ const method = req.getMethod();
2682
+ if ('/_dynapm/api/services' === url && 'get' === method.toLowerCase()) this.getServicesList(res);
2683
+ else if (url.startsWith('/_dynapm/api/services/') && 'get' === method.toLowerCase()) {
2684
+ const serviceName = url.split('/')[4];
2685
+ this.getServiceDetail(res, serviceName);
2686
+ } else if (url.endsWith('/stop') && 'post' === method.toLowerCase()) {
2687
+ const parts = url.split('/');
2688
+ const serviceName = parts[4];
2689
+ this.stopService(res, serviceName);
2690
+ } else if (url.endsWith('/start') && 'post' === method.toLowerCase()) {
2691
+ const parts = url.split('/');
2692
+ const serviceName = parts[4];
2693
+ this.startService(res, serviceName);
2694
+ } else if ('/_dynapm/api/events' === url && 'get' === method.toLowerCase()) this.handleEventStream(res);
2695
+ else res.cork(()=>{
2696
+ res.writeStatus('404 Not Found');
2697
+ res.end('Not Found');
2698
+ });
2699
+ }
2700
+ getServicesList(res) {
2701
+ const serviceMap = new Map();
2702
+ for (const mapping of this.hostnameRoutes.values())serviceMap.set(mapping.service.name, mapping.service);
2703
+ for (const mapping of this.portRoutes.values())serviceMap.set(mapping.service.name, mapping.service);
2704
+ const services = Array.from(serviceMap.values()).map((service)=>({
2705
+ name: service.name,
2706
+ base: service.base,
2707
+ status: service._state.status,
2708
+ uptime: this.getServiceUptime(service),
2709
+ lastAccessTime: service._state.lastAccessTime,
2710
+ activeConnections: service._state.activeConnections,
2711
+ idleTimeout: service.idleTimeout,
2712
+ proxyOnly: service.proxyOnly || false,
2713
+ pid: service._state.pid
2714
+ }));
2715
+ res.cork(()=>{
2716
+ res.writeHeader('Content-Type', 'application/json');
2717
+ res.end(JSON.stringify({
2718
+ services
2719
+ }));
2720
+ });
2721
+ }
2722
+ getServiceDetail(res, serviceName) {
2723
+ const mapping = this.findServiceMapping(serviceName);
2724
+ this.logger.info({
2725
+ msg: `🔍 [Admin API] 查找服务: ${serviceName}, 找到: ${mapping?.service.name || 'null'}`
2726
+ });
2727
+ if (!mapping) return void res.cork(()=>{
2728
+ res.writeStatus('404 Not Found');
2729
+ res.end(JSON.stringify({
2730
+ error: 'Service not found'
2731
+ }));
2732
+ });
2733
+ const service = mapping.service;
2734
+ const detail = {
2735
+ name: service.name,
2736
+ base: service.base,
2737
+ status: service._state.status,
2738
+ uptime: this.getServiceUptime(service),
2739
+ lastAccessTime: service._state.lastAccessTime,
2740
+ activeConnections: service._state.activeConnections,
2741
+ idleTimeout: service.idleTimeout,
2742
+ startTimeout: service.startTimeout,
2743
+ proxyOnly: service.proxyOnly || false,
2744
+ pid: service._state.pid,
2745
+ healthCheck: service.healthCheck || {
2746
+ type: 'tcp'
2747
+ },
2748
+ startCount: service._state.startCount,
2749
+ totalUptime: service._state.totalUptime
2750
+ };
2751
+ res.cork(()=>{
2752
+ res.writeHeader('Content-Type', 'application/json');
2753
+ res.end(JSON.stringify(detail));
2754
+ });
2755
+ }
2756
+ async stopService(res, serviceName) {
2757
+ let aborted = false;
2758
+ res.onAborted(()=>{
2759
+ aborted = true;
2760
+ });
2761
+ const mapping = this.findServiceMapping(serviceName);
2762
+ if (!mapping) {
2763
+ if (!aborted) res.cork(()=>{
2764
+ res.writeStatus('404 Not Found');
2765
+ res.end(JSON.stringify({
2766
+ error: 'Service not found'
2767
+ }));
2768
+ });
2769
+ return;
2770
+ }
2771
+ const service = mapping.service;
2772
+ if ('online' !== service._state.status) {
2773
+ if (!aborted) res.cork(()=>{
2774
+ res.writeStatus('400 Bad Request');
2775
+ res.end(JSON.stringify({
2776
+ error: 'Service is not online'
2777
+ }));
2778
+ });
2779
+ return;
2780
+ }
2781
+ try {
2782
+ service._state.status = 'stopping';
2783
+ if (service._state.startTime) {
2784
+ service._state.totalUptime += Date.now() - service._state.startTime;
2785
+ service._state.startTime = void 0;
2786
+ }
2787
+ await this.serviceManager.stop(service);
2788
+ service._state.status = 'offline';
2789
+ if (!aborted) res.cork(()=>{
2790
+ res.writeHeader('Content-Type', 'application/json');
2791
+ res.end(JSON.stringify({
2792
+ success: true,
2793
+ message: `服务 ${service.name} 已停止`
2794
+ }));
2795
+ });
2796
+ } catch (error) {
2797
+ const message = error instanceof Error ? error.message : String(error);
2798
+ service._state.status = 'online';
2799
+ if (!aborted) res.cork(()=>{
2800
+ res.writeStatus('500 Internal Server Error');
2801
+ res.end(JSON.stringify({
2802
+ error: message
2803
+ }));
2804
+ });
2805
+ }
2806
+ }
2807
+ async startService(res, serviceName) {
2808
+ let aborted = false;
2809
+ res.onAborted(()=>{
2810
+ aborted = true;
2811
+ });
2812
+ const mapping = this.findServiceMapping(serviceName);
2813
+ if (!mapping) {
2814
+ if (!aborted) res.cork(()=>{
2815
+ res.writeStatus('404 Not Found');
2816
+ res.end(JSON.stringify({
2817
+ error: 'Service not found'
2818
+ }));
2819
+ });
2820
+ return;
2821
+ }
2822
+ const service = mapping.service;
2823
+ if ('online' === service._state.status || 'starting' === service._state.status) {
2824
+ if (!aborted) res.cork(()=>{
2825
+ res.writeStatus('400 Bad Request');
2826
+ res.end(JSON.stringify({
2827
+ error: 'Service is already running or starting'
2828
+ }));
2829
+ });
2830
+ return;
2831
+ }
2832
+ try {
2833
+ service._state.status = 'starting';
2834
+ this.serviceManager.start(service).catch((err)=>{
2835
+ this.logger.error({
2836
+ msg: `❌ [${service.name}] 启动失败`,
2837
+ error: err.message
2838
+ });
2839
+ service._state.status = 'offline';
2840
+ });
2841
+ const waitStartTime = Date.now();
2842
+ let isReady = false;
2843
+ while(Date.now() - waitStartTime < service.startTimeout){
2844
+ isReady = await checkTcpPort(service.base);
2845
+ if (isReady) break;
2846
+ await new Promise((resolve)=>setTimeout(resolve, 100));
2847
+ }
2848
+ if (!isReady) {
2849
+ service._state.status = 'offline';
2850
+ if (!aborted) res.cork(()=>{
2851
+ res.writeStatus('503 Service Unavailable');
2852
+ res.end(JSON.stringify({
2853
+ error: 'Service start timeout'
2854
+ }));
2855
+ });
2856
+ return;
2857
+ }
2858
+ service._state.status = 'online';
2859
+ service._state.startTime = Date.now();
2860
+ service._state.startCount++;
2861
+ if (!aborted) res.cork(()=>{
2862
+ res.writeHeader('Content-Type', 'application/json');
2863
+ res.end(JSON.stringify({
2864
+ success: true,
2865
+ message: `服务 ${service.name} 已启动`
2866
+ }));
2867
+ });
2868
+ } catch (error) {
2869
+ const message = error instanceof Error ? error.message : String(error);
2870
+ service._state.status = 'offline';
2871
+ if (!aborted) res.cork(()=>{
2872
+ res.writeStatus('500 Internal Server Error');
2873
+ res.end(JSON.stringify({
2874
+ error: message
2875
+ }));
2876
+ });
2877
+ }
2878
+ }
2879
+ handleEventStream(res) {
2880
+ res.cork(()=>{
2881
+ res.writeStatus('200 OK');
2882
+ res.writeHeader('Content-Type', 'text/event-stream');
2883
+ res.writeHeader('Cache-Control', 'no-cache');
2884
+ res.writeHeader('Connection', 'keep-alive');
2885
+ res.writeHeader('X-Accel-Buffering', 'no');
2886
+ });
2887
+ res.cork(()=>{
2888
+ res.end(`event: connected\ndata: {"timestamp":${Date.now()}}\n\n`);
2889
+ });
2890
+ }
2891
+ }
2892
+ function checkTcpPort(url) {
2893
+ const parsed = new external_node_url_namespaceObject.URL(url);
2894
+ const host = parsed.hostname;
2895
+ const port = parseInt(parsed.port || ('https:' === parsed.protocol ? '443' : '80'));
2896
+ return new Promise((resolve)=>{
2897
+ const socket = external_node_net_default().createConnection({
2898
+ host,
2899
+ port,
2900
+ timeout: 100
2901
+ }, ()=>{
2902
+ socket.destroy();
2903
+ resolve(true);
2904
+ });
2905
+ socket.on('error', ()=>{
2906
+ socket.destroy();
2907
+ resolve(false);
2908
+ });
2909
+ socket.on('timeout', ()=>{
2910
+ socket.destroy();
2911
+ resolve(false);
2912
+ });
2913
+ });
2914
+ }
2699
2915
  function formatTime(ms) {
2700
2916
  if (ms < 1000) return `${ms}ms`;
2701
2917
  return `${(ms / 1000).toFixed(2)}s`;
2702
2918
  }
2919
+ function collectRequestBody(res) {
2920
+ return new Promise((resolve)=>{
2921
+ const chunks = [];
2922
+ let totalSize = 0;
2923
+ let resolved = false;
2924
+ res.onData((ab, isLast)=>{
2925
+ if (resolved) return;
2926
+ const chunk = Buffer.alloc(ab.byteLength);
2927
+ Buffer.from(ab).copy(chunk);
2928
+ totalSize += chunk.length;
2929
+ if (totalSize > GatewayConstants.MAX_REQUEST_BODY_SIZE) {
2930
+ resolved = true;
2931
+ resolve(Buffer.concat(chunks));
2932
+ return;
2933
+ }
2934
+ chunks.push(chunk);
2935
+ if (isLast) {
2936
+ resolved = true;
2937
+ resolve(Buffer.concat(chunks));
2938
+ }
2939
+ });
2940
+ res.onAborted(()=>{
2941
+ if (resolved) return;
2942
+ resolved = true;
2943
+ resolve(Buffer.concat(chunks));
2944
+ });
2945
+ });
2946
+ }
2947
+ function getStatusMessage(statusCode) {
2948
+ const messages = {
2949
+ 200: 'OK',
2950
+ 201: 'Created',
2951
+ 204: 'No Content',
2952
+ 301: 'Moved Permanently',
2953
+ 302: 'Found',
2954
+ 304: 'Not Modified',
2955
+ 400: 'Bad Request',
2956
+ 401: 'Unauthorized',
2957
+ 403: 'Forbidden',
2958
+ 404: 'Not Found',
2959
+ 405: 'Method Not Allowed',
2960
+ 409: 'Conflict',
2961
+ 500: 'Internal Server Error',
2962
+ 502: 'Bad Gateway',
2963
+ 503: 'Service Unavailable'
2964
+ };
2965
+ return messages[statusCode] || 'Unknown';
2966
+ }
2703
2967
  const GatewayConstants = {
2704
2968
  IDLE_CHECK_INTERVAL: 3000,
2705
2969
  TCP_CHECK_TIMEOUT: 100,
2706
- BACKEND_READY_CHECK_DELAY: 50
2970
+ BACKEND_READY_CHECK_DELAY: 50,
2971
+ SKIP_RESPONSE_HEADERS: new Set([
2972
+ 'connection',
2973
+ 'transfer-encoding',
2974
+ 'keep-alive',
2975
+ 'content-length'
2976
+ ]),
2977
+ CRLF_REGEX: /[\r\n]/g,
2978
+ SKIP_REQUEST_HEADERS: new Set([
2979
+ 'connection',
2980
+ 'keep-alive',
2981
+ 'content-length'
2982
+ ]),
2983
+ MAX_REQUEST_BODY_SIZE: 10485760,
2984
+ MAX_WS_MESSAGE_QUEUE_SIZE: 1000
2707
2985
  };
2708
- function checkTcpPort(url) {
2986
+ function gateway_checkTcpPort(url) {
2709
2987
  const parsed = new external_node_url_namespaceObject.URL(url);
2710
2988
  const host = parsed.hostname;
2711
2989
  const port = parseInt(parsed.port || ('https:' === parsed.protocol ? '443' : '80'));
@@ -2730,395 +3008,652 @@ var __webpack_exports__ = {};
2730
3008
  }
2731
3009
  class Gateway {
2732
3010
  config;
2733
- serviceManager;
2734
- services;
3011
+ serviceManager = new ServiceManager();
3012
+ hostnameRoutes = new Map();
3013
+ portRoutes = new Map();
2735
3014
  logger;
3015
+ logging;
3016
+ adminApi;
3017
+ startingPromises = new Map();
2736
3018
  constructor(config, logger){
2737
3019
  this.config = config;
2738
- this.serviceManager = new ServiceManager();
2739
- this.services = new Map();
2740
3020
  this.logger = logger;
3021
+ this.logging = {
3022
+ enableRequestLog: config.logging?.enableRequestLog ?? false,
3023
+ enableWebSocketLog: config.logging?.enableWebSocketLog ?? false,
3024
+ enablePerformanceLog: config.logging?.enablePerformanceLog ?? false
3025
+ };
3026
+ this.adminApi = new AdminApiHandler(config, logger, this.hostnameRoutes, this.portRoutes, this.serviceManager);
2741
3027
  this.initServices();
2742
3028
  this.initIdleChecker();
2743
3029
  }
2744
3030
  initServices() {
2745
- for (const [hostname, service] of Object.entries(this.config.services)){
3031
+ if (!this.config.services) return;
3032
+ console.log('[DynaPM] 初始化服务...');
3033
+ console.log('[DynaPM] 服务数量:', Object.keys(this.config.services).length);
3034
+ for (const service of Object.values(this.config.services)){
2746
3035
  service._state = {
2747
3036
  status: service.proxyOnly ? 'online' : 'offline',
2748
3037
  lastAccessTime: Date.now(),
2749
- activeConnections: 0
3038
+ activeConnections: 0,
3039
+ startCount: 0,
3040
+ totalUptime: 0
2750
3041
  };
2751
- this.services.set(hostname, service);
3042
+ const routes = service.routes || [];
3043
+ if (0 === routes.length) {
3044
+ console.warn(`[DynaPM] ⚠️ [${service.name}] 没有配置路由`);
3045
+ continue;
3046
+ }
3047
+ console.log(`[DynaPM] ✅ [${service.name}] 配置了 ${routes.length} 个路由:`);
3048
+ for (const route of routes){
3049
+ const targetUrl = new external_node_url_namespaceObject.URL(route.target);
3050
+ const mapping = {
3051
+ service,
3052
+ target: route.target,
3053
+ targetUrl
3054
+ };
3055
+ if ('host' === route.type) {
3056
+ const hostname = route.value;
3057
+ this.hostnameRoutes.set(hostname, mapping);
3058
+ console.log(`[DynaPM] └─ hostname: ${hostname} -> ${route.target}`);
3059
+ } else if ('port' === route.type) {
3060
+ const port = route.value;
3061
+ this.portRoutes.set(port, mapping);
3062
+ console.log(`[DynaPM] └─ port: ${port} -> ${route.target}`);
3063
+ }
3064
+ }
2752
3065
  }
3066
+ const hostnameCount = this.hostnameRoutes.size;
3067
+ const portCount = this.portRoutes.size;
3068
+ console.log(`[DynaPM] 📊 共配置 ${hostnameCount} 个 hostname 映射, ${portCount} 个端口绑定`);
3069
+ this.logger.info({
3070
+ msg: `📊 共配置 ${hostnameCount} 个 hostname 映射, ${portCount} 个端口绑定`
3071
+ });
2753
3072
  }
2754
3073
  initIdleChecker() {
2755
3074
  setInterval(()=>{
2756
3075
  const now = Date.now();
2757
- for (const service of this.services.values())if (!service.proxyOnly) {
2758
- if ('online' === service._state.status && 0 === service._state.activeConnections && now - service._state.lastAccessTime > service.idleTimeout) {
2759
- this.logger.info({
2760
- msg: `🛌 [${service.name}] 闲置超时,正在停止...`
2761
- });
2762
- this.serviceManager.stop(service).catch((err)=>{
2763
- this.logger.error({
2764
- msg: `❌ [${service.name}] 停止失败`,
2765
- error: err.message
2766
- });
2767
- });
2768
- service._state.status = 'offline';
2769
- }
3076
+ const checkedServices = new Set();
3077
+ for (const mapping of this.hostnameRoutes.values())if (!checkedServices.has(mapping.service)) {
3078
+ checkedServices.add(mapping.service);
3079
+ this.checkIdleService(mapping.service, now);
3080
+ }
3081
+ for (const mapping of this.portRoutes.values())if (!checkedServices.has(mapping.service)) {
3082
+ checkedServices.add(mapping.service);
3083
+ this.checkIdleService(mapping.service, now);
2770
3084
  }
2771
3085
  }, GatewayConstants.IDLE_CHECK_INTERVAL);
2772
3086
  }
2773
- handleRequest(res, req) {
3087
+ checkIdleService(service, now) {
3088
+ if (service.proxyOnly) return;
3089
+ if ('online' === service._state.status && 0 === service._state.activeConnections && now - service._state.lastAccessTime > service.idleTimeout) {
3090
+ this.logger.info({
3091
+ msg: `🛌 [${service.name}] 闲置超时,正在停止...`
3092
+ });
3093
+ service._state.status = 'stopping';
3094
+ if (service._state.startTime) {
3095
+ service._state.totalUptime += now - service._state.startTime;
3096
+ service._state.startTime = void 0;
3097
+ }
3098
+ this.serviceManager.stop(service).catch((err)=>{
3099
+ this.logger.error({
3100
+ msg: `❌ [${service.name}] 停止失败`,
3101
+ error: err.message
3102
+ });
3103
+ }).finally(()=>{
3104
+ service._state.status = 'offline';
3105
+ });
3106
+ }
3107
+ }
3108
+ handlePortBindingRequest(res, req, mapping) {
3109
+ const service = mapping.service;
2774
3110
  const startTime = Date.now();
2775
- const hostname = req.getHeader('host')?.split(':')[0] || '';
2776
3111
  const method = req.getMethod();
2777
3112
  const url = req.getUrl();
2778
3113
  const queryString = req.getQuery();
2779
3114
  const fullUrl = queryString ? `${url}?${queryString}` : url;
2780
3115
  const headers = {};
2781
3116
  req.forEach((key, value)=>{
2782
- const safeValue = value.replace(/[\r\n]/g, '');
3117
+ const safeValue = value.replace(GatewayConstants.CRLF_REGEX, '');
2783
3118
  headers[key] = safeValue;
2784
3119
  });
2785
- const service = this.services.get(hostname);
2786
- if (!service) {
2787
- this.logger.info({
2788
- msg: `❌ [${hostname}] ${method} ${fullUrl} - 404`
2789
- });
2790
- res.cork(()=>{
2791
- res.writeStatus('404 Not Found');
2792
- res.end(`Service not found: ${hostname}`);
2793
- });
3120
+ service._state.lastAccessTime = Date.now();
3121
+ const status = service._state.status;
3122
+ if ('starting' === status) {
3123
+ const startPromise = this.startingPromises.get(service.name);
3124
+ if (startPromise) this.handleServiceWithStartPromise(res, mapping, fullUrl, startTime, method, headers, startPromise);
3125
+ else this.handleDirectProxy(res, mapping, fullUrl, startTime, method, headers);
2794
3126
  return;
2795
3127
  }
2796
- service._state.lastAccessTime = Date.now();
2797
- const needsStart = 'offline' === service._state.status;
2798
- if (needsStart) this.handleServiceStart(res, service, fullUrl, startTime, method, headers);
2799
- else this.handleDirectProxy(res, service, fullUrl, startTime, method, headers);
3128
+ const needsStart = 'offline' === status || 'stopping' === status;
3129
+ if (needsStart) if ('stopping' === status) this.handleServiceWithWait(res, mapping, fullUrl, startTime, method, headers);
3130
+ else this.handleServiceStart(res, mapping, fullUrl, startTime, method, headers);
3131
+ else this.handleDirectProxy(res, mapping, fullUrl, startTime, method, headers);
2800
3132
  }
2801
- handleServiceStart(res, service, fullUrl, startTime, method, headers) {
2802
- const startStartTime = Date.now();
3133
+ handleRequest(res, req) {
3134
+ const startTime = Date.now();
3135
+ const hostHeader = req.getHeader('host');
3136
+ let hostname = '';
3137
+ if (hostHeader) {
3138
+ const colonIndex = hostHeader.indexOf(':');
3139
+ hostname = -1 !== colonIndex ? hostHeader.substring(0, colonIndex) : hostHeader;
3140
+ }
3141
+ const method = req.getMethod();
3142
+ const url = req.getUrl();
3143
+ const queryString = req.getQuery();
3144
+ const fullUrl = queryString ? url + '?' + queryString : url;
3145
+ const headers = {};
3146
+ req.forEach((key, value)=>{
3147
+ headers[key] = value.replace(GatewayConstants.CRLF_REGEX, '');
3148
+ });
3149
+ const mapping = this.hostnameRoutes.get(hostname);
3150
+ if (!mapping) {
3151
+ this.logger.info({
3152
+ msg: `❌ [${hostname}] ${method} ${fullUrl} - 404`
3153
+ });
3154
+ res.cork(()=>{
3155
+ res.writeStatus('404 Not Found');
3156
+ res.end(`Service not found: ${hostname}`);
3157
+ });
3158
+ return;
3159
+ }
3160
+ const service = mapping.service;
3161
+ service._state.lastAccessTime = Date.now();
3162
+ const status = service._state.status;
3163
+ if ('starting' === status) {
3164
+ const startPromise = this.startingPromises.get(service.name);
3165
+ if (startPromise) this.handleServiceWithStartPromise(res, mapping, fullUrl, startTime, method, headers, startPromise);
3166
+ else this.handleDirectProxy(res, mapping, fullUrl, startTime, method, headers);
3167
+ return;
3168
+ }
3169
+ const needsStart = 'offline' === status || 'stopping' === status;
3170
+ if (needsStart) if ('stopping' === status) this.handleServiceWithWait(res, mapping, fullUrl, startTime, method, headers);
3171
+ else this.handleServiceStart(res, mapping, fullUrl, startTime, method, headers);
3172
+ else this.handleDirectProxy(res, mapping, fullUrl, startTime, method, headers);
3173
+ }
3174
+ async startServiceAndProxy(res, mapping, fullUrl, startTime, method, headers, body) {
3175
+ const service = mapping.service;
3176
+ const target = mapping.target;
2803
3177
  this.logger.info({
2804
3178
  msg: `🚀 [${service.name}] ${method} ${fullUrl} - 启动服务...`
2805
3179
  });
2806
3180
  service._state.status = 'starting';
2807
- const chunks = [];
2808
- let aborted = false;
2809
- res.onAborted(()=>{
2810
- aborted = true;
3181
+ let resolveStartPromise;
3182
+ let rejectStartPromise;
3183
+ const startPromise = new Promise((resolve, reject)=>{
3184
+ resolveStartPromise = resolve;
3185
+ rejectStartPromise = reject;
2811
3186
  });
2812
- res.onData((ab, isLast)=>{
2813
- if (aborted) return;
2814
- const chunk = Buffer.from(ab);
2815
- chunks.push(chunk);
2816
- if (isLast) {
2817
- const fullBody = Buffer.concat(chunks);
2818
- (async ()=>{
2819
- try {
2820
- await this.serviceManager.start(service);
2821
- const waitStartTime = Date.now();
2822
- let isReady = false;
2823
- while(Date.now() - waitStartTime < service.startTimeout){
2824
- isReady = await checkTcpPort(service.base);
2825
- if (isReady) {
2826
- const waitDuration = Date.now() - waitStartTime;
2827
- const totalDuration = Date.now() - startStartTime;
2828
- this.logger.info({
2829
- msg: `✅ [${service.name}] 服务就绪 (启动${formatTime(totalDuration - waitDuration)}, 等待${formatTime(waitDuration)})`
2830
- });
2831
- break;
2832
- }
2833
- }
2834
- if (!isReady) {
2835
- service._state.status = 'offline';
2836
- throw new Error(`服务启动超时: 端口 ${service.base} 不可用`);
2837
- }
2838
- service._state.status = 'online';
2839
- if (aborted) return;
2840
- await this.forwardProxyRequest(res, service, fullUrl, startTime, method, headers, fullBody);
2841
- } catch (error) {
2842
- const message = error instanceof Error ? error.message : String(error);
2843
- if ('Client aborted' === message) return;
2844
- this.logger.error({
2845
- msg: `❌ [${service.name}] 启动失败`,
2846
- error: message
2847
- });
2848
- if (!aborted) try {
2849
- res.cork(()=>{
2850
- res.writeStatus('503 Service Unavailable');
2851
- res.end('Service Unavailable');
2852
- });
2853
- } catch (sendErr) {
2854
- const sendErrMsg = sendErr instanceof Error ? sendErr.message : String(sendErr);
2855
- this.logger.error({
2856
- msg: `❌ [${service.name}] 发送错误响应失败`,
2857
- error: sendErrMsg
2858
- });
2859
- }
2860
- }
2861
- })();
3187
+ this.startingPromises.set(service.name, startPromise);
3188
+ try {
3189
+ await this.serviceManager.start(service);
3190
+ const waitStartTime = Date.now();
3191
+ let isReady = false;
3192
+ while(Date.now() - waitStartTime < service.startTimeout){
3193
+ isReady = await gateway_checkTcpPort(target);
3194
+ if (isReady) {
3195
+ const waitDuration = Date.now() - waitStartTime;
3196
+ this.logger.info({
3197
+ msg: `✅ [${service.name}] 服务就绪 (等待${formatTime(waitDuration)})`
3198
+ });
3199
+ break;
3200
+ }
2862
3201
  }
2863
- });
3202
+ if (!isReady) {
3203
+ service._state.status = 'offline';
3204
+ throw new Error(`服务启动超时: 端口 ${target} 不可用`);
3205
+ }
3206
+ service._state.status = 'online';
3207
+ service._state.startTime = Date.now();
3208
+ service._state.startCount++;
3209
+ resolveStartPromise();
3210
+ this.startingPromises.delete(service.name);
3211
+ await this.forwardProxyRequest(res, mapping, fullUrl, startTime, method, headers, body);
3212
+ } catch (error) {
3213
+ this.startingPromises.delete(service.name);
3214
+ const message = error instanceof Error ? error.message : String(error);
3215
+ if ('Client aborted' === message) return void resolveStartPromise();
3216
+ rejectStartPromise(error instanceof Error ? error : new Error(message));
3217
+ this.logger.error({
3218
+ msg: `❌ [${service.name}] 启动失败`,
3219
+ error: message
3220
+ });
3221
+ try {
3222
+ res.cork(()=>{
3223
+ res.writeStatus('503 Service Unavailable');
3224
+ res.end('Service Unavailable');
3225
+ });
3226
+ } catch {}
3227
+ }
2864
3228
  }
2865
- handleDirectProxy(res, service, fullUrl, startTime, method, headers) {
2866
- const chunks = [];
2867
- let aborted = false;
2868
- res.onAborted(()=>{
2869
- aborted = true;
3229
+ handleServiceWithWait(res, mapping, fullUrl, startTime, method, headers) {
3230
+ const service = mapping.service;
3231
+ this.logger.info({
3232
+ msg: `⏳ [${service.name}] ${method} ${fullUrl} - 等待服务停止完成...`
2870
3233
  });
2871
- res.onData((ab, isLast)=>{
2872
- if (aborted) return;
2873
- const chunk = Buffer.from(ab);
2874
- chunks.push(chunk);
2875
- if (isLast) {
2876
- const fullBody = Buffer.concat(chunks);
2877
- if (aborted) return;
2878
- this.forwardProxyRequest(res, service, fullUrl, startTime, method, headers, fullBody).catch((err)=>{
2879
- if ('Client aborted' === err.message) return;
3234
+ const bodyPromise = collectRequestBody(res);
3235
+ (async ()=>{
3236
+ const maxWaitTime = 30000;
3237
+ const checkInterval = 100;
3238
+ const waitStartTime = Date.now();
3239
+ while('stopping' === service._state.status){
3240
+ if (Date.now() - waitStartTime > maxWaitTime) {
2880
3241
  this.logger.error({
2881
- msg: `❌ [${service.name}] 代理失败`,
2882
- error: err.message
3242
+ msg: `❌ [${service.name}] 等待服务停止超时`
2883
3243
  });
2884
- if (!aborted) try {
3244
+ try {
2885
3245
  res.cork(()=>{
2886
- res.writeStatus('500 Internal Server Error');
2887
- res.end('Proxy Error');
2888
- });
2889
- } catch (sendErr) {
2890
- const sendErrMsg = sendErr instanceof Error ? sendErr.message : String(sendErr);
2891
- this.logger.error({
2892
- msg: `❌ [${service.name}] 发送错误响应失败`,
2893
- error: sendErrMsg
3246
+ res.writeStatus('503 Service Unavailable');
3247
+ res.end('Service stopping timeout');
2894
3248
  });
2895
- }
2896
- });
3249
+ } catch {}
3250
+ return;
3251
+ }
3252
+ await new Promise((resolve)=>setTimeout(resolve, checkInterval));
2897
3253
  }
2898
- });
3254
+ this.logger.info({
3255
+ msg: `✅ [${service.name}] 服务已停止,开始启动...`
3256
+ });
3257
+ const body = await bodyPromise;
3258
+ await this.startServiceAndProxy(res, mapping, fullUrl, startTime, method, headers, body);
3259
+ })();
2899
3260
  }
2900
- async forwardProxyRequest(res, service, path, startTime, method, headers, body) {
2901
- const targetUrl = new external_node_url_namespaceObject.URL(service.base + path);
2902
- const isHttps = 'https:' === targetUrl.protocol;
2903
- const httpModule = isHttps ? external_node_https_default() : external_node_http_default();
2904
- const proxyHeaders = {
2905
- ...headers
2906
- };
2907
- delete proxyHeaders['connection'];
2908
- delete proxyHeaders['keep-alive'];
3261
+ handleServiceWithStartPromise(res, mapping, fullUrl, startTime, method, headers, startPromise) {
3262
+ const bodyPromise = collectRequestBody(res);
3263
+ (async ()=>{
3264
+ try {
3265
+ await startPromise;
3266
+ } catch {
3267
+ await bodyPromise;
3268
+ try {
3269
+ res.cork(()=>{
3270
+ res.writeStatus('503 Service Unavailable');
3271
+ res.end('Service start failed');
3272
+ });
3273
+ } catch {}
3274
+ return;
3275
+ }
3276
+ const body = await bodyPromise;
3277
+ await this.forwardProxyRequest(res, mapping, fullUrl, startTime, method, headers, body);
3278
+ })();
3279
+ }
3280
+ handleServiceStart(res, mapping, fullUrl, startTime, method, headers) {
3281
+ const bodyPromise = collectRequestBody(res);
3282
+ (async ()=>{
3283
+ const body = await bodyPromise;
3284
+ await this.startServiceAndProxy(res, mapping, fullUrl, startTime, method, headers, body);
3285
+ })();
3286
+ }
3287
+ handleDirectProxy(res, mapping, fullUrl, startTime, method, headers) {
3288
+ const service = mapping.service;
3289
+ const targetUrl = mapping.targetUrl;
3290
+ const proxyHeaders = {};
3291
+ for(const key in headers)if (!GatewayConstants.SKIP_REQUEST_HEADERS.has(key.toLowerCase())) proxyHeaders[key] = headers[key];
2909
3292
  proxyHeaders['host'] = targetUrl.host;
2910
3293
  const state = {
2911
3294
  aborted: false,
2912
3295
  responded: false
2913
3296
  };
2914
3297
  service._state.activeConnections++;
2915
- return new Promise((resolve, reject)=>{
2916
- const cleanup = ()=>{
2917
- service._state.activeConnections--;
2918
- };
2919
- res.onAborted(()=>{
2920
- state.aborted = true;
2921
- if (state.proxyReq && !state.proxyReq.destroyed) state.proxyReq.destroy();
2922
- if (state.proxyRes && !state.proxyRes.destroyed) state.proxyRes.destroy();
3298
+ const cleanup = ()=>{
3299
+ service._state.activeConnections--;
3300
+ };
3301
+ res.onAborted(()=>{
3302
+ state.aborted = true;
3303
+ cleanup();
3304
+ });
3305
+ const proxyReq = external_node_http_default().request({
3306
+ hostname: targetUrl.hostname,
3307
+ port: parseInt(targetUrl.port || ('https:' === targetUrl.protocol ? '443' : '80')),
3308
+ path: fullUrl,
3309
+ method: method.toUpperCase(),
3310
+ headers: proxyHeaders,
3311
+ timeout: 30000
3312
+ }, (proxyRes)=>{
3313
+ if (state.aborted) {
3314
+ proxyRes.destroy();
2923
3315
  cleanup();
2924
- resolve();
2925
- });
2926
- state.proxyReq = httpModule.request(targetUrl, {
2927
- method,
2928
- headers: proxyHeaders,
2929
- rejectUnauthorized: false
2930
- }, (proxyRes)=>{
2931
- state.proxyRes = proxyRes;
2932
- const statusCode = proxyRes.statusCode || 200;
2933
- const statusMessage = proxyRes.statusMessage || 'OK';
2934
- if (state.aborted) {
2935
- proxyRes.destroy();
2936
- cleanup();
2937
- resolve();
2938
- return;
3316
+ return;
3317
+ }
3318
+ const statusCode = proxyRes.statusCode || 200;
3319
+ const statusMessage = getStatusMessage(statusCode);
3320
+ if (101 === statusCode) {
3321
+ res.cork(()=>{
3322
+ if (state.aborted) return;
3323
+ res.writeStatus(`${statusCode} ${statusMessage}`);
3324
+ for (const [key, value] of Object.entries(proxyRes.headers))if (!GatewayConstants.SKIP_RESPONSE_HEADERS.has(key.toLowerCase())) {
3325
+ if (value) res.writeHeader(key, Array.isArray(value) ? value.join(', ') : value);
3326
+ }
3327
+ res.end();
3328
+ state.responded = true;
3329
+ });
3330
+ cleanup();
3331
+ return;
3332
+ }
3333
+ res.cork(()=>{
3334
+ if (state.aborted) return;
3335
+ res.writeStatus(`${statusCode} ${statusMessage}`);
3336
+ for (const [key, value] of Object.entries(proxyRes.headers))if (!GatewayConstants.SKIP_RESPONSE_HEADERS.has(key.toLowerCase())) {
3337
+ if (value) res.writeHeader(key, Array.isArray(value) ? value.join(', ') : value);
2939
3338
  }
2940
- if (101 === statusCode) {
2941
- this.logger.info({
2942
- msg: `✅ [${service.name}] WebSocket 升级成功`
2943
- });
2944
- res.cork(()=>{
2945
- if (state.aborted) return;
2946
- res.writeStatus(`${statusCode} ${statusMessage}`);
2947
- const responseHeaders = proxyRes.headers;
2948
- for (const [key, value] of Object.entries(responseHeaders)){
2949
- const keyLower = key.toLowerCase();
2950
- if ('connection' !== keyLower && 'transfer-encoding' !== keyLower && 'keep-alive' !== keyLower) {
2951
- if (Array.isArray(value)) for (const v of value)res.writeHeader(key, v);
2952
- else if (void 0 !== value) res.writeHeader(key, value);
2953
- }
3339
+ });
3340
+ proxyRes.on('data', (chunk)=>{
3341
+ if (state.aborted) return void proxyRes.destroy();
3342
+ let writeSuccess = false;
3343
+ res.cork(()=>{
3344
+ if (state.aborted) return;
3345
+ writeSuccess = res.write(chunk);
3346
+ });
3347
+ if (!writeSuccess) {
3348
+ proxyRes.pause();
3349
+ res.onWritable(()=>{
3350
+ if (state.aborted) {
3351
+ proxyRes.destroy();
3352
+ return false;
2954
3353
  }
2955
- res.end();
2956
- state.responded = true;
3354
+ proxyRes.resume();
3355
+ return true;
2957
3356
  });
2958
- cleanup();
2959
- resolve();
2960
- return;
2961
3357
  }
3358
+ });
3359
+ proxyRes.on('end', ()=>{
3360
+ if (state.aborted) return void cleanup();
2962
3361
  res.cork(()=>{
2963
3362
  if (state.aborted) return;
2964
- res.writeStatus(`${statusCode} ${statusMessage}`);
2965
- const responseHeaders = proxyRes.headers;
2966
- for (const [key, value] of Object.entries(responseHeaders)){
2967
- const keyLower = key.toLowerCase();
2968
- if ('connection' !== keyLower && 'transfer-encoding' !== keyLower && 'keep-alive' !== keyLower) {
2969
- if (Array.isArray(value)) for (const v of value)res.writeHeader(key, v);
2970
- else if (void 0 !== value) res.writeHeader(key, value);
2971
- }
3363
+ res.end();
3364
+ state.responded = true;
3365
+ if (this.logging.enableRequestLog) {
3366
+ const responseTime = Date.now() - startTime;
3367
+ this.logger.info({
3368
+ msg: `📤 [${service.name}] ${method} ${fullUrl} - ${statusCode} - ${formatTime(responseTime)}`,
3369
+ service: service.name,
3370
+ method,
3371
+ path: fullUrl,
3372
+ statusCode,
3373
+ responseTime
3374
+ });
2972
3375
  }
2973
3376
  });
2974
- proxyRes.on('data', (chunk)=>{
3377
+ cleanup();
3378
+ });
3379
+ proxyRes.on('error', (err)=>{
3380
+ if (state.aborted) return void cleanup();
3381
+ this.logger.error({
3382
+ msg: `❌ [${service.name}] 代理响应错误`,
3383
+ error: err.message
3384
+ });
3385
+ if (!state.responded) {
3386
+ state.responded = true;
3387
+ res.cork(()=>{
3388
+ res.writeStatus('502 Bad Gateway');
3389
+ res.end('Bad Gateway');
3390
+ });
3391
+ }
3392
+ cleanup();
3393
+ });
3394
+ });
3395
+ proxyReq.on('error', (err)=>{
3396
+ if (state.aborted) return void cleanup();
3397
+ this.logger.error({
3398
+ msg: `❌ [${service.name}] 代理请求错误`,
3399
+ error: err.message
3400
+ });
3401
+ if (!state.responded) {
3402
+ state.responded = true;
3403
+ res.cork(()=>{
3404
+ res.writeStatus('502 Bad Gateway');
3405
+ res.end('Bad Gateway');
3406
+ });
3407
+ }
3408
+ if (!service.proxyOnly && 'online' === service._state.status) {
3409
+ this.logger.info({
3410
+ msg: `🔄 [${service.name}] 后端不可达,重置状态为 offline`
3411
+ });
3412
+ service._state.status = 'offline';
3413
+ }
3414
+ cleanup();
3415
+ });
3416
+ res.onData((ab, isLast)=>{
3417
+ if (state.aborted) return void proxyReq.destroy();
3418
+ const chunk = Buffer.from(ab);
3419
+ if (isLast) proxyReq.end(chunk);
3420
+ else proxyReq.write(chunk);
3421
+ });
3422
+ }
3423
+ async forwardProxyRequest(res, mapping, path, startTime, method, headers, body) {
3424
+ const service = mapping.service;
3425
+ const perfLog = this.logging.enablePerformanceLog;
3426
+ const perfPrepStart = perfLog ? performance.now() : 0;
3427
+ const targetUrl = mapping.targetUrl;
3428
+ const proxyHeaders = {};
3429
+ for(const key in headers){
3430
+ const keyLower = key.toLowerCase();
3431
+ if ('connection' !== keyLower && 'keep-alive' !== keyLower && 'content-length' !== keyLower && 'transfer-encoding' !== keyLower) proxyHeaders[key] = headers[key];
3432
+ }
3433
+ proxyHeaders['host'] = targetUrl.host;
3434
+ if (body.length > 0) proxyHeaders['content-length'] = String(body.length);
3435
+ const perfPrepTime = perfLog ? performance.now() - perfPrepStart : 0;
3436
+ const state = {
3437
+ aborted: false,
3438
+ responded: false
3439
+ };
3440
+ service._state.activeConnections++;
3441
+ const cleanup = ()=>{
3442
+ service._state.activeConnections--;
3443
+ };
3444
+ res.onAborted(()=>{
3445
+ state.aborted = true;
3446
+ cleanup();
3447
+ });
3448
+ const perfHttpStart = perfLog ? performance.now() : 0;
3449
+ let perfTtfb = 0;
3450
+ let perfStreamStart = 0;
3451
+ let chunkCount = 0;
3452
+ let totalBytes = 0;
3453
+ return new Promise((resolve, reject)=>{
3454
+ let proxyReq;
3455
+ try {
3456
+ proxyReq = external_node_http_default().request({
3457
+ hostname: targetUrl.hostname,
3458
+ port: parseInt(targetUrl.port || ('https:' === targetUrl.protocol ? '443' : '80')),
3459
+ path,
3460
+ method: method.toUpperCase(),
3461
+ headers: proxyHeaders,
3462
+ timeout: 30000
3463
+ }, (proxyRes)=>{
3464
+ if (perfLog && 0 === perfTtfb) {
3465
+ perfTtfb = performance.now() - perfHttpStart;
3466
+ perfStreamStart = performance.now();
3467
+ }
3468
+ const statusCode = proxyRes.statusCode || 200;
3469
+ const statusMessage = getStatusMessage(statusCode);
2975
3470
  if (state.aborted) {
2976
3471
  proxyRes.destroy();
3472
+ cleanup();
3473
+ resolve();
2977
3474
  return;
2978
3475
  }
2979
- let writeSuccess = false;
2980
- res.cork(()=>{
2981
- if (state.aborted) return;
2982
- writeSuccess = res.write(chunk);
2983
- });
2984
- if (!writeSuccess) {
2985
- proxyRes.pause();
2986
- res.onWritable(()=>{
2987
- if (state.aborted) {
2988
- proxyRes.destroy();
2989
- return false;
3476
+ if (101 === statusCode) {
3477
+ res.cork(()=>{
3478
+ if (state.aborted) return;
3479
+ res.writeStatus(`${statusCode} ${statusMessage}`);
3480
+ for (const [key, value] of Object.entries(proxyRes.headers))if (!GatewayConstants.SKIP_RESPONSE_HEADERS.has(key.toLowerCase())) {
3481
+ if (value) res.writeHeader(key, Array.isArray(value) ? value.join(', ') : value);
2990
3482
  }
2991
- proxyRes.resume();
2992
- return true;
3483
+ res.end();
3484
+ state.responded = true;
2993
3485
  });
2994
- }
2995
- });
2996
- proxyRes.on('end', ()=>{
2997
- if (state.aborted) {
2998
3486
  cleanup();
2999
3487
  resolve();
3000
3488
  return;
3001
3489
  }
3002
3490
  res.cork(()=>{
3003
3491
  if (state.aborted) return;
3004
- res.end();
3005
- state.responded = true;
3006
- const responseTime = Date.now() - startTime;
3007
- this.logger.info({
3008
- msg: `📤 [${service.name}] ${method} ${path} - ${statusCode} - ${formatTime(responseTime)}`,
3009
- service: service.name,
3010
- method,
3011
- path,
3012
- statusCode,
3013
- responseTime
3492
+ res.writeStatus(`${statusCode} ${statusMessage}`);
3493
+ for (const [key, value] of Object.entries(proxyRes.headers))if (!GatewayConstants.SKIP_RESPONSE_HEADERS.has(key.toLowerCase())) {
3494
+ if (value) res.writeHeader(key, Array.isArray(value) ? value.join(', ') : value);
3495
+ }
3496
+ });
3497
+ proxyRes.on('data', (chunk)=>{
3498
+ if (state.aborted) return void proxyRes.destroy();
3499
+ if (perfLog) {
3500
+ chunkCount++;
3501
+ totalBytes += chunk.length;
3502
+ }
3503
+ let writeSuccess = false;
3504
+ res.cork(()=>{
3505
+ if (state.aborted) return;
3506
+ writeSuccess = res.write(chunk);
3014
3507
  });
3508
+ if (!writeSuccess) {
3509
+ proxyRes.pause();
3510
+ res.onWritable(()=>{
3511
+ if (state.aborted) {
3512
+ proxyRes.destroy();
3513
+ return false;
3514
+ }
3515
+ proxyRes.resume();
3516
+ return true;
3517
+ });
3518
+ }
3519
+ });
3520
+ proxyRes.on('end', ()=>{
3521
+ if (state.aborted) {
3522
+ cleanup();
3523
+ resolve();
3524
+ return;
3525
+ }
3526
+ const perfStreamTime = perfLog ? performance.now() - perfStreamStart : 0;
3527
+ const perfTotalTime = perfLog ? performance.now() - perfPrepStart : 0;
3528
+ res.cork(()=>{
3529
+ if (state.aborted) return;
3530
+ res.end();
3531
+ state.responded = true;
3532
+ if (this.logging.enableRequestLog) {
3533
+ const responseTime = Date.now() - startTime;
3534
+ this.logger.info({
3535
+ msg: `📤 [${service.name}] ${method} ${path} - ${statusCode} - ${formatTime(responseTime)}`,
3536
+ service: service.name,
3537
+ method,
3538
+ path,
3539
+ statusCode,
3540
+ responseTime
3541
+ });
3542
+ }
3543
+ if (perfLog) console.error(`⚡ [${service.name}] 性能分析:`, {
3544
+ method,
3545
+ path,
3546
+ statusCode,
3547
+ prepTime: perfPrepTime.toFixed(3) + 'ms',
3548
+ ttfb: perfTtfb.toFixed(3) + 'ms',
3549
+ streamTime: perfStreamTime.toFixed(3) + 'ms',
3550
+ totalTime: perfTotalTime.toFixed(3) + 'ms',
3551
+ chunkCount,
3552
+ totalBytes,
3553
+ avgChunkSize: chunkCount > 0 ? (totalBytes / chunkCount).toFixed(1) + 'B' : '0B'
3554
+ });
3555
+ });
3556
+ cleanup();
3557
+ resolve();
3558
+ });
3559
+ proxyRes.on('error', (err)=>{
3560
+ if (state.aborted) {
3561
+ cleanup();
3562
+ resolve();
3563
+ return;
3564
+ }
3565
+ this.logger.error({
3566
+ msg: `❌ [${service.name}] 代理响应错误`,
3567
+ error: err.message
3568
+ });
3569
+ if (!state.responded) {
3570
+ state.responded = true;
3571
+ res.cork(()=>{
3572
+ res.writeStatus('502 Bad Gateway');
3573
+ res.end('Bad Gateway');
3574
+ });
3575
+ }
3576
+ cleanup();
3577
+ reject(err);
3015
3578
  });
3016
- cleanup();
3017
- resolve();
3018
3579
  });
3019
- proxyRes.on('error', (err)=>{
3580
+ proxyReq.on('error', (err)=>{
3020
3581
  if (state.aborted) {
3021
3582
  cleanup();
3022
3583
  resolve();
3023
3584
  return;
3024
3585
  }
3025
3586
  this.logger.error({
3026
- msg: `❌ [${service.name}] 代理响应错误`,
3587
+ msg: `❌ [${service.name}] 代理请求错误`,
3027
3588
  error: err.message
3028
3589
  });
3029
3590
  if (!state.responded) {
3030
3591
  state.responded = true;
3031
- try {
3032
- res.cork(()=>{
3033
- if (!state.aborted) {
3034
- res.writeStatus('502 Bad Gateway');
3035
- res.end('Bad Gateway');
3036
- }
3037
- });
3038
- } catch (sendErr) {
3039
- const sendErrMsg = sendErr instanceof Error ? sendErr.message : String(sendErr);
3040
- this.logger.error({
3041
- msg: `❌ [${service.name}] 发送错误响应失败`,
3042
- error: sendErrMsg
3043
- });
3044
- }
3592
+ res.cork(()=>{
3593
+ res.writeStatus('502 Bad Gateway');
3594
+ res.end('Bad Gateway');
3595
+ });
3045
3596
  }
3046
3597
  cleanup();
3047
3598
  reject(err);
3048
3599
  });
3049
- });
3050
- state.proxyReq.on('error', (err)=>{
3051
- if (state.aborted) {
3052
- cleanup();
3053
- resolve();
3054
- return;
3055
- }
3600
+ if (body.length > 0) proxyReq.end(body);
3601
+ else proxyReq.end();
3602
+ } catch (err) {
3603
+ const message = err instanceof Error ? err.message : String(err);
3056
3604
  this.logger.error({
3057
- msg: `❌ [${service.name}] 代理请求错误`,
3058
- error: err.message
3605
+ msg: `❌ [${service.name}] 创建代理请求失败`,
3606
+ error: message
3059
3607
  });
3060
3608
  if (!state.responded) {
3061
3609
  state.responded = true;
3062
- try {
3063
- res.cork(()=>{
3064
- if (!state.aborted) {
3065
- res.writeStatus('502 Bad Gateway');
3066
- res.end('Bad Gateway');
3067
- }
3068
- });
3069
- } catch (sendErr) {
3070
- const sendErrMsg = sendErr instanceof Error ? sendErr.message : String(sendErr);
3071
- this.logger.error({
3072
- msg: `❌ [${service.name}] 发送错误响应失败`,
3073
- error: sendErrMsg
3074
- });
3075
- }
3610
+ res.cork(()=>{
3611
+ res.writeStatus('502 Bad Gateway');
3612
+ res.end('Bad Gateway');
3613
+ });
3076
3614
  }
3077
3615
  cleanup();
3078
- reject(err);
3079
- });
3080
- state.proxyReq.write(body);
3081
- state.proxyReq.end();
3616
+ reject(new Error(message));
3617
+ }
3082
3618
  });
3083
3619
  }
3084
3620
  async start() {
3085
- const uWS = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "uWebSockets.js"));
3086
3621
  const host = this.config.host || '127.0.0.1';
3087
3622
  const port = this.config.port || 3000;
3088
- const app = uWS.App();
3623
+ const app = external_uWebSockets_js_default().App();
3089
3624
  app.ws('/*', {
3090
3625
  upgrade: (res, req, context)=>{
3091
3626
  const hostname = req.getHeader('host')?.split(':')[0] || '';
3092
- const service = this.services.get(hostname);
3093
- if (!service) {
3094
- res.cork(()=>{
3095
- res.writeStatus('404 Not Found');
3096
- res.end(`Service not found: ${hostname}`);
3097
- });
3098
- return;
3099
- }
3627
+ const mapping = this.hostnameRoutes.get(hostname);
3628
+ if (!mapping) return void res.cork(()=>{
3629
+ res.writeStatus('404 Not Found');
3630
+ res.end(`Service not found: ${hostname}`);
3631
+ });
3632
+ const { service, target } = mapping;
3100
3633
  service._state.lastAccessTime = Date.now();
3101
3634
  const clientHeaders = {};
3102
3635
  req.forEach((key, value)=>{
3103
- const safeValue = value.replace(/[\r\n]/g, '');
3636
+ const safeValue = value.replace(GatewayConstants.CRLF_REGEX, '');
3104
3637
  clientHeaders[key] = safeValue;
3105
3638
  });
3106
3639
  const clientPath = req.getUrl() + (req.getQuery() ? `?${req.getQuery()}` : '');
3107
3640
  res.upgrade({
3108
3641
  hostname,
3109
3642
  service,
3643
+ target,
3110
3644
  clientHeaders,
3111
3645
  clientPath
3112
3646
  }, req.getHeader('sec-websocket-key'), req.getHeader('sec-websocket-protocol'), req.getHeader('sec-websocket-extensions'), context);
3113
- this.logger.info({
3647
+ if (this.logging.enableWebSocketLog) this.logger.info({
3114
3648
  msg: `🔌 [${service.name}] WebSocket 升级请求: ${clientPath}`
3115
3649
  });
3116
3650
  },
3117
3651
  open: (ws)=>{
3118
3652
  const userData = ws.getUserData();
3119
3653
  const service = userData.service;
3654
+ const target = userData.target;
3120
3655
  service._state.activeConnections++;
3121
- this.logger.info({
3656
+ if (this.logging.enableWebSocketLog) this.logger.info({
3122
3657
  msg: `🔌 [${service.name}] WebSocket 连接已建立`
3123
3658
  });
3124
3659
  const wsState = {
@@ -3140,7 +3675,7 @@ var __webpack_exports__ = {};
3140
3675
  const waitStartTime = Date.now();
3141
3676
  let isReady = false;
3142
3677
  while(Date.now() - waitStartTime < service.startTimeout){
3143
- isReady = await checkTcpPort(service.base);
3678
+ isReady = await gateway_checkTcpPort(target);
3144
3679
  if (isReady) {
3145
3680
  const waitDuration = Date.now() - waitStartTime;
3146
3681
  this.logger.info({
@@ -3159,13 +3694,15 @@ var __webpack_exports__ = {};
3159
3694
  return;
3160
3695
  }
3161
3696
  service._state.status = 'online';
3697
+ service._state.startTime = Date.now();
3698
+ service._state.startCount++;
3162
3699
  }
3163
- const targetUrl = new external_node_url_namespaceObject.URL(service.base);
3164
- const userData = ws.getUserData();
3165
- const clientPath = userData.clientPath;
3166
- const clientHeaders = userData.clientHeaders;
3700
+ const targetUrl = new external_node_url_namespaceObject.URL(target);
3701
+ const wsUserData = ws.getUserData();
3702
+ const clientPath = wsUserData.clientPath;
3703
+ const clientHeaders = wsUserData.clientHeaders;
3167
3704
  const wsUrl = `${'https:' === targetUrl.protocol ? 'wss:' : 'ws:'}//${targetUrl.host}${clientPath}`;
3168
- this.logger.info({
3705
+ if (this.logging.enableWebSocketLog) this.logger.info({
3169
3706
  msg: `🔌 [${service.name}] 连接后端 WebSocket: ${wsUrl}`
3170
3707
  });
3171
3708
  const backendHeaders = {};
@@ -3187,17 +3724,17 @@ var __webpack_exports__ = {};
3187
3724
  });
3188
3725
  wsState.backendWs = backendWs;
3189
3726
  backendWs.on('open', ()=>{
3190
- this.logger.info({
3727
+ if (this.logging.enableWebSocketLog) this.logger.info({
3191
3728
  msg: `✅ [${service.name}] 后端 WebSocket 连接已建立`
3192
3729
  });
3193
3730
  wsState.backendReady = true;
3194
- this.logger.info({
3731
+ if (this.logging.enableWebSocketLog) this.logger.info({
3195
3732
  msg: `📤 [${service.name}] 发送队列中的 ${wsState.messageQueue.length} 条消息`
3196
3733
  });
3197
3734
  while(wsState.messageQueue.length > 0 && backendWs.readyState === wrapper.OPEN){
3198
3735
  const msg = wsState.messageQueue.shift();
3199
3736
  if (msg) {
3200
- this.logger.info({
3737
+ if (this.logging.enableWebSocketLog) this.logger.info({
3201
3738
  msg: `📨 [${service.name}] 发送队列消息: ${msg.length} 字节`
3202
3739
  });
3203
3740
  backendWs.send(msg);
@@ -3222,7 +3759,7 @@ var __webpack_exports__ = {};
3222
3759
  }
3223
3760
  });
3224
3761
  backendWs.on('close', ()=>{
3225
- this.logger.info({
3762
+ if (this.logging.enableWebSocketLog) this.logger.info({
3226
3763
  msg: `🔌 [${service.name}] 后端 WebSocket 连接关闭`
3227
3764
  });
3228
3765
  if (null !== ws && !wsState.closing) {
@@ -3239,12 +3776,12 @@ var __webpack_exports__ = {};
3239
3776
  if (null !== ws) ws.close();
3240
3777
  });
3241
3778
  backendWs.on('pause', ()=>{
3242
- this.logger.info({
3779
+ if (this.logging.enableWebSocketLog) this.logger.info({
3243
3780
  msg: `⏸️ [${service.name}] 后端 WebSocket 暂停(背压)`
3244
3781
  });
3245
3782
  });
3246
3783
  backendWs.on('resume', ()=>{
3247
- this.logger.info({
3784
+ if (this.logging.enableWebSocketLog) this.logger.info({
3248
3785
  msg: `▶️ [${service.name}] 后端 WebSocket 恢复`
3249
3786
  });
3250
3787
  });
@@ -3264,23 +3801,23 @@ var __webpack_exports__ = {};
3264
3801
  const wsState = ws.wsState;
3265
3802
  if (wsState.backendReady && wsState.backendWs && wsState.backendWs.readyState === wrapper.OPEN) {
3266
3803
  const msgBuffer = Buffer.from(message);
3267
- this.logger.info({
3804
+ if (this.logging.enableWebSocketLog) this.logger.info({
3268
3805
  msg: `📨 [${service.name}] 转发消息到后端: ${msgBuffer.length} 字节`
3269
3806
  });
3270
3807
  wsState.backendWs.send(msgBuffer);
3271
3808
  service._state.lastAccessTime = Date.now();
3272
3809
  } else {
3273
- this.logger.info({
3810
+ if (this.logging.enableWebSocketLog) this.logger.info({
3274
3811
  msg: `📦 [${service.name}] 消息加入队列`
3275
3812
  });
3276
- wsState.messageQueue.push(Buffer.from(message));
3813
+ if (wsState.messageQueue.length < GatewayConstants.MAX_WS_MESSAGE_QUEUE_SIZE) wsState.messageQueue.push(Buffer.from(message));
3277
3814
  }
3278
3815
  },
3279
3816
  close: (ws)=>{
3280
3817
  const userData = ws.getUserData();
3281
3818
  const service = userData.service;
3282
3819
  service._state.activeConnections--;
3283
- this.logger.info({
3820
+ if (this.logging.enableWebSocketLog) this.logger.info({
3284
3821
  msg: `🔌 [${service.name}] 客户端 WebSocket 连接关闭`
3285
3822
  });
3286
3823
  const wsState = ws.wsState;
@@ -3301,27 +3838,270 @@ var __webpack_exports__ = {};
3301
3838
  msg: `❌ DynaPM 网关启动失败: ${host}:${port}`
3302
3839
  });
3303
3840
  });
3841
+ for (const [portNum, mapping] of this.portRoutes)this.createPortBindingListener(host, portNum, mapping);
3842
+ const adminApiConfig = this.config.adminApi;
3843
+ if (adminApiConfig && false !== adminApiConfig.enabled && adminApiConfig.port) this.createAdminApiListener(host, adminApiConfig.port);
3844
+ }
3845
+ createAdminApiListener(host, port) {
3846
+ const app = external_uWebSockets_js_default().App();
3847
+ app.any('/*', (res, req)=>{
3848
+ this.adminApi.handleAdminApi(res, req);
3849
+ });
3850
+ app.listen(host, port, (token)=>{
3851
+ if (token) this.logger.info({
3852
+ msg: `🔌 管理 API 已启动: http://${host}:${port}`
3853
+ });
3854
+ else this.logger.error({
3855
+ msg: `❌ 管理 API 启动失败: ${host}:${port}`
3856
+ });
3857
+ });
3858
+ }
3859
+ createPortBindingListener(host, portNum, mapping) {
3860
+ const { service, target } = mapping;
3861
+ const app = external_uWebSockets_js_default().App();
3862
+ app.ws('/*', {
3863
+ upgrade: (res, req, context)=>{
3864
+ service._state.lastAccessTime = Date.now();
3865
+ const clientHeaders = {};
3866
+ req.forEach((key, value)=>{
3867
+ const safeValue = value.replace(GatewayConstants.CRLF_REGEX, '');
3868
+ clientHeaders[key] = safeValue;
3869
+ });
3870
+ const clientPath = req.getUrl() + (req.getQuery() ? `?${req.getQuery()}` : '');
3871
+ res.upgrade({
3872
+ service,
3873
+ target,
3874
+ clientHeaders,
3875
+ clientPath
3876
+ }, req.getHeader('sec-websocket-key'), req.getHeader('sec-websocket-protocol'), req.getHeader('sec-websocket-extensions'), context);
3877
+ if (this.logging.enableWebSocketLog) this.logger.info({
3878
+ msg: `🔌 [${service.name}] 端口${portNum} WebSocket 升级请求: ${clientPath}`
3879
+ });
3880
+ },
3881
+ open: (ws)=>{
3882
+ const userData = ws.getUserData();
3883
+ const svc = userData.service;
3884
+ const backendTarget = userData.target;
3885
+ svc._state.activeConnections++;
3886
+ if (this.logging.enableWebSocketLog) this.logger.info({
3887
+ msg: `🔌 [${svc.name}] 端口${portNum} WebSocket 连接已建立`
3888
+ });
3889
+ const wsState = {
3890
+ backendReady: false,
3891
+ messageQueue: [],
3892
+ backendWs: void 0,
3893
+ closing: false
3894
+ };
3895
+ ws.wsState = wsState;
3896
+ (async ()=>{
3897
+ try {
3898
+ const needsStart = 'offline' === svc._state.status;
3899
+ if (needsStart) {
3900
+ this.logger.info({
3901
+ msg: `🚀 [${svc.name}] 端口${portNum} WebSocket - 启动服务...`
3902
+ });
3903
+ svc._state.status = 'starting';
3904
+ await this.serviceManager.start(svc);
3905
+ const waitStartTime = Date.now();
3906
+ let isReady = false;
3907
+ while(Date.now() - waitStartTime < svc.startTimeout){
3908
+ isReady = await gateway_checkTcpPort(backendTarget);
3909
+ if (isReady) {
3910
+ const waitDuration = Date.now() - waitStartTime;
3911
+ this.logger.info({
3912
+ msg: `✅ [${svc.name}] 端口${portNum} WebSocket 服务就绪 (等待${formatTime(waitDuration)})`
3913
+ });
3914
+ break;
3915
+ }
3916
+ await new Promise((resolve)=>setTimeout(resolve, GatewayConstants.BACKEND_READY_CHECK_DELAY));
3917
+ }
3918
+ if (!isReady) {
3919
+ svc._state.status = 'offline';
3920
+ this.logger.error({
3921
+ msg: `❌ [${svc.name}] 端口${portNum} WebSocket 服务启动超时`
3922
+ });
3923
+ ws.close();
3924
+ return;
3925
+ }
3926
+ svc._state.status = 'online';
3927
+ svc._state.startTime = Date.now();
3928
+ svc._state.startCount++;
3929
+ }
3930
+ const targetUrl = new external_node_url_namespaceObject.URL(backendTarget);
3931
+ const wsUserData = ws.getUserData();
3932
+ const clientPath = wsUserData.clientPath;
3933
+ const clientHeaders = wsUserData.clientHeaders;
3934
+ const wsUrl = `${'https:' === targetUrl.protocol ? 'wss:' : 'ws:'}//${targetUrl.host}${clientPath}`;
3935
+ if (this.logging.enableWebSocketLog) this.logger.info({
3936
+ msg: `🔌 [${svc.name}] 端口${portNum} 连接后端 WebSocket: ${wsUrl}`
3937
+ });
3938
+ const backendHeaders = {};
3939
+ const skipHeaders = new Set([
3940
+ 'host',
3941
+ 'connection',
3942
+ 'upgrade',
3943
+ 'sec-websocket-key',
3944
+ 'sec-websocket-version'
3945
+ ]);
3946
+ for (const [key, value] of Object.entries(clientHeaders))if (!skipHeaders.has(key.toLowerCase())) backendHeaders[key] = value;
3947
+ backendHeaders['Host'] = targetUrl.host;
3948
+ const backendWs = new wrapper(wsUrl, {
3949
+ headers: backendHeaders
3950
+ });
3951
+ wsState.backendWs = backendWs;
3952
+ backendWs.on('open', ()=>{
3953
+ if (this.logging.enableWebSocketLog) this.logger.info({
3954
+ msg: `✅ [${svc.name}] 端口${portNum} 后端 WebSocket 连接已建立`
3955
+ });
3956
+ wsState.backendReady = true;
3957
+ while(wsState.messageQueue.length > 0 && backendWs.readyState === wrapper.OPEN){
3958
+ const msg = wsState.messageQueue.shift();
3959
+ if (msg) backendWs.send(msg);
3960
+ }
3961
+ });
3962
+ backendWs.on('message', (data, isBinary)=>{
3963
+ if (null !== ws) ws.send(data, isBinary, false);
3964
+ });
3965
+ backendWs.on('close', ()=>{
3966
+ if (null !== ws && !wsState.closing) {
3967
+ wsState.closing = true;
3968
+ ws.close();
3969
+ }
3970
+ });
3971
+ backendWs.on('error', ()=>{
3972
+ wsState.closing = true;
3973
+ if (null !== ws) ws.close();
3974
+ });
3975
+ } catch (error) {
3976
+ const message = error instanceof Error ? error.message : String(error);
3977
+ this.logger.error({
3978
+ msg: `❌ [${service.name}] WebSocket 连接失败`,
3979
+ error: message
3980
+ });
3981
+ if (null !== ws) ws.close();
3982
+ }
3983
+ })();
3984
+ },
3985
+ message: (ws, message, _isBinary)=>{
3986
+ const userData = ws.getUserData();
3987
+ const svc = userData.service;
3988
+ const wsState = ws.wsState;
3989
+ if (wsState.backendReady && wsState.backendWs && wsState.backendWs.readyState === wrapper.OPEN) {
3990
+ wsState.backendWs.send(Buffer.from(message));
3991
+ svc._state.lastAccessTime = Date.now();
3992
+ } else if (wsState.messageQueue.length < GatewayConstants.MAX_WS_MESSAGE_QUEUE_SIZE) wsState.messageQueue.push(Buffer.from(message));
3993
+ },
3994
+ close: (ws)=>{
3995
+ const userData = ws.getUserData();
3996
+ const svc = userData.service;
3997
+ svc._state.activeConnections--;
3998
+ const wsState = ws.wsState;
3999
+ if (wsState?.backendWs && wsState.backendWs.readyState === wrapper.OPEN) {
4000
+ wsState.closing = true;
4001
+ wsState.backendWs.close();
4002
+ }
4003
+ }
4004
+ });
4005
+ app.any('/*', (res, req)=>{
4006
+ this.handlePortBindingRequest(res, req, mapping);
4007
+ });
4008
+ app.listen(host, portNum, (token)=>{
4009
+ if (token) this.logger.info({
4010
+ msg: `🔌 端口绑定已启动: http://${host}:${portNum} -> ${service.name}`
4011
+ });
4012
+ else this.logger.error({
4013
+ msg: `❌ 端口绑定启动失败: ${host}:${portNum}`
4014
+ });
4015
+ });
4016
+ }
4017
+ async cleanup() {
4018
+ this.logger.info({
4019
+ msg: '🧹 正在清理所有服务...'
4020
+ });
4021
+ const cleanedServices = new Set();
4022
+ for (const mapping of this.hostnameRoutes.values())cleanedServices.add(mapping.service);
4023
+ for (const mapping of this.portRoutes.values())cleanedServices.add(mapping.service);
4024
+ const stopPromises = [];
4025
+ for (const service of cleanedServices)if ('online' === service._state.status || 'starting' === service._state.status) stopPromises.push(this.serviceManager.stop(service).catch((err)=>{
4026
+ this.logger.error({
4027
+ msg: `❌ [${service.name}] 停止失败`,
4028
+ error: err.message
4029
+ });
4030
+ }));
4031
+ await Promise.all(stopPromises);
4032
+ this.logger.info({
4033
+ msg: `✅ 已清理 ${cleanedServices.size} 个服务`
4034
+ });
3304
4035
  }
3305
4036
  }
3306
4037
  const external_c12_namespaceObject = require("c12");
3307
4038
  async function loadDynaPMConfig() {
4039
+ const configFile = process.env.DYNAPM_CONFIG || void 0;
3308
4040
  const { config } = await (0, external_c12_namespaceObject.loadConfig)({
3309
4041
  name: 'dynapm',
4042
+ configFile,
3310
4043
  defaultConfig: {
3311
4044
  port: 3000,
3312
4045
  host: '127.0.0.1',
3313
- services: {}
4046
+ services: {},
4047
+ adminApi: {
4048
+ enabled: true,
4049
+ port: 4000,
4050
+ allowedIps: [
4051
+ '127.0.0.1',
4052
+ '::1'
4053
+ ]
4054
+ }
3314
4055
  }
3315
4056
  });
3316
4057
  if (!config.services || 0 === Object.keys(config.services).length) throw new Error('配置文件中至少需要一个服务');
3317
- for (const [hostname, service] of Object.entries(config.services)){
3318
- service.name = service.name || hostname;
4058
+ const mainPort = config.port || 3000;
4059
+ if (config.adminApi?.enabled && config.adminApi.port === mainPort) throw new Error(`管理 API 端口 ${mainPort} 与主端口冲突`);
4060
+ const portMap = new Map();
4061
+ const hostnameMap = new Map();
4062
+ for (const [key, service] of Object.entries(config.services)){
4063
+ service.name = service.name || key;
3319
4064
  service.idleTimeout = service.idleTimeout || 300000;
3320
4065
  service.startTimeout = service.startTimeout || 30000;
3321
4066
  service.healthCheck = service.healthCheck || {
3322
4067
  type: 'tcp'
3323
4068
  };
3324
4069
  if ('http' === service.healthCheck.type && !service.healthCheck.url) service.healthCheck.url = service.base;
4070
+ let routes = service.routes;
4071
+ if (!routes || 0 === routes.length) {
4072
+ routes = [];
4073
+ if (service.host) routes.push({
4074
+ type: 'host',
4075
+ value: service.host,
4076
+ target: service.base
4077
+ });
4078
+ if (service.port) routes.push({
4079
+ type: 'port',
4080
+ value: service.port,
4081
+ target: service.base
4082
+ });
4083
+ if (0 === routes.length) routes.push({
4084
+ type: 'host',
4085
+ value: key,
4086
+ target: service.base
4087
+ });
4088
+ service.routes = routes;
4089
+ }
4090
+ for (const route of routes)if (!route.target) throw new Error(`服务 [${service.name}] 的路由配置缺少 target 字段`);
4091
+ for (const route of routes)if ('port' === route.type) {
4092
+ const port = route.value;
4093
+ if (port === mainPort) throw new Error(`服务 [${service.name}] 的路由端口 ${port} 与主端口冲突`);
4094
+ const adminApiPort = config.adminApi?.enabled ? config.adminApi.port : void 0;
4095
+ if (adminApiPort && port === adminApiPort) throw new Error(`服务 [${service.name}] 的路由端口 ${port} 与管理 API 端口冲突`);
4096
+ const existingService = portMap.get(port);
4097
+ if (existingService) throw new Error(`端口冲突: 服务 [${service.name}] 和 [${existingService}] 都配置了端口 ${port}`);
4098
+ portMap.set(port, service.name);
4099
+ } else if ('host' === route.type) {
4100
+ const hostname = route.value;
4101
+ const existingService = hostnameMap.get(hostname);
4102
+ if (existingService && existingService !== service.name) throw new Error(`Hostname 冲突: 服务 [${service.name}] 和 [${existingService}] 都配置了 hostname ${hostname}`);
4103
+ hostnameMap.set(hostname, service.name);
4104
+ }
3325
4105
  }
3326
4106
  return config;
3327
4107
  }
@@ -3377,6 +4157,20 @@ var __webpack_exports__ = {};
3377
4157
  msg: 'DynaPM 网关已启动',
3378
4158
  port: config.port || 3000
3379
4159
  });
4160
+ const cleanup = async (signal)=>{
4161
+ logger.info({
4162
+ msg: `⚠️ 收到 ${signal} 信号,正在清理...`
4163
+ });
4164
+ await gateway.cleanup();
4165
+ process.exit(0);
4166
+ };
4167
+ process.on('SIGINT', ()=>cleanup('SIGINT'));
4168
+ process.on('SIGTERM', ()=>cleanup('SIGTERM'));
4169
+ process.on('exit', ()=>{
4170
+ logger.info({
4171
+ msg: '👋 DynaPM 网关已退出'
4172
+ });
4173
+ });
3380
4174
  } catch (error) {
3381
4175
  logger.error({
3382
4176
  msg: '启动失败',
@@ -3387,8 +4181,7 @@ var __webpack_exports__ = {};
3387
4181
  }
3388
4182
  main();
3389
4183
  })();
3390
- var __webpack_export_target__ = exports;
3391
- for(var __webpack_i__ in __webpack_exports__)__webpack_export_target__[__webpack_i__] = __webpack_exports__[__webpack_i__];
3392
- if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, '__esModule', {
4184
+ for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
4185
+ Object.defineProperty(exports, '__esModule', {
3393
4186
  value: true
3394
4187
  });