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,149 @@
1
+ const startWhistle = require('whistle');
2
+ const path = require('path');
3
+ const HOME_DIR = require('os').homedir();
4
+ const { CONFIG_DATA_TYPE, getPort, getNohostPluginsPath, loadModule, PLUGINS_DIR } = require('./util');
5
+
6
+ const WHISTLE_SECURE_FILTER = path.resolve(HOME_DIR, 'nohost/whistleSecureFilter.js');
7
+ const IDLE_TIMEOUT = 6 * 60 * 1000;
8
+
9
+ let index = 0;
10
+ let updatedIndex;
11
+ let curEnvList = [];
12
+ let config;
13
+ let proxy;
14
+ let isHeadless;
15
+
16
+ const getShadowRules = (options) => {
17
+ const shadowRules = isHeadless ? '* responseFor://name=x-upstream' : '* responseFor://name=x-upstream,req.x-whistle-nohost-env';
18
+ return options.defaultRules ? `${options.defaultRules}\n${shadowRules}` : shadowRules;
19
+ };
20
+
21
+ const syncData = () => {
22
+ let updateTimer;
23
+ let checkTimer;
24
+ process.on('data', (data) => {
25
+ const type = data && data.type;
26
+ if (type !== CONFIG_DATA_TYPE) {
27
+ return;
28
+ }
29
+ if (data.index >= 0) {
30
+ updatedIndex = data.index;
31
+ if (updatedIndex === index) {
32
+ clearTimeout(checkTimer);
33
+ checkTimer = null;
34
+ }
35
+ }
36
+ const shadowRules = getShadowRules(data);
37
+ if (config && shadowRules !== config.shadowRules) {
38
+ config.shadowRules = shadowRules;
39
+ proxy.rulesUtil.parseRules();
40
+ }
41
+ proxy.setAuth(data);
42
+ });
43
+ const updateRules = () => {
44
+ process.sendData({
45
+ index,
46
+ envList: proxy.rulesUtil.rules.list(),
47
+ type: CONFIG_DATA_TYPE,
48
+ runtimeInfo: proxy.getRuntimeInfo(),
49
+ });
50
+ updateTimer = null;
51
+ clearTimeout(checkTimer);
52
+ checkTimer = setTimeout(() => {
53
+ checkTimer = null;
54
+ if (updatedIndex !== index) {
55
+ updateRules();
56
+ }
57
+ }, 1000);
58
+ };
59
+ proxy.on('rulesDataChange', () => {
60
+ ++index;
61
+ updateTimer = updateTimer || setTimeout(updateRules, 300);
62
+ });
63
+ };
64
+
65
+ module.exports = (options, callback) => {
66
+ const projectPluginsPath = [
67
+ path.join(__dirname, '../../account'),
68
+ path.join(__dirname, '../../../../node_modules'),
69
+ ];
70
+ const username = options.value || '$';
71
+ const isRulesMode = username === '$';
72
+ const oldPluginsPath = [
73
+ path.join(PLUGINS_DIR, `whistle.nohost/${username}/lib/node_modules`),
74
+ path.join(PLUGINS_DIR, `whistle.nohost/${username}/node_modules`),
75
+ path.join(PLUGINS_DIR, 'whistle.nohost/lib/node_modules'),
76
+ path.join(PLUGINS_DIR, 'whistle.nohost/node_modules'),
77
+ ];
78
+ const accountPluginsPath = [
79
+ path.join(getNohostPluginsPath(), `account_plugins/${username}`),
80
+ ];
81
+ const customPluginsPath = [
82
+ path.join(getNohostPluginsPath(), 'worker_plugins'),
83
+ ];
84
+
85
+ isHeadless = /^\$\d+/.test(username);
86
+ let mode = 'multiEnv|disableUpdateTips|keepXFF|x-forwarded-proto';
87
+ if (options.worker) {
88
+ mode = `${mode}|plugins|hideLeftMenu`;
89
+ }
90
+ if (process.env.PFORK_MODE === 'bind') {
91
+ projectPluginsPath.unshift(process.cwd());
92
+ }
93
+ getPort((port) => {
94
+ proxy = startWhistle({
95
+ port,
96
+ host: '127.0.0.1',
97
+ cmdName: 'n2',
98
+ authKey: options.authKey,
99
+ encrypted: true,
100
+ storage: options.storage,
101
+ baseDir: process.env.NOHOST_BADE_DIR,
102
+ username,
103
+ password: isRulesMode ? `${Math.random()}` : options.password,
104
+ guestName: isRulesMode ? '' : options.guestName,
105
+ realPort: options.realPort,
106
+ realHost: options.realHost,
107
+ guestPassword: options.guestPassword,
108
+ shadowRules: getShadowRules(options),
109
+ mode: `${mode}${isRulesMode ? '|rules' : ''}`,
110
+ secureFilter: WHISTLE_SECURE_FILTER,
111
+ projectPluginsPath,
112
+ account: username,
113
+ accountPluginsPath,
114
+ customPluginsPath,
115
+ pluginsPath: (options.pluginsPath || []).concat(oldPluginsPath),
116
+ dnsServer: options.dnsServer,
117
+ addon: options.accountPluginPath,
118
+ pluginsDataMap: {
119
+ storage: {
120
+ storageServer: options.storageServer,
121
+ },
122
+ },
123
+ }, () => {
124
+ curEnvList = proxy.rulesUtil.rules.list();
125
+ const {
126
+ REMOTE_ADDR_HEAD: remoteAddrHead,
127
+ REMOTE_PORT_HEAD: remotePortHead,
128
+ } = proxy.config;
129
+ setTimeout(() => callback(null, {
130
+ port,
131
+ remoteAddrHead,
132
+ remotePortHead,
133
+ envList: curEnvList,
134
+ }), 100);
135
+ });
136
+ let timer;
137
+ const exitWhistleIfIdleTimeout = () => {
138
+ clearTimeout(timer);
139
+ timer = setTimeout(() => process.exit(), IDLE_TIMEOUT);
140
+ };
141
+ exitWhistleIfIdleTimeout();
142
+ config = loadModule('whistle/lib/config');
143
+ config.baseDirHash = '';
144
+ proxy.on('tunnelRequest', exitWhistleIfIdleTimeout);
145
+ proxy.on('wsRequest', exitWhistleIfIdleTimeout);
146
+ proxy.on('_request', exitWhistleIfIdleTimeout);
147
+ syncData();
148
+ });
149
+ };
@@ -0,0 +1,94 @@
1
+ const p = require('pfork');
2
+ const path = require('path');
3
+ const accountMgr = require('./accountMgr');
4
+ const {
5
+ CONFIG_DATA_TYPE,
6
+ AUTH_KEY,
7
+ pluginConfig,
8
+ } = require('./util');
9
+
10
+ const { realPort, realHost } = pluginConfig;
11
+ const GUEST_AUTH = { guestName: '-' };
12
+ const WHISTLE_WORKER = path.join(__dirname, 'whistle.js');
13
+ const DELAY = 6000;
14
+ const UPDATE_INTERVAL = 5000;
15
+ const cache = {};
16
+
17
+ exports.fork = (account) => {
18
+ if (!account) {
19
+ return;
20
+ }
21
+ const { name, password } = account;
22
+ if (cache[name]) {
23
+ return cache[name];
24
+ }
25
+ const getAccountData = () => {
26
+ const data = {
27
+ type: CONFIG_DATA_TYPE,
28
+ username: account.name,
29
+ password: account.password,
30
+ guest: accountMgr.isEnableGuest() ? GUEST_AUTH : null,
31
+ defaultRules: accountMgr.accountRules,
32
+ };
33
+ return data;
34
+ };
35
+ cache[name] = new Promise((resolve, reject) => {
36
+ const guestName = accountMgr.isEnableGuest() ? '-' : undefined;
37
+ p.fork({
38
+ authKey: AUTH_KEY,
39
+ password,
40
+ guestName,
41
+ defaultRules: accountMgr.accountRules,
42
+ storage: `whistle.nohost/${name}`,
43
+ script: WHISTLE_WORKER,
44
+ value: name,
45
+ realPort,
46
+ realHost,
47
+ storageServer: accountMgr.storageServer,
48
+ dnsServer: accountMgr.dnsServer,
49
+ accountPluginPath: accountMgr.accountPluginPath,
50
+ }, (err, result, child) => {
51
+ if (err) {
52
+ delete cache[name];
53
+ return reject(err);
54
+ }
55
+ resolve(result);
56
+ accountMgr.addEnvList(name, result.envList);
57
+ let timer;
58
+ const updateAuth = () => {
59
+ account = account && accountMgr.getAccount(account.name);
60
+ if (!account) {
61
+ return;
62
+ }
63
+ child.sendData(getAccountData());
64
+ timer = setTimeout(updateAuth, UPDATE_INTERVAL);
65
+ };
66
+ timer = setTimeout(updateAuth, UPDATE_INTERVAL);
67
+ child.on('exit', () => {
68
+ clearTimeout(timer);
69
+ delete cache[name];
70
+ });
71
+ child.on('data', (data) => {
72
+ const type = data && data.type;
73
+ if (type !== CONFIG_DATA_TYPE) {
74
+ return;
75
+ }
76
+ account = account && accountMgr.getAccount(account.name);
77
+ if (!account) {
78
+ return;
79
+ }
80
+ const accountData = getAccountData();
81
+ accountData.index = data.index;
82
+ child.sendData(accountData);
83
+ accountMgr.addEnvList(name, data.envList);
84
+ });
85
+ });
86
+ });
87
+ return cache[name];
88
+ };
89
+ exports.kill = (name) => {
90
+ p.kill({
91
+ script: WHISTLE_WORKER,
92
+ value: name,
93
+ }, DELAY);
94
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "whistle.nohost",
3
+ "version": "1.0.0",
4
+ "whistleConfig": {
5
+ "priority": 100
6
+ }
7
+ }
@@ -0,0 +1,3 @@
1
+ * whistle.nohost://none includeFilter://reqH:x-whistle-nohost-env=/^\$\d+$/
2
+ * whistle.nohost://`${reqH.x-whistle-nohost-policy}` includeFilter://reqH:x-whistle-nohost-policy
3
+ @whistle.nohost/cgi-bin/rules
@@ -0,0 +1,76 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const fse = require('fs-extra');
4
+ const Limiter = require('async-limiter');
5
+ const { gzip } = require('zlib');
6
+ const { getSessionsDir, getDate, getAccessCode } = require('./util');
7
+
8
+ const promises = {};
9
+ const limiter = new Limiter({ concurrency: 10 });
10
+ const ROUTE_RE = /^[\w.:/=+-]{1,100}$/;
11
+
12
+ const writeFile = (filepath, data) => {
13
+ return new Promise((resolve, reject) => {
14
+ fs.writeFile(filepath, data, {
15
+ flag: 'wx',
16
+ }, (err) => {
17
+ if (err) {
18
+ reject(err);
19
+ } else {
20
+ resolve();
21
+ }
22
+ });
23
+ });
24
+ };
25
+
26
+ const createDir = (dir) => {
27
+ let p = promises[dir];
28
+ if (!p) {
29
+ p = fse.ensureDir(dir);
30
+ promises[dir] = p;
31
+ }
32
+ return p;
33
+ };
34
+
35
+ const compressSessions = (sessions) => {
36
+ return new Promise((resolve, reject) => {
37
+ limiter.push((done) => {
38
+ gzip(sessions, (err, buf) => {
39
+ done();
40
+ if (err) {
41
+ reject(err);
42
+ } else {
43
+ resolve(buf);
44
+ }
45
+ });
46
+ });
47
+ });
48
+ };
49
+
50
+ module.exports = async (ctx) => {
51
+ let { query: { username, route }, body: { name, sessions, code } } = ctx.request;
52
+ if (!name || !sessions || typeof sessions !== 'string') {
53
+ return;
54
+ }
55
+ code = getAccessCode(code);
56
+ const date = getDate();
57
+ const dir = getSessionsDir(username, date, code);
58
+ if (!dir) {
59
+ return;
60
+ }
61
+ await createDir(dir);
62
+ try {
63
+ sessions = await compressSessions(sessions);
64
+ name = encodeURIComponent(name);
65
+ if (code) {
66
+ name = `${name}[${code}]`;
67
+ }
68
+ await writeFile(path.join(dir, `${name}[gz]`), sessions);
69
+ ctx.body = { ec: 0, date, username, route: route && ROUTE_RE.test(route) ? route : undefined };
70
+ } catch (e) {
71
+ ctx.body = { ec: e.code === 'EEXIST' ? 1 : 2 };
72
+ }
73
+ if (ctx.get('origin')) {
74
+ ctx.set('Access-Control-Allow-Origin', '*');
75
+ }
76
+ };
@@ -0,0 +1,33 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { getSessionsDir, getAccessCode } = require('./util');
4
+
5
+ const isGzipFile = (gzFilePath) => {
6
+ return new Promise((resolve) => {
7
+ fs.stat(gzFilePath, (err, stat) => {
8
+ resolve(!err && stat.isFile());
9
+ });
10
+ });
11
+ };
12
+
13
+ module.exports = async (ctx) => {
14
+ let { username, name, date, code } = ctx.request.query;
15
+ if (!name || typeof name !== 'string') {
16
+ return;
17
+ }
18
+ code = getAccessCode(code);
19
+ const dir = getSessionsDir(username, date, code);
20
+ if (!dir) {
21
+ return;
22
+ }
23
+ name = encodeURIComponent(name);
24
+ if (code) {
25
+ name = `${name}[${code}]`;
26
+ }
27
+ const gzFilePath = path.join(dir, `${name}[gz]`);
28
+ const isGFile = await isGzipFile(gzFilePath);
29
+ if (isGFile) {
30
+ ctx.set('content-encoding', 'gzip');
31
+ }
32
+ ctx.body = fs.createReadStream(isGFile ? gzFilePath : path.join(dir, name));
33
+ };
@@ -0,0 +1,35 @@
1
+ const os = require('os');
2
+ const path = require('path');
3
+
4
+ const USERNAME_RE = /^[a-z\d.]{1,64}$/;
5
+ const DATE_RE = /^\d{8}$/;
6
+ const ACCESS_CODE_RE = /^[a-z\d]{4}$/i;
7
+ const leftPad = num => (num > 9 ? num : `0${num}`);
8
+ let nohostPath;
9
+
10
+ const getSessionsPath = () => {
11
+ nohostPath = nohostPath || process.env.NOHOST_PATH || path.join(os.homedir(), '.NohostAppData');
12
+ return nohostPath;
13
+ };
14
+
15
+ const getDate = () => {
16
+ const now = new Date();
17
+ return `${now.getFullYear()}${leftPad(now.getMonth() + 1)}${leftPad(now.getDate())}`;
18
+ };
19
+
20
+ exports.getDate = getDate;
21
+
22
+ const checkUsername = (username) => {
23
+ return username == null || username === '' || (typeof username === 'string' && USERNAME_RE.test(username));
24
+ };
25
+
26
+ exports.getSessionsDir = (username, date, encrypted) => {
27
+ if (!DATE_RE.test(date) || !checkUsername(username)) {
28
+ return;
29
+ }
30
+ const dir = getSessionsPath();
31
+ date = path.join(date, encrypted ? 'encrypted' : 'sessions');
32
+ return username ? path.join(dir, username, date) : path.join(dir, date);
33
+ };
34
+
35
+ exports.getAccessCode = code => (ACCESS_CODE_RE.test(code) ? code.toLowerCase() : '');
@@ -0,0 +1,37 @@
1
+ const { fork } = require('pfork');
2
+ const script = require('path').join(__dirname, 'server.js');
3
+ const getPort = require('../util/getPort');
4
+
5
+ const workers = [];
6
+ const getCreator = () => {
7
+ let promise;
8
+ return () => {
9
+ if (!promise) {
10
+ promise = new Promise((resolve, reject) => {
11
+ getPort((port) => {
12
+ fork({ value: `${port}`, script }, (err, _, child) => {
13
+ if (err) {
14
+ promise = null;
15
+ reject(err);
16
+ } else {
17
+ child.once('close', () => {
18
+ promise = null;
19
+ });
20
+ resolve(port);
21
+ }
22
+ });
23
+ });
24
+ });
25
+ }
26
+ return promise;
27
+ };
28
+ };
29
+
30
+ module.exports = (index) => {
31
+ let createServer = workers[index];
32
+ if (!createServer) {
33
+ createServer = getCreator();
34
+ workers[index] = getCreator();
35
+ }
36
+ return createServer();
37
+ };
@@ -0,0 +1,7 @@
1
+ const exportSessions = require('./cgi/export');
2
+ const importSessions = require('./cgi/import');
3
+
4
+ module.exports = (router) => {
5
+ router.post('/cgi-bin/sessions/export', exportSessions);
6
+ router.get('/cgi-bin/sessions/import', importSessions);
7
+ };
@@ -0,0 +1,25 @@
1
+
2
+ const { createServer } = require('http');
3
+ const Koa = require('koa');
4
+ const onerror = require('koa-onerror');
5
+ const router = require('koa-router')();
6
+ const bodyParser = require('koa-bodyparser');
7
+ const setupRouter = require('./router');
8
+
9
+
10
+ module.exports = ({ value: port }, callback) => {
11
+ const server = createServer();
12
+ const app = new Koa();
13
+ app.proxy = true;
14
+ app.silent = true;
15
+ if (process.env.PFORK_MODE === 'bind') {
16
+ onerror(app);
17
+ }
18
+ setupRouter(router);
19
+ app.use(bodyParser({ formLimit: '8mb' }));
20
+ app.use(router.routes());
21
+ app.use(router.allowedMethods());
22
+ server.on('request', app.callback());
23
+ server.on('error', callback);
24
+ server.listen(port, '127.0.0.1', callback);
25
+ };
@@ -0,0 +1,55 @@
1
+ const os = require('os');
2
+ const net = require('net');
3
+
4
+ const LOCALHOST = '127.0.0.1';
5
+ let addressList = [];
6
+ let serverIp;
7
+
8
+ (function updateSystyemInfo() {
9
+ const interfaces = os.networkInterfaces();
10
+ addressList = [];
11
+ Object.keys(interfaces).forEach((name) => {
12
+ const list = interfaces[name];
13
+ if (Array.isArray(list)) {
14
+ list.forEach((info) => {
15
+ if (!info.internal && (info.family === 'IPv4' || info.family === 4)) {
16
+ serverIp = info.address;
17
+ }
18
+ addressList.push(info.address.toLowerCase());
19
+ });
20
+ // 支持多网卡时,以环境变量指定 serverIp
21
+ const envServerIp = process.env.NOHOST_SERVER_IP;
22
+ if (net.isIP(envServerIp) && addressList.includes(envServerIp)) {
23
+ serverIp = envServerIp;
24
+ }
25
+ }
26
+ });
27
+ setTimeout(updateSystyemInfo, 30000);
28
+ }());
29
+
30
+ exports.getAddressList = () => addressList;
31
+
32
+ const isLocalHost = (ip) => {
33
+ if (!ip || typeof ip !== 'string') {
34
+ return true;
35
+ }
36
+ return ip.length < 7 || ip === LOCALHOST;
37
+ };
38
+
39
+ const isLocalAddress = (address) => {
40
+ if (isLocalHost(address)) {
41
+ return true;
42
+ }
43
+ if (address === '0:0:0:0:0:0:0:1') {
44
+ return true;
45
+ }
46
+ address = address.toLowerCase();
47
+ if (address[0] === '[') {
48
+ address = address.slice(1, -1);
49
+ }
50
+ return addressList.indexOf(address) !== -1;
51
+ };
52
+
53
+ exports.isLocalAddress = isLocalAddress;
54
+
55
+ exports.getServerIp = () => serverIp;
@@ -0,0 +1,19 @@
1
+ const http = require('http');
2
+
3
+ let curPort = 30013;
4
+
5
+ const getPort = (callback) => {
6
+ const server = http.createServer();
7
+ server.on('error', () => {
8
+ if (++curPort % 5 === 0) {
9
+ ++curPort;
10
+ }
11
+ getPort(callback);
12
+ });
13
+ server.listen(curPort, '127.0.0.1', () => {
14
+ server.removeAllListeners();
15
+ server.close(() => callback(curPort));
16
+ });
17
+ };
18
+
19
+ module.exports = getPort;
@@ -0,0 +1,55 @@
1
+ const crypto = require('crypto');
2
+ const getAuth = require('basic-auth');
3
+
4
+ const ENV_MAX_AGE = 60 * 60 * 24 * 3;
5
+
6
+ const shasum = (str) => {
7
+ if (typeof str !== 'string') {
8
+ str = '';
9
+ }
10
+ const result = crypto.createHash('sha1');
11
+ result.update(str);
12
+ return result.digest('hex');
13
+ };
14
+ exports.shasum = shasum;
15
+
16
+ const getLoginKey = (ctx, username, password) => {
17
+ const ip = ctx.ip || '127.0.0.1';
18
+ return shasum(`${username || ''}\n${password || ''}\n${ip}`);
19
+ };
20
+
21
+ exports.checkLogin = (ctx, authConf) => {
22
+ const {
23
+ username,
24
+ password,
25
+ nameKey,
26
+ authKey,
27
+ } = authConf;
28
+
29
+ if (!username || !password) {
30
+ return true;
31
+ }
32
+ const curName = ctx.cookies.get(nameKey);
33
+ const lkey = ctx.cookies.get(authKey);
34
+ const correctKey = getLoginKey(ctx, username, password);
35
+ if (curName === username && correctKey === lkey) {
36
+ return true;
37
+ }
38
+
39
+ const { name, pass } = getAuth(ctx.req) || {};
40
+ if (name === username && shasum(pass) === password) {
41
+ const options = {
42
+ expires: new Date(Date.now() + (ENV_MAX_AGE * 1000)),
43
+ path: '/',
44
+ };
45
+ ctx.cookies.set(nameKey, username, options);
46
+ ctx.cookies.set(authKey, correctKey, options);
47
+ return true;
48
+ }
49
+
50
+ ctx.status = 401;
51
+ ctx.set('WWW-Authenticate', ' Basic realm=User Login');
52
+ ctx.set('Content-Type', 'text/html; charset=utf8');
53
+ ctx.body = 'Access denied, please <a href="javascript:;" onclick="location.reload()">try again</a>.';
54
+ return false;
55
+ };
@@ -0,0 +1,17 @@
1
+
2
+ const isBaseUrl = d => /^[\w-]+(?:\.[\w-]+){1,}$/.test(d);
3
+ const SEP_RE = /\s*[,\s]\s*/;
4
+ let domainList = [];
5
+ let curDomain;
6
+
7
+ const parseDomain = (domainStr) => {
8
+ if (domainStr !== curDomain) {
9
+ curDomain = domainStr;
10
+ domainList = curDomain.trim().split(SEP_RE).filter(isBaseUrl);
11
+ }
12
+ return domainList;
13
+ };
14
+
15
+ module.exports = (str) => {
16
+ return !str || typeof str !== 'string' ? [] : parseDomain(str);
17
+ };