t880216t-server 1.5.20

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 (104) hide show
  1. package/LICENSE +124 -0
  2. package/README.md +191 -0
  3. package/bin/nohost.js +178 -0
  4. package/bin/plugin.js +70 -0
  5. package/bin/util.js +93 -0
  6. package/index.js +89 -0
  7. package/lib/config.js +42 -0
  8. package/lib/index.js +205 -0
  9. package/lib/main/cgi/getSettings.js +11 -0
  10. package/lib/main/cgi/getVersion.js +12 -0
  11. package/lib/main/cgi/login.js +14 -0
  12. package/lib/main/cgi/restart.js +5 -0
  13. package/lib/main/cgi/setAdmin.js +6 -0
  14. package/lib/main/cgi/setDomain.js +13 -0
  15. package/lib/main/cgi/status.js +122 -0
  16. package/lib/main/index.js +94 -0
  17. package/lib/main/router.js +79 -0
  18. package/lib/main/storage.js +132 -0
  19. package/lib/main/util.js +138 -0
  20. package/lib/main/whistleMgr.js +56 -0
  21. package/lib/main/worker.js +52 -0
  22. package/lib/main/workerNum.js +11 -0
  23. package/lib/plugins/account/whistle.share/menu.html +171 -0
  24. package/lib/plugins/account/whistle.share/package.json +20 -0
  25. package/lib/plugins/account/whistle.storage/index.js +43 -0
  26. package/lib/plugins/account/whistle.storage/package.json +8 -0
  27. package/lib/plugins/account/whistle.storage/rules.txt +1 -0
  28. package/lib/plugins/whistle.nohost/_rules.txt +38 -0
  29. package/lib/plugins/whistle.nohost/index.js +3 -0
  30. package/lib/plugins/whistle.nohost/initial.js +9 -0
  31. package/lib/plugins/whistle.nohost/lib/accountMgr.js +750 -0
  32. package/lib/plugins/whistle.nohost/lib/envMgr.js +114 -0
  33. package/lib/plugins/whistle.nohost/lib/rulesServer.js +85 -0
  34. package/lib/plugins/whistle.nohost/lib/tunnelRulesServer.js +71 -0
  35. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/account/changePassword.js +8 -0
  36. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/activeAccount.js +9 -0
  37. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/addAccount.js +8 -0
  38. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/allAccounts.js +9 -0
  39. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/changeNotice.js +8 -0
  40. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/changePassword.js +8 -0
  41. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/enableGuest.js +5 -0
  42. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/getAuthKey.js +3 -0
  43. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/getSettings.js +22 -0
  44. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/login.js +14 -0
  45. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/move.js +9 -0
  46. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/removeAccount.js +9 -0
  47. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setAccountRules.js +5 -0
  48. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setAuthKey.js +5 -0
  49. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setDefaultRules.js +5 -0
  50. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setEntryPatterns.js +6 -0
  51. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setJsonData.js +5 -0
  52. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setRulesTpl.js +5 -0
  53. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setTestRules.js +5 -0
  54. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/specPattern.js +4 -0
  55. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/allowlist.js +4 -0
  56. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/entryRules.js +4 -0
  57. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/follow.js +17 -0
  58. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/getEnv.js +12 -0
  59. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/list.js +43 -0
  60. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/patterns.js +14 -0
  61. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/pluginRules.js +61 -0
  62. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/proxy.js +42 -0
  63. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/proxyNetwork.js +12 -0
  64. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/redirect.js +10 -0
  65. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/selectEnv.js +30 -0
  66. package/lib/plugins/whistle.nohost/lib/uiServer/cgi/unfollow.js +4 -0
  67. package/lib/plugins/whistle.nohost/lib/uiServer/index.js +60 -0
  68. package/lib/plugins/whistle.nohost/lib/uiServer/network.js +38 -0
  69. package/lib/plugins/whistle.nohost/lib/uiServer/openAPI.js +94 -0
  70. package/lib/plugins/whistle.nohost/lib/uiServer/router.js +85 -0
  71. package/lib/plugins/whistle.nohost/lib/util.js +230 -0
  72. package/lib/plugins/whistle.nohost/lib/whistle.js +149 -0
  73. package/lib/plugins/whistle.nohost/lib/whistleMgr.js +94 -0
  74. package/lib/plugins/whistle.nohost/package.json +7 -0
  75. package/lib/plugins/whistle.nohost/rules.txt +3 -0
  76. package/lib/service/cgi/export.js +76 -0
  77. package/lib/service/cgi/import.js +33 -0
  78. package/lib/service/cgi/util.js +35 -0
  79. package/lib/service/index.js +37 -0
  80. package/lib/service/router.js +7 -0
  81. package/lib/service/server.js +25 -0
  82. package/lib/util/address.js +55 -0
  83. package/lib/util/getPort.js +19 -0
  84. package/lib/util/login.js +55 -0
  85. package/lib/util/parseDomain.js +17 -0
  86. package/lib/whistle.js +86 -0
  87. package/package.json +135 -0
  88. package/pnpm-workspace.yaml +6 -0
  89. package/public/admin.75d42731d4aa7f7a558d3abf2a375fcb.js +2 -0
  90. package/public/admin.75d42731d4aa7f7a558d3abf2a375fcb.js.LICENSE.txt +76 -0
  91. package/public/admin.html +11 -0
  92. package/public/button.js +2 -0
  93. package/public/button.js.LICENSE.txt +31 -0
  94. package/public/capture.7bab900f27c9bb1b0e33523e994554ae.js +2 -0
  95. package/public/capture.7bab900f27c9bb1b0e33523e994554ae.js.LICENSE.txt +83 -0
  96. package/public/capture.html +11 -0
  97. package/public/eed368d0656f03932671530c3132ed61.svg +1 -0
  98. package/public/favicon.ico +0 -0
  99. package/public/network.e4814ec8c966a8a789296679619ec573.js +2 -0
  100. package/public/network.e4814ec8c966a8a789296679619ec573.js.LICENSE.txt +76 -0
  101. package/public/network.html +11 -0
  102. package/public/select.cb638be6656d02a558f2690acd59901a.js +2 -0
  103. package/public/select.cb638be6656d02a558f2690acd59901a.js.LICENSE.txt +24 -0
  104. package/public/select.html +26 -0
