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.
- package/LICENSE +124 -0
- package/README.md +191 -0
- package/bin/nohost.js +178 -0
- package/bin/plugin.js +70 -0
- package/bin/util.js +93 -0
- package/index.js +89 -0
- package/lib/config.js +42 -0
- package/lib/index.js +205 -0
- package/lib/main/cgi/getSettings.js +11 -0
- package/lib/main/cgi/getVersion.js +12 -0
- package/lib/main/cgi/login.js +14 -0
- package/lib/main/cgi/restart.js +5 -0
- package/lib/main/cgi/setAdmin.js +6 -0
- package/lib/main/cgi/setDomain.js +13 -0
- package/lib/main/cgi/status.js +122 -0
- package/lib/main/index.js +94 -0
- package/lib/main/router.js +79 -0
- package/lib/main/storage.js +132 -0
- package/lib/main/util.js +138 -0
- package/lib/main/whistleMgr.js +56 -0
- package/lib/main/worker.js +52 -0
- package/lib/main/workerNum.js +11 -0
- package/lib/plugins/account/whistle.share/menu.html +171 -0
- package/lib/plugins/account/whistle.share/package.json +20 -0
- package/lib/plugins/account/whistle.storage/index.js +43 -0
- package/lib/plugins/account/whistle.storage/package.json +8 -0
- package/lib/plugins/account/whistle.storage/rules.txt +1 -0
- package/lib/plugins/whistle.nohost/_rules.txt +38 -0
- package/lib/plugins/whistle.nohost/index.js +3 -0
- package/lib/plugins/whistle.nohost/initial.js +9 -0
- package/lib/plugins/whistle.nohost/lib/accountMgr.js +750 -0
- package/lib/plugins/whistle.nohost/lib/envMgr.js +114 -0
- package/lib/plugins/whistle.nohost/lib/rulesServer.js +85 -0
- package/lib/plugins/whistle.nohost/lib/tunnelRulesServer.js +71 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/account/changePassword.js +8 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/activeAccount.js +9 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/addAccount.js +8 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/allAccounts.js +9 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/changeNotice.js +8 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/changePassword.js +8 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/enableGuest.js +5 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/getAuthKey.js +3 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/getSettings.js +22 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/login.js +14 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/move.js +9 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/removeAccount.js +9 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setAccountRules.js +5 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setAuthKey.js +5 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setDefaultRules.js +5 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setEntryPatterns.js +6 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setJsonData.js +5 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setRulesTpl.js +5 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/setTestRules.js +5 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/admin/specPattern.js +4 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/allowlist.js +4 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/entryRules.js +4 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/follow.js +17 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/getEnv.js +12 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/list.js +43 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/patterns.js +14 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/pluginRules.js +61 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/proxy.js +42 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/proxyNetwork.js +12 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/redirect.js +10 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/selectEnv.js +30 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/cgi/unfollow.js +4 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/index.js +60 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/network.js +38 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/openAPI.js +94 -0
- package/lib/plugins/whistle.nohost/lib/uiServer/router.js +85 -0
- package/lib/plugins/whistle.nohost/lib/util.js +230 -0
- package/lib/plugins/whistle.nohost/lib/whistle.js +149 -0
- package/lib/plugins/whistle.nohost/lib/whistleMgr.js +94 -0
- package/lib/plugins/whistle.nohost/package.json +7 -0
- package/lib/plugins/whistle.nohost/rules.txt +3 -0
- package/lib/service/cgi/export.js +76 -0
- package/lib/service/cgi/import.js +33 -0
- package/lib/service/cgi/util.js +35 -0
- package/lib/service/index.js +37 -0
- package/lib/service/router.js +7 -0
- package/lib/service/server.js +25 -0
- package/lib/util/address.js +55 -0
- package/lib/util/getPort.js +19 -0
- package/lib/util/login.js +55 -0
- package/lib/util/parseDomain.js +17 -0
- package/lib/whistle.js +86 -0
- package/package.json +135 -0
- package/pnpm-workspace.yaml +6 -0
- package/public/admin.75d42731d4aa7f7a558d3abf2a375fcb.js +2 -0
- package/public/admin.75d42731d4aa7f7a558d3abf2a375fcb.js.LICENSE.txt +76 -0
- package/public/admin.html +11 -0
- package/public/button.js +2 -0
- package/public/button.js.LICENSE.txt +31 -0
- package/public/capture.7bab900f27c9bb1b0e33523e994554ae.js +2 -0
- package/public/capture.7bab900f27c9bb1b0e33523e994554ae.js.LICENSE.txt +83 -0
- package/public/capture.html +11 -0
- package/public/eed368d0656f03932671530c3132ed61.svg +1 -0
- package/public/favicon.ico +0 -0
- package/public/network.e4814ec8c966a8a789296679619ec573.js +2 -0
- package/public/network.e4814ec8c966a8a789296679619ec573.js.LICENSE.txt +76 -0
- package/public/network.html +11 -0
- package/public/select.cb638be6656d02a558f2690acd59901a.js +2 -0
- package/public/select.cb638be6656d02a558f2690acd59901a.js.LICENSE.txt +24 -0
- package/public/select.html +26 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const { getClientId } = require('../../util');
|
|
2
|
+
|
|
3
|
+
module.exports = (ctx) => {
|
|
4
|
+
const { url, name, env } = ctx.request.query;
|
|
5
|
+
const account = ctx.accountMgr.getAccount(name);
|
|
6
|
+
if (account) {
|
|
7
|
+
ctx.envMgr.setEnv(getClientId(ctx), name, env);
|
|
8
|
+
}
|
|
9
|
+
ctx.redirect(url || './');
|
|
10
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { COOKIE_NAME, ENV_MAX_AGE, decodeURIComponentSafe, getClientId } = require('../../util');
|
|
2
|
+
|
|
3
|
+
module.exports = (ctx) => {
|
|
4
|
+
const { name, envId, redirect } = ctx.request.query;
|
|
5
|
+
let envName;
|
|
6
|
+
// 如果 envId 看起来像短ID(不包含编码的环境名特征),则通过 envId 查找 envName
|
|
7
|
+
if (envId && !envId.includes('%')) {
|
|
8
|
+
envName = ctx.accountMgr.getEnvNameById(name, envId);
|
|
9
|
+
// 如果没找到,回退到直接解码(兼容旧逻辑)
|
|
10
|
+
if (!envName) {
|
|
11
|
+
envName = decodeURIComponentSafe(envId);
|
|
12
|
+
}
|
|
13
|
+
} else {
|
|
14
|
+
envName = decodeURIComponentSafe(envId);
|
|
15
|
+
}
|
|
16
|
+
const env = ctx.envMgr.setEnv(getClientId(ctx), name, envName);
|
|
17
|
+
let value = '';
|
|
18
|
+
if (env) {
|
|
19
|
+
value = encodeURIComponent(`${name}/${env.envName}`);
|
|
20
|
+
}
|
|
21
|
+
ctx.cookies.set(COOKIE_NAME, value, {
|
|
22
|
+
path: '/',
|
|
23
|
+
expires: new Date(Date.now() + (ENV_MAX_AGE * 1000)),
|
|
24
|
+
});
|
|
25
|
+
if (redirect && typeof redirect === 'string') {
|
|
26
|
+
ctx.redirect(redirect);
|
|
27
|
+
} else {
|
|
28
|
+
ctx.body = { ec: 0 };
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const Koa = require('koa');
|
|
2
|
+
const onerror = require('koa-onerror');
|
|
3
|
+
const serve = require('koa-static');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const router = require('koa-router')();
|
|
6
|
+
const setupRouter = require('./router');
|
|
7
|
+
const accountMgr = require('../accountMgr');
|
|
8
|
+
const envMgr = require('../envMgr');
|
|
9
|
+
const whistleMgr = require('../whistleMgr');
|
|
10
|
+
|
|
11
|
+
const MAX_AGE = 1000 * 60 * 5;
|
|
12
|
+
const error = function (em, ec) {
|
|
13
|
+
em = em || '请求失败,请刷新页面重试';
|
|
14
|
+
ec = ec || 2;
|
|
15
|
+
this.body = { ec, em };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = (server, options) => {
|
|
19
|
+
const { username, password } = options.data;
|
|
20
|
+
const { runtimeId } = options.config;
|
|
21
|
+
const app = new Koa();
|
|
22
|
+
app.proxy = true;
|
|
23
|
+
app.silent = true;
|
|
24
|
+
if (process.env.PFORK_MODE === 'bind') {
|
|
25
|
+
onerror(app);
|
|
26
|
+
}
|
|
27
|
+
app.use(async (ctx, next) => {
|
|
28
|
+
const baseUrl = `http://${accountMgr.getBaseUrl()}/`;
|
|
29
|
+
ctx.baseUrl = baseUrl;
|
|
30
|
+
ctx.success = function () {
|
|
31
|
+
this.body = { ec: 0, baseUrl };
|
|
32
|
+
};
|
|
33
|
+
ctx.error = error;
|
|
34
|
+
ctx.accountMgr = accountMgr;
|
|
35
|
+
ctx.envMgr = envMgr;
|
|
36
|
+
ctx.config = options.config;
|
|
37
|
+
ctx.updateRules = options.updateRules;
|
|
38
|
+
ctx.whistleMgr = whistleMgr;
|
|
39
|
+
ctx.admin = { username, password };
|
|
40
|
+
ctx.runtimeId = runtimeId;
|
|
41
|
+
ctx.set('x-nohost-base-url', baseUrl);
|
|
42
|
+
const { path: pathname } = ctx;
|
|
43
|
+
if (pathname === '/' || pathname === '/index.html') {
|
|
44
|
+
ctx.req.url = '/select.html';
|
|
45
|
+
} else if (pathname === '/js/nohost.js') {
|
|
46
|
+
ctx.req.url = '/button.js';
|
|
47
|
+
}
|
|
48
|
+
const origin = ctx.get('origin');
|
|
49
|
+
if (origin) {
|
|
50
|
+
ctx.set('Access-Control-Allow-Origin', origin);
|
|
51
|
+
ctx.set('Access-Control-Allow-Credentials', true);
|
|
52
|
+
}
|
|
53
|
+
await next();
|
|
54
|
+
});
|
|
55
|
+
setupRouter(router);
|
|
56
|
+
app.use(router.routes());
|
|
57
|
+
app.use(router.allowedMethods());
|
|
58
|
+
app.use(serve(path.join(__dirname, '../../../../../public/'), { maxage: MAX_AGE }));
|
|
59
|
+
server.on('request', app.callback());
|
|
60
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
const startWhistle = require('whistle');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { getNohostPluginsPath, getPort, PLUGINS_DIR } = require('../util');
|
|
5
|
+
|
|
6
|
+
let result;
|
|
7
|
+
const projectPluginsPath = [
|
|
8
|
+
path.join(__dirname, '../../../account'),
|
|
9
|
+
];
|
|
10
|
+
const customPluginsPath = [
|
|
11
|
+
path.join(getNohostPluginsPath(), 'worker_plugins'),
|
|
12
|
+
];
|
|
13
|
+
const pluginsPath = [
|
|
14
|
+
path.join(PLUGINS_DIR, 'whistle.nohost/lib/node_modules'),
|
|
15
|
+
path.join(PLUGINS_DIR, 'whistle.nohost/node_modules'),
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
module.exports = () => {
|
|
19
|
+
if (!result) {
|
|
20
|
+
result = new Promise((resolve) => {
|
|
21
|
+
getPort((port) => {
|
|
22
|
+
const proxy = startWhistle({
|
|
23
|
+
globalData: 'Network',
|
|
24
|
+
cmdName: 'n2',
|
|
25
|
+
port,
|
|
26
|
+
host: '127.0.0.1',
|
|
27
|
+
encrypted: true,
|
|
28
|
+
storage: 'nohost_network_mode',
|
|
29
|
+
mode: 'plugins|disableUpdateTips|hideLeftMenu',
|
|
30
|
+
projectPluginsPath,
|
|
31
|
+
customPluginsPath,
|
|
32
|
+
pluginsPath,
|
|
33
|
+
}, () => resolve(port, proxy));
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const proxy = require('./cgi/proxy');
|
|
2
|
+
const accountMgr = require('../accountMgr');
|
|
3
|
+
const { AUTH_KEY } = require('../util');
|
|
4
|
+
|
|
5
|
+
const CGI_MAP = {
|
|
6
|
+
'add-env': 'rules/add',
|
|
7
|
+
'add-top-env': 'rules/project',
|
|
8
|
+
'add-top-rules': 'rules/project',
|
|
9
|
+
'add-rules': 'rules/add',
|
|
10
|
+
'modify-env': 'rules/add',
|
|
11
|
+
'modify-rules': 'rules/add',
|
|
12
|
+
'remove-env': 'rules/remove',
|
|
13
|
+
'remove-rules': 'rules/remove',
|
|
14
|
+
'rename-env': 'rules/rename',
|
|
15
|
+
'rename-rules': 'rules/rename',
|
|
16
|
+
list: 'rules/list',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getRules = (options) => {
|
|
20
|
+
const { account, name, env, envName, envId } = options;
|
|
21
|
+
let rules = '';
|
|
22
|
+
let headers;
|
|
23
|
+
// 如果传入了 envId,优先使用 envId 查询
|
|
24
|
+
if (envId && (account || name)) {
|
|
25
|
+
const result = accountMgr.getRulesByEnvId(account || name, envId);
|
|
26
|
+
rules = result.rules || '';
|
|
27
|
+
headers = result.headers;
|
|
28
|
+
} else {
|
|
29
|
+
const result = accountMgr.getRules(account || name, env || envName);
|
|
30
|
+
rules = result.rules || '';
|
|
31
|
+
headers = result.headers;
|
|
32
|
+
}
|
|
33
|
+
if (headers) {
|
|
34
|
+
headers = JSON.stringify(headers);
|
|
35
|
+
if (headers.length > 2) {
|
|
36
|
+
rules += `\n* reqHeaders://(${headers})`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return rules;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
module.exports = async (ctx, next) => {
|
|
43
|
+
let { cgiName } = ctx.params;
|
|
44
|
+
const authKey = ctx.accountMgr.getAuthKey();
|
|
45
|
+
if (cgiName === 'rules') {
|
|
46
|
+
const { query } = ctx.request;
|
|
47
|
+
if (!authKey || authKey !== query.authKey) {
|
|
48
|
+
ctx.status = 403;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
ctx.body = getRules(query);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Handle rename-env specially to preserve env id
|
|
56
|
+
if (cgiName === 'rename-env') {
|
|
57
|
+
const name = ctx.get('x-nohost-account-name');
|
|
58
|
+
const account = ctx.accountMgr.getAccount(name);
|
|
59
|
+
if (!account) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const curAuthKey = authKey && ctx.get('x-nohost-auth-key');
|
|
63
|
+
if (!curAuthKey || (authKey !== curAuthKey && curAuthKey !== ctx.accountMgr.defaultAuthKey)) {
|
|
64
|
+
ctx.status = 403;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const { query } = ctx.request;
|
|
68
|
+
const oldName = query.name;
|
|
69
|
+
const newName = query.newValue || query.newName;
|
|
70
|
+
if (oldName && newName) {
|
|
71
|
+
ctx.accountMgr.renameEnv(name, oldName, newName);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
cgiName = CGI_MAP[cgiName];
|
|
76
|
+
const name = ctx.get('x-nohost-account-name');
|
|
77
|
+
const account = cgiName && ctx.accountMgr.getAccount(name);
|
|
78
|
+
if (!account) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const curAuthKey = authKey && ctx.get('x-nohost-auth-key');
|
|
82
|
+
if (!curAuthKey || (authKey !== curAuthKey && curAuthKey !== ctx.accountMgr.defaultAuthKey)) {
|
|
83
|
+
ctx.status = 403;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const { req } = ctx;
|
|
87
|
+
req.headers['x-whistle-auth-key'] = AUTH_KEY;
|
|
88
|
+
let { url } = req;
|
|
89
|
+
const index = url.indexOf('?');
|
|
90
|
+
url = index === -1 ? '' : url.substring(index);
|
|
91
|
+
req.url = `/account/${name}/cgi-bin/${cgiName}${url}`;
|
|
92
|
+
ctx.account = account;
|
|
93
|
+
await proxy(ctx, next);
|
|
94
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const bodyParser = require('koa-bodyparser');
|
|
2
|
+
const list = require('./cgi/list');
|
|
3
|
+
const proxyNetwork = require('./cgi/proxyNetwork');
|
|
4
|
+
const selectEnv = require('./cgi/selectEnv');
|
|
5
|
+
const checkAdminLogin = require('./cgi/admin/login');
|
|
6
|
+
const getAllAccounts = require('./cgi/admin/allAccounts');
|
|
7
|
+
const addAccount = require('./cgi/admin/addAccount');
|
|
8
|
+
const moveAccount = require('./cgi/admin/move');
|
|
9
|
+
const activeAccount = require('./cgi/admin/activeAccount');
|
|
10
|
+
const removeAccount = require('./cgi/admin/removeAccount');
|
|
11
|
+
const getSettings = require('./cgi/admin/getSettings');
|
|
12
|
+
const setJsonData = require('./cgi/admin/setJsonData');
|
|
13
|
+
const setRulesTpl = require('./cgi/admin/setRulesTpl');
|
|
14
|
+
const setEntryPatterns = require('./cgi/admin/setEntryPatterns');
|
|
15
|
+
const setTestRules = require('./cgi/admin/setTestRules');
|
|
16
|
+
const setDefaultRules = require('./cgi/admin/setDefaultRules');
|
|
17
|
+
const setAccountRules = require('./cgi/admin/setAccountRules');
|
|
18
|
+
const getAuthKey = require('./cgi/admin/getAuthKey');
|
|
19
|
+
const setAuthKey = require('./cgi/admin/setAuthKey');
|
|
20
|
+
const enableGuest = require('./cgi/admin/enableGuest');
|
|
21
|
+
const proxy = require('./cgi/proxy');
|
|
22
|
+
const getEntryRules = require('./cgi/entryRules');
|
|
23
|
+
const getPluginRules = require('./cgi/pluginRules');
|
|
24
|
+
const changePassword = require('./cgi/account/changePassword');
|
|
25
|
+
const patterns = require('./cgi/patterns');
|
|
26
|
+
const allowlist = require('./cgi/allowlist');
|
|
27
|
+
const openAPI = require('./openAPI');
|
|
28
|
+
const follow = require('./cgi/follow');
|
|
29
|
+
const unfollow = require('./cgi/unfollow');
|
|
30
|
+
const redirect = require('./cgi/redirect');
|
|
31
|
+
const getEnv = require('./cgi/getEnv');
|
|
32
|
+
const changeAdminPassword = require('./cgi/admin/changePassword');
|
|
33
|
+
const changeNotice = require('./cgi/admin/changeNotice');
|
|
34
|
+
const setSpecPattern = require('./cgi/admin/specPattern');
|
|
35
|
+
|
|
36
|
+
const defaultBodyParser = bodyParser({ formLimit: '1mb' });
|
|
37
|
+
const forwardCapture = async (ctx, next) => {
|
|
38
|
+
ctx.url = '/capture.html';
|
|
39
|
+
await next();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
module.exports = (router) => {
|
|
43
|
+
router.get('/admin.html', checkAdminLogin);
|
|
44
|
+
router.all('/cgi-bin/admin/(.*)', checkAdminLogin);
|
|
45
|
+
router.all('/cgi-bin/(.*)', defaultBodyParser);
|
|
46
|
+
router.get('/open-api/select', defaultBodyParser, selectEnv);
|
|
47
|
+
router.get('/open-api/cur-env', defaultBodyParser, getEnv);
|
|
48
|
+
router.get('/open-api/get-env', defaultBodyParser, getEnv);
|
|
49
|
+
router.all('/open-api/:cgiName', openAPI);
|
|
50
|
+
router.get('/cgi-bin/list', list);
|
|
51
|
+
router.get('/cgi-bin/cur-env', getEnv);
|
|
52
|
+
router.get('/cgi-bin/get-env', getEnv);
|
|
53
|
+
router.get('/cgi-bin/rules', getEntryRules);
|
|
54
|
+
router.get('/cgi-bin/entry-rules', getEntryRules);
|
|
55
|
+
router.get('/cgi-bin/remote-rules', getPluginRules);
|
|
56
|
+
router.get('/cgi-bin/plugin-rules', getPluginRules);
|
|
57
|
+
router.all('/network/(.*)', proxyNetwork);
|
|
58
|
+
router.get('/data.html', forwardCapture);
|
|
59
|
+
router.get('/cgi-bin/select', selectEnv);
|
|
60
|
+
router.get('/cgi-bin/admin/all-accounts', getAllAccounts);
|
|
61
|
+
router.post('/cgi-bin/admin/add-account', addAccount);
|
|
62
|
+
router.post('/cgi-bin/admin/move', moveAccount);
|
|
63
|
+
router.post('/cgi-bin/admin/active-account', activeAccount);
|
|
64
|
+
router.post('/cgi-bin/admin/remove-account', removeAccount);
|
|
65
|
+
router.post('/cgi-bin/admin/change-password', changeAdminPassword);
|
|
66
|
+
router.post('/cgi-bin/admin/change-notice', changeNotice);
|
|
67
|
+
router.post('/cgi-bin/admin/enable-guest', enableGuest);
|
|
68
|
+
router.post('/cgi-bin/admin/set-entry-patterns', setEntryPatterns);
|
|
69
|
+
router.get('/cgi-bin/admin/get-settings', getSettings);
|
|
70
|
+
router.post('/cgi-bin/admin/set-json-data', setJsonData);
|
|
71
|
+
router.post('/cgi-bin/admin/set-rules-tpl', setRulesTpl);
|
|
72
|
+
router.post('/cgi-bin/admin/set-test-rules', setTestRules);
|
|
73
|
+
router.post('/cgi-bin/admin/set-default-rules', setDefaultRules);
|
|
74
|
+
router.post('/cgi-bin/admin/set-account-rules', setAccountRules);
|
|
75
|
+
router.get('/cgi-bin/admin/get-auth-key', getAuthKey);
|
|
76
|
+
router.post('/cgi-bin/admin/set-auth-key', setAuthKey);
|
|
77
|
+
router.post('/cgi-bin/admin/set-spec-pattern', setSpecPattern);
|
|
78
|
+
router.get('/cgi-bin/patterns', patterns);
|
|
79
|
+
router.get('/cgi-bin/allowlist', allowlist);
|
|
80
|
+
router.all('/account/:name/(.*)', proxy);
|
|
81
|
+
router.post('/cgi-bin/account/change-password', changePassword);
|
|
82
|
+
router.get('/follow', follow);
|
|
83
|
+
router.get('/unfollow', unfollow);
|
|
84
|
+
router.get('/redirect', redirect);
|
|
85
|
+
};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
const net = require('net');
|
|
3
|
+
const parseurl = require('parseurl');
|
|
4
|
+
const http = require('http');
|
|
5
|
+
const HOME_DIR = require('os').homedir();
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { getWhistlePath } = require('whistle');
|
|
8
|
+
|
|
9
|
+
const ENV_MAX_AGE = 60 * 60 * 24 * 3;
|
|
10
|
+
const MAX_CLIENT_ID_LEN = 100;
|
|
11
|
+
const CONF_KEY_RE = /^([\w-]{1,64}:?|[\w.-]{1,64}:)$/;
|
|
12
|
+
|
|
13
|
+
exports.AUTH_KEY = `${Date.now()}/${Math.random()}`;
|
|
14
|
+
exports.loadModule = require;
|
|
15
|
+
exports.ENV_MAX_AGE = ENV_MAX_AGE;
|
|
16
|
+
exports.WHISTLE_ENV_HEADER = 'x-whistle-nohost-env';
|
|
17
|
+
exports.CONFIG_DATA_TYPE = 'PROXY_CONFIG';
|
|
18
|
+
exports.COOKIE_NAME = 'whistle_nohost_env';
|
|
19
|
+
exports.WHISTLE_RULE_VALUE = 'x-whistle-rule-value';
|
|
20
|
+
exports.BASE_URL = 'http://local.whistlejs.com/plugin.nohost/';
|
|
21
|
+
|
|
22
|
+
const PLUGINS_DIR = path.resolve(HOME_DIR, 'whistle-plugins');
|
|
23
|
+
exports.PLUGINS_DIR = PLUGINS_DIR;
|
|
24
|
+
|
|
25
|
+
const LOCALHOST = '127.0.0.1';
|
|
26
|
+
const STRIDE = 5;
|
|
27
|
+
let curPort = Math.floor(20001 + (10000 * Math.random()));
|
|
28
|
+
curPort = (curPort + 1) - (curPort % STRIDE);
|
|
29
|
+
|
|
30
|
+
const getPort = (callback) => {
|
|
31
|
+
const server = http.createServer();
|
|
32
|
+
server.on('error', () => {
|
|
33
|
+
if (++curPort % 5 === 0) {
|
|
34
|
+
++curPort;
|
|
35
|
+
}
|
|
36
|
+
getPort(callback);
|
|
37
|
+
});
|
|
38
|
+
server.listen(curPort, LOCALHOST, () => {
|
|
39
|
+
server.removeAllListeners();
|
|
40
|
+
server.close(() => callback(curPort));
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
exports.getPort = getPort;
|
|
45
|
+
|
|
46
|
+
/* eslint-disable no-empty */
|
|
47
|
+
const shasum = (str) => {
|
|
48
|
+
if (typeof str !== 'string') {
|
|
49
|
+
str = '';
|
|
50
|
+
}
|
|
51
|
+
const result = crypto.createHash('sha1');
|
|
52
|
+
result.update(str);
|
|
53
|
+
return result.digest('hex');
|
|
54
|
+
};
|
|
55
|
+
exports.shasum = shasum;
|
|
56
|
+
|
|
57
|
+
const decodeURIComponentSafe = (str) => {
|
|
58
|
+
if (typeof str !== 'string') {
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
return decodeURIComponent(str);
|
|
63
|
+
} catch (e) {}
|
|
64
|
+
return str;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
exports.decodeURIComponentSafe = decodeURIComponentSafe;
|
|
68
|
+
|
|
69
|
+
const parseJSON = (str) => {
|
|
70
|
+
try {
|
|
71
|
+
const result = JSON.parse(str);
|
|
72
|
+
if (typeof result === 'object') {
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
exports.parseJSON = parseJSON;
|
|
79
|
+
|
|
80
|
+
const transformReq = (req, port, host) => {
|
|
81
|
+
const options = parseurl(req);
|
|
82
|
+
options.host = host || '127.0.0.1';
|
|
83
|
+
options.method = req.method;
|
|
84
|
+
options.headers = req.headers;
|
|
85
|
+
delete options.headers.referer;
|
|
86
|
+
delete options.protocol;
|
|
87
|
+
delete options.hostname;
|
|
88
|
+
if (port > 0) {
|
|
89
|
+
options.port = port;
|
|
90
|
+
}
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const client = http.request(options, resolve);
|
|
93
|
+
client.on('error', reject);
|
|
94
|
+
req.pipe(client);
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
exports.transformReq = transformReq;
|
|
99
|
+
exports.transformWhistle = async (ctx, port) => {
|
|
100
|
+
const { req } = ctx;
|
|
101
|
+
const res = await transformReq(req, port);
|
|
102
|
+
ctx.status = res.statusCode;
|
|
103
|
+
ctx.set(res.headers);
|
|
104
|
+
ctx.body = res;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
exports.parseConfig = (ctn) => {
|
|
108
|
+
if (typeof ctn !== 'string') {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
ctn = ctn.trim().split(/\r\n|\r|\n/g);
|
|
112
|
+
let conf;
|
|
113
|
+
ctn.forEach((line) => {
|
|
114
|
+
line = line.replace(/#.*$/, '').trim().split(/\s+/);
|
|
115
|
+
if (line.length) {
|
|
116
|
+
let key = line[0].toLowerCase();
|
|
117
|
+
if (!key || !CONF_KEY_RE.test(key)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
conf = conf || {};
|
|
121
|
+
key = key.replace(':', '');
|
|
122
|
+
if (key !== 'host') {
|
|
123
|
+
key = `x-nohost-${key}`;
|
|
124
|
+
}
|
|
125
|
+
if (conf[key] == null && (!conf.headers || conf.headers[key] == null)) {
|
|
126
|
+
if (key === 'host') {
|
|
127
|
+
conf.host = line[1];
|
|
128
|
+
} else {
|
|
129
|
+
conf.headers = conf.headers || {};
|
|
130
|
+
conf.headers[key] = line[1] || '';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
return conf;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const KEY_RE = /(?:@([^\s@]+)|\{([^\s@]+)\})/ig;
|
|
139
|
+
const resolveConf = (ctn, values) => {
|
|
140
|
+
if (!ctn || !values) {
|
|
141
|
+
return ctn;
|
|
142
|
+
}
|
|
143
|
+
return ctn.replace(KEY_RE, (all, $1, $2) => {
|
|
144
|
+
return values[$1 || $2] || '';
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
exports.resolveConfList = (list, publicList) => {
|
|
149
|
+
if (!list.length || !publicList || !publicList.length) {
|
|
150
|
+
return list;
|
|
151
|
+
}
|
|
152
|
+
const map = {};
|
|
153
|
+
publicList.forEach((conf) => {
|
|
154
|
+
map[conf.name] = conf.rules || '';
|
|
155
|
+
});
|
|
156
|
+
list.forEach((conf) => {
|
|
157
|
+
conf.rules = resolveConf(conf.rules, map);
|
|
158
|
+
});
|
|
159
|
+
return list;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
let REQ_FROM_HEADER;
|
|
163
|
+
let RULE_VALUE_HEADER;
|
|
164
|
+
|
|
165
|
+
exports.initPlugin = (options) => {
|
|
166
|
+
RULE_VALUE_HEADER = options.RULE_VALUE_HEADER;
|
|
167
|
+
REQ_FROM_HEADER = options.config.REQ_FROM_HEADER;
|
|
168
|
+
exports.config = options.config;
|
|
169
|
+
exports.pluginConfig = options.data;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
exports.getRuleValue = (ctx) => {
|
|
173
|
+
const ruleValue = ctx.get(RULE_VALUE_HEADER);
|
|
174
|
+
return ruleValue ? decodeURIComponent(ruleValue) : '';
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
exports.isFromComposer = (ctx) => {
|
|
178
|
+
return ctx.get(REQ_FROM_HEADER) === 'W2COMPOSER';
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
exports.getDomain = function (hostname) {
|
|
182
|
+
if (net.isIP(hostname)) {
|
|
183
|
+
return hostname;
|
|
184
|
+
}
|
|
185
|
+
let list = hostname.split('.');
|
|
186
|
+
const len = list.length;
|
|
187
|
+
if (len < 3) {
|
|
188
|
+
return hostname;
|
|
189
|
+
}
|
|
190
|
+
let wildcard = len > 3;
|
|
191
|
+
if (wildcard) {
|
|
192
|
+
list = list.slice(-3);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (list[1].length > 3 || list[1] === 'url' || list[2] === 'com') {
|
|
196
|
+
wildcard = true;
|
|
197
|
+
list = list.slice(-2);
|
|
198
|
+
}
|
|
199
|
+
if (wildcard) {
|
|
200
|
+
list.unshift('');
|
|
201
|
+
}
|
|
202
|
+
return list.join('.');
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
exports.isClientId = (str) => {
|
|
206
|
+
if (!str || typeof str !== 'string') {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
return str.length <= MAX_CLIENT_ID_LEN;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
exports.getClientId = (ctx) => {
|
|
213
|
+
const clientId = ctx.get('x-whistle-nohost-client-id')
|
|
214
|
+
|| ctx.get('x-whistle-client-id');
|
|
215
|
+
if (clientId && typeof clientId === 'string') {
|
|
216
|
+
return (clientId.trim() || ctx.ip).substring(0, MAX_CLIENT_ID_LEN);
|
|
217
|
+
}
|
|
218
|
+
return ctx.ip;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
exports.changeFilter = (req, followerIp) => {
|
|
222
|
+
if (!req.url.indexOf('/cgi-bin/get-data?') && req.url.indexOf('ip=self') !== -1) {
|
|
223
|
+
followerIp = followerIp ? `,${followerIp}` : '';
|
|
224
|
+
req.url = req.url.replace('ip=self', `ip=clientId${followerIp}`);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
exports.getNohostPluginsPath = () => {
|
|
229
|
+
return path.join(getWhistlePath(), 'nohost_plugins');
|
|
230
|
+
};
|