@abtnode/router-provider 1.16.43-beta-20250424-125523-08a65a5c → 1.16.43-beta-20250427-132304-6da95c55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/nginx/index.js +86 -8
  2. package/package.json +7 -6
@@ -4,6 +4,7 @@ const { NginxConfFile } = require('nginx-conf');
4
4
  const fs = require('fs-extra');
5
5
  const os = require('os');
6
6
  const fg = require('fast-glob');
7
+ const semver = require('semver');
7
8
  const path = require('path');
8
9
  const shelljs = require('shelljs');
9
10
  const kill = require('fkill');
@@ -22,6 +23,8 @@ const {
22
23
  USER_AVATAR_PATH_PREFIX,
23
24
  LOG_RETAIN_IN_DAYS,
24
25
  ROUTER_CACHE_GROUPS,
26
+ GATEWAY_RATE_LIMIT_GLOBAL,
27
+ GATEWAY_RATE_LIMIT,
25
28
  } = require('@abtnode/constant');
26
29
 
27
30
  const promiseRetry = require('promise-retry');
@@ -121,6 +124,7 @@ class NginxProvider extends BaseProvider {
121
124
  this.capabilities = this._checkCapabilities();
122
125
 
123
126
  this.conf = null; // nginx `conf` object
127
+ this.requestLimit = null;
124
128
 
125
129
  logger.info('nginx provider config', {
126
130
  configDir,
@@ -206,6 +210,7 @@ class NginxProvider extends BaseProvider {
206
210
  this._addCommonResHeaders(conf.nginx.http, commonHeaders);
207
211
  this._addExposeServices(conf, services);
208
212
  if (requestLimit && requestLimit.enabled) {
213
+ this.requestLimit = requestLimit;
209
214
  this.addRequestLimiting(conf.nginx.http, requestLimit);
210
215
  }
211
216
  if (blockPolicy && blockPolicy?.enabled) {
@@ -273,6 +278,8 @@ class NginxProvider extends BaseProvider {
273
278
  this._addDefaultServer(conf, nodeInfo.port);
274
279
  }
275
280
 
281
+ this._addStubStatusLocation(conf);
282
+
276
283
  conf.flush();
277
284
  });
278
285
  });
@@ -366,7 +373,7 @@ class NginxProvider extends BaseProvider {
366
373
  _checkCapabilities() {
367
374
  const config = this.readNginxConfigParams();
368
375
 
369
- const capabilities = ['http_v2', 'http_geoip'].reduce((acc, key) => {
376
+ const capabilities = ['http_v2', 'http_geoip', 'http_stub_status'].reduce((acc, key) => {
370
377
  const arg = `with-${key}_module`;
371
378
  const capability = camelCase(key);
372
379
 
@@ -393,6 +400,11 @@ class NginxProvider extends BaseProvider {
393
400
  capabilities.modsecurity = modulePaths.some((x) => toLower(x).includes('modsecurity'));
394
401
  }
395
402
 
403
+ const result = shelljs.exec(`${this.binPath} -v`, { silent: true });
404
+ if (result.code === 0) {
405
+ capabilities.version = semver.coerce((result.stderr || result.stdout).split('/').pop())?.version || '';
406
+ }
407
+
396
408
  return capabilities;
397
409
  }
398
410
 
@@ -791,14 +803,43 @@ class NginxProvider extends BaseProvider {
791
803
  location._add('add_header', 'X-Request-ID $request_id');
792
804
  }
793
805
 
806
+ _addStubStatusLocation(conf) {
807
+ if (!conf?.nginx?.http?.server) {
808
+ return;
809
+ }
810
+
811
+ if (!this.capabilities?.httpStubStatus) {
812
+ return;
813
+ }
814
+
815
+ const servers = Array.isArray(conf.nginx.http.server) ? conf.nginx.http.server : [conf.nginx.http.server];
816
+ const server = servers.find((x) => x.server_name._value === '127.0.0.1');
817
+ if (!server) {
818
+ return;
819
+ }
820
+
821
+ server._add('location', '/__nginx_status');
822
+ const location = this._getLastLocation(server);
823
+ location._add('stub_status', 'on');
824
+ location._add('access_log', 'off');
825
+ location._add('allow', '127.0.0.1');
826
+ location._add('deny', 'all');
827
+ }
828
+
794
829
  _getLastServer(conf) {
795
830
  return conf.nginx.http.server.length
796
831
  ? conf.nginx.http.server[conf.nginx.http.server.length - 1]
797
832
  : conf.nginx.http.server;
798
833
  }
799
834
 
800
- _getLastLocation(serverConf) {
801
- return serverConf.location.length ? serverConf.location[serverConf.location.length - 1] : serverConf.location;
835
+ _getLastLocation(server) {
836
+ const location = server.location.length ? server.location[server.location.length - 1] : server.location;
837
+ if (location && this.requestLimit?.enabled && !location.toString().includes('limit_req')) {
838
+ location._add('limit_req', `zone=ip_rate_limit burst=${this.requestLimit.burst} delay=10`);
839
+ location._add('limit_req', `zone=global_rate_limit burst=${this.requestLimit.burstGlobal} nodelay`);
840
+ }
841
+
842
+ return location;
802
843
  }
803
844
 
804
845
  _getLastStreamServer(conf) {
@@ -843,7 +884,7 @@ class NginxProvider extends BaseProvider {
843
884
  locations.forEach((x) => this._addReverseProxy({ server: httpsServerUnit, ...x, serverName, corsAllowedOrigins, commonHeaders, blockletDid })); // prettier-ignore
844
885
  }
845
886
 
846
- _addHttpServerUnit({ conf, serverName, port }) {
887
+ _addHttpServerUnit({ conf, serverName, port = '' }) {
847
888
  let listen = port || this.httpPort;
848
889
  if (serverName === '_') {
849
890
  listen = `${listen} default_server`;
@@ -865,13 +906,24 @@ class NginxProvider extends BaseProvider {
865
906
  const crtPath = `${joinNginxPath(this.certDir, certificateFileName.replace('*', '-'))}.crt`;
866
907
  const keyPath = `${joinNginxPath(this.certDir, certificateFileName.replace('*', '-'))}.key`;
867
908
 
868
- let listen = `${this.httpsPort} ssl ${this.capabilities.httpV2 ? 'http2' : ''}`.trim();
909
+ // Handle HTTP/2 configuration based on Nginx version
910
+ let listen = `${this.httpsPort} ssl`;
911
+ let http2Config = '';
912
+ if (this.capabilities.httpV2) {
913
+ if (this.capabilities.version && semver.gte(this.capabilities.version, '1.25.1')) {
914
+ http2Config = 'http2 on';
915
+ } else {
916
+ listen += ' http2';
917
+ }
918
+ }
869
919
  if (serverName === '_') {
870
920
  listen = `${listen} default_server`;
871
921
  }
872
-
873
922
  httpsServerUnit._add('server_name', serverName);
874
923
  httpsServerUnit._add('listen', listen);
924
+ if (http2Config) {
925
+ httpsServerUnit._add('http2', 'on');
926
+ }
875
927
  httpsServerUnit._add('ssl_certificate', crtPath);
876
928
  httpsServerUnit._add('ssl_certificate_key', keyPath);
877
929
  httpsServerUnit._add('include', 'includes/ssl');
@@ -958,8 +1010,34 @@ class NginxProvider extends BaseProvider {
958
1010
  }
959
1011
 
960
1012
  addRequestLimiting(block, limit) {
961
- block._add('limit_req_zone', `$binary_remote_addr zone=req_limit_per_ip:20m rate=${limit.rate || 5}r/s`);
962
- block._add('limit_req', `zone=req_limit_per_ip burst=${limit.maxInstantRate || 30} delay=10`);
1013
+ if (!limit?.enabled) {
1014
+ return;
1015
+ }
1016
+
1017
+ const resourceExts =
1018
+ 'jpg|jpeg|png|gif|webp|svg|ico|css|js|jsx|ts|tsx|mjs|woff|woff2|ttf|eot|otf|json|bmp|avif|wasm|xml|txt';
1019
+
1020
+ // limit by global
1021
+ block._addVerbatimBlock(
1022
+ 'map $request_uri $global_rate_limit_key',
1023
+ `${os.EOL}${[`~*\\.(${resourceExts})$ "";`, 'default global_rate_limit;'].join(os.EOL)}${os.EOL}`
1024
+ );
1025
+
1026
+ // limit by ip
1027
+ block._addVerbatimBlock(
1028
+ 'map $request_uri:$request_method $ip_rate_limit_key',
1029
+ `${os.EOL}${[`~*\\.(${resourceExts})(\\?.*)?: "";`, `~*:(${limit.methods.join('|')})$ ip_rate_limit;`, 'default "";'].join(os.EOL)}${os.EOL}`
1030
+ );
1031
+
1032
+ // link: https://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_zone
1033
+ block._add(
1034
+ 'limit_req_zone',
1035
+ `$global_rate_limit_key zone=global_rate_limit:128k rate=${limit.global || GATEWAY_RATE_LIMIT_GLOBAL.min}r/s`
1036
+ );
1037
+ block._add(
1038
+ 'limit_req_zone',
1039
+ `$ip_rate_limit_key zone=ip_rate_limit:5m rate=${limit.rate || GATEWAY_RATE_LIMIT.min}r/s`
1040
+ );
963
1041
  block._add('limit_req_status', 429);
964
1042
  }
965
1043
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abtnode/router-provider",
3
- "version": "1.16.43-beta-20250424-125523-08a65a5c",
3
+ "version": "1.16.43-beta-20250427-132304-6da95c55",
4
4
  "description": "Routing engine implementations for abt node",
5
5
  "author": "polunzh <polunzh@gmail.com>",
6
6
  "homepage": "https://github.com/ArcBlock/blocklet-server#readme",
@@ -32,10 +32,10 @@
32
32
  "url": "https://github.com/ArcBlock/blocklet-server/issues"
33
33
  },
34
34
  "dependencies": {
35
- "@abtnode/constant": "1.16.43-beta-20250424-125523-08a65a5c",
36
- "@abtnode/logger": "1.16.43-beta-20250424-125523-08a65a5c",
37
- "@abtnode/router-templates": "1.16.43-beta-20250424-125523-08a65a5c",
38
- "@abtnode/util": "1.16.43-beta-20250424-125523-08a65a5c",
35
+ "@abtnode/constant": "1.16.43-beta-20250427-132304-6da95c55",
36
+ "@abtnode/logger": "1.16.43-beta-20250427-132304-6da95c55",
37
+ "@abtnode/router-templates": "1.16.43-beta-20250427-132304-6da95c55",
38
+ "@abtnode/util": "1.16.43-beta-20250427-132304-6da95c55",
39
39
  "@arcblock/http-proxy": "^1.19.1",
40
40
  "@arcblock/is-valid-domain": "^1.0.5",
41
41
  "axios": "^1.7.9",
@@ -51,6 +51,7 @@
51
51
  "object-hash": "^3.0.0",
52
52
  "port-used": "^2.0.8",
53
53
  "promise-retry": "^2.0.1",
54
+ "semver": "^7.6.3",
54
55
  "shelljs": "^0.8.5",
55
56
  "tar": "^6.1.11",
56
57
  "ufo": "^1.5.3",
@@ -60,5 +61,5 @@
60
61
  "bluebird": "^3.7.2",
61
62
  "fs-extra": "^11.2.0"
62
63
  },
63
- "gitHead": "499de9f28770298a819122ba16a7f2b3cc78c7ee"
64
+ "gitHead": "9df10dcb3135a528912241e7fc1ed54171bfeb03"
64
65
  }