@@ -0,0 +1,114 @@
1
+ const LRU = require('lru-cache');
2
+ const {
3
+ COOKIE_NAME,
4
+ WHISTLE_ENV_HEADER,
5
+ decodeURIComponentSafe,
6
+ getClientId,
7
+ isFromComposer,
8
+ } = require('./util');
9
+
10
+ const followers = new LRU({ max: 10000, maxAge: 1000 * 60 * 30 });
11
+ const cache = new LRU({ max: 10000 });
12
+ let accountMgr;
13
+
14
+ class EnvMgr {
15
+ checkEnvName({ envList }, envName) {
16
+ if (!envName) {
17
+ return '';
18
+ }
19
+ return envList.some(({ name }) => name === envName) ? envName : '';
20
+ }
21
+
22
+ setFollower(followId, ctx) {
23
+ const clientId = getClientId(ctx);
24
+ if (clientId !== followId) {
25
+ followers.set(followId, clientId);
26
+ }
27
+ }
28
+
29
+ unfollow(ctx) {
30
+ followers.del(getClientId(ctx));
31
+ }
32
+
33
+ getFollower(ctx) {
34
+ return followers.get(getClientId(ctx));
35
+ }
36
+
37
+ setEnv(ip, name, envName) {
38
+ const account = accountMgr.getAccount(name);
39
+ if (!account) {
40
+ cache.set(ip, '');
41
+ return;
42
+ }
43
+ envName = this.checkEnvName(account, envName);
44
+ const env = { name, envName };
45
+ cache.set(ip, env);
46
+ return env;
47
+ }
48
+
49
+ getEnvFromCookie(ctx, withAccount) {
50
+ let env = decodeURIComponentSafe(ctx.cookies.get(COOKIE_NAME));
51
+ if (!env) {
52
+ return '';
53
+ }
54
+ const index = env.indexOf('/');
55
+ let name = env;
56
+ let envName;
57
+ if (index !== -1) {
58
+ name = env.substring(0, index);
59
+ envName = env.substring(index + 1);
60
+ }
61
+ const account = accountMgr.getAccount(name);
62
+ if (!account) {
63
+ return '';
64
+ }
65
+ envName = this.checkEnvName(account, envName);
66
+ env = { name, envName };
67
+ return withAccount ? { account, env } : { name, envName };
68
+ }
69
+
70
+ getEnv(ctx) {
71
+ let env = cache.get(getClientId(ctx));
72
+ if (env != null) {
73
+ if (env) {
74
+ const { name, envName } = env;
75
+ const account = accountMgr.getAccount(name);
76
+ if (!account || !this.checkEnvName(account, envName)) {
77
+ env.envName = '';
78
+ }
79
+ }
80
+ return env;
81
+ }
82
+ env = this.getEnvFromCookie(ctx);
83
+ cache.set(getClientId(ctx), env);
84
+ return env;
85
+ }
86
+
87
+ getEnvOnly(ctx) {
88
+ return cache.get(getClientId(ctx));
89
+ }
90
+
91
+ getEnvByHeader(ctx) {
92
+ let name = decodeURIComponentSafe(ctx.get(WHISTLE_ENV_HEADER));
93
+ if (!name) {
94
+ return (isFromComposer(ctx) && this.getEnvFromCookie(ctx, true)) || '';
95
+ }
96
+ const index = name.indexOf('/');
97
+ let envName = '';
98
+ if (index !== -1) {
99
+ envName = name.substring(index + 1);
100
+ name = name.substring(0, index);
101
+ }
102
+ const account = accountMgr.getAccount(name);
103
+ if (!account) {
104
+ return '';
105
+ }
106
+ envName = this.checkEnvName(account, envName);
107
+ return { account, env: { name, envName } };
108
+ }
109
+ }
110
+
111
+ module.exports = (mgr) => {
112
+ accountMgr = mgr;
113
+ module.exports = new EnvMgr();
114
+ };
@@ -0,0 +1,85 @@
1
+ const Koa = require('koa');
2
+ const accountMgr = require('./accountMgr');
3
+ const envMgr = require('./envMgr');
4
+ const whistleMgr = require('./whistleMgr');
5
+ const {
6
+ COOKIE_NAME,
7
+ ENV_MAX_AGE,
8
+ WHISTLE_ENV_HEADER,
9
+ WHISTLE_RULE_VALUE,
10
+ getRuleValue,
11
+ getDomain,
12
+ } = require('./util');
13
+
14
+ const getCookie = (value, maxAge, hostname) => {
15
+ return {
16
+ whistleEnvCookie: {
17
+ [COOKIE_NAME]: {
18
+ value,
19
+ maxAge,
20
+ domain: getDomain(hostname),
21
+ path: '/',
22
+ },
23
+ },
24
+ };
25
+ };
26
+
27
+ module.exports = (server) => {
28
+ const app = new Koa();
29
+ app.proxy = true;
30
+ app.silent = true;
31
+ app.use(async (ctx) => {
32
+ let { account, env } = envMgr.getEnvByHeader(ctx);
33
+ if (!account && !ctx.get(WHISTLE_ENV_HEADER)) {
34
+ env = envMgr.getEnv(ctx);
35
+ account = env && accountMgr.getAccount(env.name);
36
+ }
37
+ const hostname = (ctx.get('host') || '').split(':')[0];
38
+ const noInject = getRuleValue(ctx) === 'none' || ctx.get('x-whistle-nohost-hide');
39
+ const injectRule = `htmlPrepend://{whistle.nohost/${noInject ? 'none' : 'inject'}.html} enable://safeHtml`;
40
+ if (!account) {
41
+ ctx.body = {
42
+ rules: `* resCookies://{whistleEnvCookie} ${injectRule}`,
43
+ values: getCookie('', -ENV_MAX_AGE, hostname),
44
+ };
45
+ return;
46
+ }
47
+ const name = account && account.name;
48
+ let envHeader = '';
49
+ let envKey = `${name}/`;
50
+ const { envName } = env;
51
+ const reqHeaders = {};
52
+
53
+ if (envName) {
54
+ envKey += envName;
55
+ let { rules, headers } = accountMgr.getRules(account.name, envName);
56
+ if (headers) {
57
+ headers = JSON.stringify(headers);
58
+ rules += `\n* reqHeaders://(${headers})`;
59
+ }
60
+ if (rules) {
61
+ reqHeaders[WHISTLE_RULE_VALUE] = encodeURIComponent(rules);
62
+ }
63
+ }
64
+ reqHeaders[WHISTLE_ENV_HEADER] = encodeURIComponent(envKey);
65
+ const {
66
+ port,
67
+ remoteAddrHead,
68
+ remotePortHead,
69
+ } = await whistleMgr.fork(account);
70
+ const {
71
+ remoteAddress,
72
+ remotePort,
73
+ } = ctx.req.originalReq;
74
+ reqHeaders[remoteAddrHead] = remoteAddress;
75
+ reqHeaders[remotePortHead] = remotePort;
76
+ envHeader = `reqHeaders://(${JSON.stringify(reqHeaders)})`;
77
+ const proxyUrl = `internal-proxy://127.0.0.1:${port}`;
78
+ const cookie = getCookie(envKey, ENV_MAX_AGE, hostname);
79
+ ctx.body = {
80
+ rules: `* ${proxyUrl} ${envHeader} ${injectRule} ${cookie ? 'resCookies://{whistleEnvCookie}' : ''}`,
81
+ values: cookie,
82
+ };
83
+ });
84
+ server.on('request', app.callback());
85
+ };
@@ -0,0 +1,71 @@
1
+ const Koa = require('koa');
2
+ const accountMgr = require('./accountMgr');
3
+ const envMgr = require('./envMgr');
4
+ const whistleMgr = require('./whistleMgr');
5
+ const {
6
+ WHISTLE_ENV_HEADER,
7
+ WHISTLE_RULE_VALUE,
8
+ } = require('./util');
9
+
10
+ const SEP_RE = /\s+/;
11
+ const CRLF_RE = /\s*[\r\n]+\s*/;
12
+ const CAPTURE_RE = /^(?:[^\r\n]*\s)?enable:\/\/(?:capture|https|intercept)(?:\s|$)/m;
13
+
14
+ module.exports = (server) => {
15
+ const app = new Koa();
16
+ app.proxy = true;
17
+ app.silent = true;
18
+ app.use(async (ctx) => {
19
+ let { account, env } = envMgr.getEnvByHeader(ctx);
20
+ if (!account) {
21
+ env = envMgr.getEnvOnly(ctx);
22
+ account = env && accountMgr.getAccount(env.name);
23
+ }
24
+ let proxyUrl = '';
25
+ if (account) {
26
+ const headers = {};
27
+ let envValue = `${account.name}/`;
28
+ if (env) {
29
+ const { envName } = env;
30
+ if (envName) {
31
+ envValue = `${envValue}${envName}`;
32
+ const { rules } = accountMgr.getRules(account.name, envName);
33
+ if (rules) {
34
+ // 设置了拦截https请求,则所有该环境的请求都开启
35
+ if (CAPTURE_RE.test(rules)) {
36
+ const capRules = [];
37
+ rules.trim().split(CRLF_RE).forEach((line) => {
38
+ if (CAPTURE_RE.test(line)) {
39
+ line.trim().split(SEP_RE).forEach(p => {
40
+ if (p) {
41
+ capRules.push(`enable://capture ${p}`);
42
+ }
43
+ });
44
+ }
45
+ });
46
+ ctx.body = capRules.join('\n');
47
+ return;
48
+ }
49
+ headers[WHISTLE_RULE_VALUE] = encodeURIComponent(rules);
50
+ }
51
+ }
52
+ }
53
+ headers[WHISTLE_ENV_HEADER] = encodeURIComponent(envValue);
54
+ const {
55
+ port,
56
+ remoteAddrHead,
57
+ remotePortHead,
58
+ } = await whistleMgr.fork(account);
59
+ const {
60
+ remoteAddress,
61
+ remotePort,
62
+ } = ctx.req.originalReq;
63
+ headers[remoteAddrHead] = remoteAddress;
64
+ headers[remotePortHead] = remotePort;
65
+ const envHeader = `reqHeaders://(${JSON.stringify(headers)})`;
66
+ proxyUrl = `internal-proxy://127.0.0.1:${port}`;
67
+ ctx.body = `* ${proxyUrl} ${envHeader}`;
68
+ }
69
+ });
70
+ server.on('request', app.callback());
71
+ };
@@ -0,0 +1,8 @@
1
+ module.exports = (ctx) => {
2
+ const { password } = ctx.request.body;
3
+ if (ctx.accountMgr.changePassword(ctx.user.name, password)) {
4
+ ctx.success();
5
+ } else {
6
+ ctx.error();
7
+ }
8
+ };
@@ -0,0 +1,9 @@
1
+
2
+ module.exports = (ctx) => {
3
+ const { name, active } = ctx.request.body;
4
+ if (ctx.accountMgr.activeAccount(name, active !== 'false')) {
5
+ ctx.success();
6
+ } else {
7
+ ctx.error();
8
+ }
9
+ };
@@ -0,0 +1,8 @@
1
+
2
+ module.exports = (ctx) => {
3
+ if (ctx.accountMgr.addAccount(ctx.request.body)) {
4
+ ctx.success();
5
+ } else {
6
+ ctx.error();
7
+ }
8
+ };
@@ -0,0 +1,9 @@
1
+
2
+ module.exports = (ctx) => {
3
+ const { accountMgr } = ctx;
4
+ ctx.body = {
5
+ ec: 0,
6
+ enableGuest: accountMgr.isEnableGuest(),
7
+ list: accountMgr.getAllAccounts(),
8
+ };
9
+ };
@@ -0,0 +1,8 @@
1
+ module.exports = (ctx) => {
2
+ const { name, notice } = ctx.request.body;
3
+ if (ctx.accountMgr.changeNotice(name, notice)) {
4
+ ctx.success();
5
+ } else {
6
+ ctx.error();
7
+ }
8
+ };
@@ -0,0 +1,8 @@
1
+ module.exports = (ctx) => {
2
+ const { name, password } = ctx.request.body;
3
+ if (ctx.accountMgr.changePassword(name, password)) {
4
+ ctx.success();
5
+ } else {
6
+ ctx.error();
7
+ }
8
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = (ctx) => {
2
+ const { enableGuest } = ctx.request.body;
3
+ ctx.accountMgr.enableGuest(!!enableGuest);
4
+ ctx.success();
5
+ };
@@ -0,0 +1,3 @@
1
+ module.exports = (ctx) => {
2
+ ctx.body = { ec: 0, authKey: ctx.accountMgr.getAuthKey() };
3
+ };
@@ -0,0 +1,22 @@
1
+ module.exports = (ctx) => {
2
+ const {
3
+ jsonDataStr,
4
+ rulesTpl,
5
+ defaultRules,
6
+ accountRules,
7
+ testRules,
8
+ entryPatterns,
9
+ specPattern,
10
+ } = ctx.accountMgr;
11
+ ctx.body = {
12
+ ec: 0,
13
+ jsonData: jsonDataStr,
14
+ authKey: ctx.accountMgr.getAuthKey(),
15
+ rulesTpl,
16
+ defaultRules,
17
+ accountRules,
18
+ testRules,
19
+ entryPatterns,
20
+ specPattern,
21
+ };
22
+ };
@@ -0,0 +1,14 @@
1
+ const { checkLogin } = require('../../../../../../util/login');
2
+
3
+ module.exports = async (ctx, next) => {
4
+ const { username, password } = ctx.admin;
5
+ const accept = checkLogin(ctx, {
6
+ username,
7
+ password,
8
+ nameKey: 'nohost_admin_name',
9
+ authKey: 'nohost_admin_key',
10
+ });
11
+ if (accept) {
12
+ await next();
13
+ }
14
+ };
@@ -0,0 +1,9 @@
1
+
2
+ module.exports = (ctx) => {
3
+ const { fromName, toName } = ctx.request.body;
4
+ if (ctx.accountMgr.moveAccount(fromName, toName)) {
5
+ ctx.success();
6
+ } else {
7
+ ctx.error();
8
+ }
9
+ };
@@ -0,0 +1,9 @@
1
+ module.exports = (ctx) => {
2
+ const { name } = ctx.request.body;
3
+ if (ctx.accountMgr.removeAccount(name)) {
4
+ ctx.whistleMgr.kill(name);
5
+ ctx.success();
6
+ } else {
7
+ ctx.error();
8
+ }
9
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = (ctx) => {
2
+ const { rules } = ctx.request.body;
3
+ ctx.accountMgr.setAccountRules(rules);
4
+ ctx.body = { ec: 0 };
5
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = (ctx) => {
2
+ const { authKey } = ctx.request.body;
3
+ ctx.accountMgr.setAuthKey(authKey);
4
+ ctx.body = { ec: 0 };
5
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = (ctx) => {
2
+ const { defaultRules } = ctx.request.body;
3
+ ctx.accountMgr.setDefaultRules(defaultRules);
4
+ ctx.body = { ec: 0 };
5
+ };
@@ -0,0 +1,6 @@
1
+ module.exports = (ctx) => {
2
+ const { entryPatterns } = ctx.request.body;
3
+ ctx.accountMgr.setEntryPatterns(entryPatterns);
4
+ ctx.body = { ec: 0 };
5
+ ctx.updateRules();
6
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = (ctx) => {
2
+ const { jsonData } = ctx.request.body;
3
+ ctx.accountMgr.setJsonData(jsonData);
4
+ ctx.body = { ec: 0 };
5
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = (ctx) => {
2
+ const { rulesTpl } = ctx.request.body;
3
+ ctx.accountMgr.setRulesTpl(rulesTpl);
4
+ ctx.body = { ec: 0 };
5
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = (ctx) => {
2
+ const { testRules } = ctx.request.body;
3
+ ctx.accountMgr.setTestRules(testRules);
4
+ ctx.body = { ec: 0 };
5
+ };
@@ -0,0 +1,4 @@
1
+ module.exports = (ctx) => {
2
+ ctx.accountMgr.setSpecPattern(ctx.request.body.specPattern);
3
+ ctx.body = { ec: 0 };
4
+ };
@@ -0,0 +1,4 @@
1
+
2
+ module.exports = (ctx) => {
3
+ ctx.body = ctx.accountMgr.allowlist;
4
+ };
@@ -0,0 +1,4 @@
1
+
2
+ module.exports = (ctx) => {
3
+ ctx.body = ctx.accountMgr.entryRules;
4
+ };
@@ -0,0 +1,17 @@
1
+
2
+ const { getClientId, isClientId } = require('../../util');
3
+
4
+ module.exports = (ctx) => {
5
+ const { followId } = ctx.request.query;
6
+ if (isClientId(followId)) {
7
+ ctx.envMgr.setFollower(followId, ctx);
8
+ ctx.type = 'html';
9
+ ctx.body = '<p style="text-align: center; padding: 20px 0;">设置成功,<a href="./">点击调整到选择环境页面</a></p>';
10
+ return;
11
+ }
12
+ ctx.body = {
13
+ ec: 0,
14
+ clientId: getClientId(ctx),
15
+ followerIp: ctx.envMgr.getFollower(ctx),
16
+ };
17
+ };
@@ -0,0 +1,12 @@
1
+
2
+ const { getClientId } = require('../../util');
3
+
4
+ module.exports = (ctx) => {
5
+ const curEnv = ctx.envMgr.getEnv(ctx) || null;
6
+ ctx.body = {
7
+ ec: 0,
8
+ curEnv,
9
+ clientIp: ctx.ip,
10
+ clientId: getClientId(ctx),
11
+ };
12
+ };
@@ -0,0 +1,43 @@
1
+ const Limiter = require('async-limiter');
2
+ const { gzip } = require('zlib');
3
+ const LRU = require('lru-cache');
4
+
5
+ const limiter = new Limiter({ concurrency: 10 });
6
+ const cache = new LRU({ max: 2 });
7
+
8
+ const compress = (body) => {
9
+ let promise = cache.get(body);
10
+ if (!promise) {
11
+ promise = new Promise((resolve, reject) => {
12
+ limiter.push((done) => {
13
+ gzip(body, (err, buf) => {
14
+ done();
15
+ if (err) {
16
+ delete cache[body];
17
+ reject(err);
18
+ } else {
19
+ resolve(buf);
20
+ }
21
+ });
22
+ });
23
+ });
24
+ cache.set(body, promise);
25
+ }
26
+ return promise;
27
+ };
28
+
29
+ module.exports = async (ctx) => {
30
+ const curEnv = ctx.envMgr.getEnv(ctx);
31
+ const list = ctx.accountMgr.getAccountList(ctx.request.query.parsed);
32
+ let body = JSON.stringify({
33
+ admin: ctx.admin,
34
+ ec: 0,
35
+ baseUrl: ctx.baseUrl,
36
+ curEnv,
37
+ list,
38
+ });
39
+ body = await compress(body);
40
+ ctx.set('content-encoding', 'gzip');
41
+ ctx.set('content-type', 'application/json');
42
+ ctx.body = body;
43
+ };
@@ -0,0 +1,14 @@
1
+
2
+ module.exports = (ctx) => {
3
+ const {
4
+ patterns,
5
+ preRules,
6
+ postRules,
7
+ } = ctx.accountMgr;
8
+ ctx.body = {
9
+ ec: 0,
10
+ patterns,
11
+ preRules,
12
+ postRules,
13
+ };
14
+ };
@@ -0,0 +1,61 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const resolveValue = (text) => {
5
+ return text.substring(text.indexOf('```'));
6
+ };
7
+ const INJECT_HTML = resolveValue(fs.readFileSync(path.join(__dirname, '../../../_rules.txt'), { encoding: 'utf8' })); // eslint-disable-line
8
+ const INJECT_RULE = 'htmlPrepend://{whistle.nohost/inject.html}';
9
+ const WHISTLE_HOST = 'local.whistlejs.com';
10
+ let curBaseUrl;
11
+ let curPatterns;
12
+ let curRules;
13
+
14
+ module.exports = (ctx) => {
15
+ const {
16
+ patterns,
17
+ getBaseUrl,
18
+ getDomainList,
19
+ preRules,
20
+ postRules,
21
+ } = ctx.accountMgr;
22
+ const curRuntimeId = ctx.get('x-whistle-runtime-id');
23
+ const isSelf = curRuntimeId === ctx.runtimeId;
24
+ const baseUrl = !isSelf && getBaseUrl();
25
+ const domainList = getDomainList().concat([WHISTLE_HOST]);
26
+ if (baseUrl) {
27
+ if (curBaseUrl !== baseUrl || curPatterns !== patterns) {
28
+ curBaseUrl = baseUrl;
29
+ curPatterns = patterns;
30
+ let ignorePatterns = [];
31
+ const injectRule = [`internal-proxy://${baseUrl} ${INJECT_RULE} enable://clientId|multiClient|safeHtml`];
32
+ const normalRule = [`internal-proxy://${baseUrl} enable://clientId|multiClient`];
33
+ curRules = [];
34
+ patterns.forEach((item) => {
35
+ if (item.ignore) {
36
+ ignorePatterns.push(`excludeFilter://${item.pattern}`);
37
+ } else if (item.button) {
38
+ injectRule.push(item.pattern);
39
+ } else {
40
+ normalRule.push(item.pattern);
41
+ }
42
+ });
43
+ ignorePatterns = ignorePatterns.join(' ');
44
+ if (normalRule.length > 1) {
45
+ normalRule.push(ignorePatterns);
46
+ curRules.push(normalRule.join(' '));
47
+ }
48
+ if (injectRule.length > 1) {
49
+ injectRule.push(ignorePatterns);
50
+ curRules.push(injectRule.join(' '));
51
+ }
52
+ curRules.unshift(`internal-proxy://${baseUrl} ignore://rule|html enable://hide|proxyHost|clientId|multiClient ${domainList.join(' ')}`);
53
+ curRules.push(`*/.whistle-path.5b6af7b9884e1165/ ignore://-proxy|-host reqHeaders://whistleInternalHost=${WHISTLE_HOST} enable://proxyTunnel`);
54
+ curRules.push('\n', INJECT_HTML);
55
+ curRules = preRules.concat(curRules).concat(postRules).join('\n');
56
+ }
57
+ ctx.body = curRules;
58
+ } else {
59
+ ctx.body = '';
60
+ }
61
+ };
@@ -0,0 +1,42 @@
1
+ const LRU = require('lru-cache');
2
+ const { transformWhistle, changeFilter } = require('../../util');
3
+ const whistleMgr = require('../../whistleMgr');
4
+
5
+ const tempCache = new LRU({ maxAge: 5000 });
6
+
7
+ module.exports = async (ctx, next) => {
8
+ let { params: { name }, account } = ctx;
9
+ if (!account) {
10
+ account = ctx.accountMgr.getAccount(name);
11
+ if (!account) {
12
+ await next();
13
+ return;
14
+ }
15
+ } else {
16
+ name = account.name;
17
+ }
18
+ const { req } = ctx;
19
+ req.url = req.url.substring(`/account/${name}`.length);
20
+ changeFilter(req, ctx.envMgr.getFollower(ctx));
21
+ req.headers.host = 'local.wproxy.org';
22
+ req.headers['x-forwarded-for'] = ctx.ip || '127.0.0.1';
23
+ const {
24
+ port,
25
+ remoteAddrHead,
26
+ remotePortHead,
27
+ } = await whistleMgr.fork(account);
28
+ const remoteAddr = ctx.get('x-whistle-remote-address');
29
+ if (remoteAddr) {
30
+ req.headers[remoteAddrHead] = remoteAddr;
31
+ req.headers[remotePortHead] = ctx.get('x-whistle-remote-port');
32
+ }
33
+ try {
34
+ await transformWhistle(ctx, port);
35
+ } catch (err) {
36
+ if (err.code === 'ECONNREFUSED' && !tempCache.get(name)) {
37
+ tempCache.set(name, 1);
38
+ whistleMgr.kill(name);
39
+ }
40
+ throw err;
41
+ }
42
+ };
@@ -0,0 +1,12 @@
1
+ const { transformWhistle, changeFilter } = require('../../util');
2
+ const initNetwork = require('../network');
3
+
4
+ module.exports = async (ctx) => {
5
+ const { req } = ctx;
6
+ req.url = req.url.replace('/network', '');
7
+ req.headers.host = 'local.wproxy.org';
8
+ req.headers['x-forwarded-for'] = ctx.ip || '127.0.0.1';
9
+ const port = await initNetwork();
10
+ changeFilter(req);
11
+ await transformWhistle(ctx, port);
12
+ };