Merge remote-tracking branch 'origin/master' into master

# Conflicts:
#	packages/mitmproxy/src/lib/proxy/mitmproxy/createRequestHandler.js
pull/67/head
xiaojunnuo@qq.com 2020-11-28 09:08:06 +08:00
commit 5b7a7757c1
89 changed files with 1698 additions and 5765 deletions

147
README.md
View File

@ -1,58 +1,88 @@
# dev-sidecar
开发者边车命名取自service-mesh的service-sidecar意为为开发者打辅助的边车工具
通过本地代理的方式将http请求代理到一些国内的加速通道上
不用`ss小飞机`也能解决一些网站和库无法访问或访问速度慢的问题
不用` qiang`也能解决一些网站和库无法访问或访问速度慢的问题
<a href='https://gitee.com/docmirror/dev-sidecar'><img src='https://gitee.com/docmirror/dev-sidecar/badge/star.svg?theme=dark' alt='star'/></a>
<a href='https://github.com/docmirror/dev-sidecar'><img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github"></a>
## 特性
### 1、 github的release、source、zip下载加速
可解决npm install 时某些安装包下载不下来的问题
### 1、 dns优选
根据网络状况智能解析最佳域名ip地址获取最佳网络速度
### 2、 dns优选
根据网络状况智能解析域名ip地址获取最佳网络速度
比如:
1. 解决git push 偶尔失败需要输入账号密码的问题fatal: TaskCanceledException encountered
2. 解决github头像加载不出来的问题
3. 解决gist.github.com访问不到的问题
建议遇到打开比较慢的国外网站可以优先尝试将该域名添加到dns设置中注意被GFW封杀的无效
### 3、 github的源代码查看raw/blame查看
通过跳转到国内加速链接上
### 2、 请求拦截
拦截打不开的网站,代理到加速镜像站点上去。
可配置多个镜像站作为备份
具备测速机制,当访问失败或超时之后,自动切换到备用站点。
使得目标服务高可用
### 3、 github加速
* release、source、zip下载加速
* clone 加速
* 头像加速
* 解决readme中图片引用无法加载的问题
* gist.github.com 加速
* 解决git push 偶尔失败需要输入账号密码的问题fatal: TaskCanceledException encountered / fatal: HttpRequestException encountered
* raw/blame加速
### 4、 Stack Overflow 加速
* 将ajax.google.com代理到加速CDN上
* recaptcha 图片验证码加速
将ajax.google.com代理到加速CDN上
recaptcha 图片验证码加速
### 5、 google cdn 加速
通过代理到加速链接上
### 5、 npm加速
* 支持开启npm代理
* 官方与淘宝npm registry一键切换,
* 某些npm install的时候并且使用cnpm也无法安装时可以尝试开启npm代理再试
### 6、 更多加速配置
等你来提issue
## 快速开始
目前仅支持windows
### 1、 DevSidecar桌面应用发布啦
暂时只支持windows
### DevSidecar桌面应用
#### 1.1 下载安装包
[点此去下载](https://dev-sidecar.docmirror.cn/update/DevSidecar-1.0.2.exe)
安装后打开:
#### 1 下载安装包
下载安装包:
[阿里云](https://dev-sidecar.docmirror.cn/update/DevSidecar-1.2.1.exe)
[Gitee Release](https://gitee.com/docmirror/dev-sidecar/releases)
[Github Release](https://github.com/docmirror/dev-sidecar/releases)
安装后打开
![](./doc/index.png)
#### 1.2 安装根证书
第一次打开会提示安装根证书
#### 1.3 开始加速吧
去试试打开github
`Download ZIP`、`Release` 下载试试,体验秒下的感觉
比如去下载它: https://github.com/greper/d2-crud-plus/archive/master.zip
#### 2 安装根证书
第一次打开会提示安装证书,根据提示操作即可
>根证书是本地随机生成的,所以不用担心根证书的安全问题
#### 3 开始加速吧
去试试打开github
---------
>第一次访问会去国外的dns服务器上获取ip会比较慢一点后面就快了
---------
### 开启前 vs 开启后
| | 开启前 | 开启后 |
| ---- | ---- | ---- |
|头像| ![](./doc/avatar2.png) |![](./doc/avatar1.png) |
|clone |![](./doc/clone-before.png) |![](./doc/clone.png) |
|zip 下载 |![](./doc/download-before.png) |![](./doc/download.png)秒下的,实在截不到速度的图 |
#### 开启前vs 开启后
![](./doc/avatar2.png)
![](./doc/avatar1.png)
## 最佳实践
@ -60,17 +90,18 @@ recaptcha 图片验证码加速
建议遇到打开比较慢的国外网站可以优先尝试将该域名添加到dns设置中注意被GFW封杀的无效
如果还访问不了就需要寻找镜像cdn进行拦截代理
### npm加速
1. yarn 设置淘宝镜像registry
2. npm设置官方registry。
3. 项目install使用yarn发布包publish用npm互不影响
4. 某些库用cnpm也下载不下来的话可以试试打开dev-sidecar的npm加速
### 其他加速
1. git clone 加速
1. git clone 加速
方式1快捷复制
> 开启脚本支持然后在复制clone链接下方即可复制到加速链接
方式2
> 使用方式用实际的名称替换{}的内容即可加速clone
> https://hub.fastgit.org/{username}/{reponame}.git
> clone 出来的 remote "origin" 为fastgit的地址需要手动改回来
> 你也可以直接使用他们的clone加速工具 [fgit-go](https://github.com/FastGitORG/fgit-go)
2. github.com的镜像网站(注意:不能登录)
>1. [hub.fastgit.org](https://hub.fastgit.org/)
>2. [github.com.cnpmjs.org](https://github.com.cnpmjs.org/) 这个很容易超限
@ -90,14 +121,16 @@ const intercepts = {
// redirect:url, 临时重定向(url会变一些下载资源可以通过此方式配置)
// proxy:url, 代理url不会变没有跨域问题
// abort:true, 取消请求适用于被GFW封锁的资源找不到替代直接取消请求快速失败节省时间
// success:true, 直接返回成功请求(某些请求不想发出去,可以伪装成功返回)
redirect: 'download.fastgit.org'
},
},
'ajax.googleapis.com': {
'.*': {
proxy: 'ajax.loli.net', //代理请求url不会变
backup: ['ajax.proxy.ustclug.org'],
test: 'ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js'
backup: ['ajax.proxy.ustclug.org'], //备份,当前代理请求失败后,将会切换到备用地址
test: 'ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js',
replace:'/(.*)/xxx'//当加速地址的链接和原链接不是完全相同时可以通过正则表达式replace此时proxy通过$1$2来重组url proxy:'ajax.loli.net/xxx/$1'
}
},
'clients*.google.com': {
@ -108,11 +141,9 @@ const intercepts = {
}
```
### DNS优选
某些域名比如api.github.com会被解析到新加坡的ip上新加坡的服务器在上午挺好到了晚上就卡死基本不可用。
所以将这些域名解析到美国服务器上就可以正常访问
另外配置了dns mapping的域名将会从dns获取到的ip列表中选择相对快一点的服务器进行访问
### DNS优选配置
某些域名解析出来的ip会无法访问比如api.github.com会被解析到新加坡的ip上新加坡的服务器在上午挺好到了晚上就卡死基本不可用
通过从dns上获取ip列表切换不同的ip进行尝试最终会挑选到一个最快的ip
```js
dns: {
@ -126,6 +157,31 @@ const intercepts = {
```
注意暂时只支持IPv4的解析
## 问题排查
如果没有加速效果,请根据以下步骤进行排查
#### 1、请确认windows的代理设置处于勾选状态
如何打开查看windows代理设置
* win10: 开始->设置->网络和Internet->最下方代理
* win7: 开始->控制面板->网络和Internet->网络和共享中心->左下角Internet选项->连接选项卡->局域网设置
![](./doc/proxy.png)
#### 2. 360软件会严重拖慢设置代理命令的执行时间
如果开启了360那么软件开启windows代理的时间会特别长需要耐心等待。
#### 3.如果还是不行,请在下方加作者好友,将服务日志发送给作者进行分析
日志打开方式:加速服务->右边日志按钮->另存为
![](./doc/log.png)
## 联系作者
欢迎bug反馈需求建议技术交流等请备注dev-sidecar或简称DS
![](./doc/contact.png)
## 感谢
本项目使用lerna包管理工具
@ -134,6 +190,7 @@ const intercepts = {
本项目参考如下开源项目
* [node-mitmproxy](https://github.com/wuchangming/node-mitmproxy)
* [ReplaceGoogleCDN](https://github.com/justjavac/ReplaceGoogleCDN)
* [github增强油猴脚本](https://greasyfork.org/zh-CN/scripts/412245-github-%E5%A2%9E%E5%BC%BA-%E9%AB%98%E9%80%9F%E4%B8%8B%E8%BD%BD)
本项目加速资源由如下组织提供
* [fastgit](https://fastgit.org/)

BIN
doc/clone-before.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
doc/clone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
doc/contact.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 KiB

BIN
doc/download-before.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
doc/download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 41 KiB

BIN
doc/log.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
doc/proxy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

View File

@ -14,5 +14,5 @@
"ignore": []
}
},
"version": "1.1.0"
"version": "1.2.1"
}

View File

@ -1,6 +1,6 @@
{
"name": "@docmirror/dev-sidecar",
"version": "1.1.0",
"version": "1.2.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@docmirror/dev-sidecar",
"version": "1.1.0",
"version": "1.2.1",
"description": "给开发者的加速代理工具",
"main": "src/index.js",
"keywords": [
@ -16,7 +16,7 @@
"start": "node ./start"
},
"dependencies": {
"@docmirror/mitmproxy": "^1.1.0",
"@docmirror/mitmproxy": "^1.2.1",
"agentkeepalive": "^2.1.1",
"charset": "^1.0.0",
"child_process": "^1.0.2",
@ -30,6 +30,7 @@
"jschardet": "^1.4.1",
"json5": "^2.1.3",
"lodash": "^4.7.0",
"log4js": "^6.3.0",
"lru-cache": "^6.0.0",
"mkdirp": "^0.5.1",
"node-cmd": "^3.0.0",

View File

@ -1,7 +1,7 @@
const Shell = require('./shell')
const lodash = require('lodash')
const defConfig = require('./config/index.js')
const proxyServer = require('@docmirror/mitmproxy')
let configTarget = lodash.cloneDeep(defConfig)
function _deleteDisabledItem (target) {
lodash.forEach(target, (item, key) => {
@ -13,6 +13,7 @@ function _deleteDisabledItem (target) {
}
})
}
const configApi = {
get () {
return configTarget
@ -68,7 +69,7 @@ const configApi = {
})
if (list.length > 0) {
const context = {
ca_cert_path: proxyServer.config.getDefaultCACertPath()
root_ca_cert_path: configApi.get().server.setting.rootCaFile.certPath
}
for (const item of noSetList) {
if (item.value.indexOf('${') >= 0) {

View File

@ -1,30 +1,73 @@
const path = require('path')
function getUserBasePath () {
const userHome = process.env.USERPROFILE
return path.resolve(userHome, './.dev-sidecar')
}
function getRootCaCertPath () {
return getUserBasePath() + '/dev-sidecar.ca.crt'
}
function getRootCaKeyPath () {
return getUserBasePath() + '/dev-sidecar.ca.key.pem'
}
module.exports = {
server: {
enabled: true,
port: 1181,
setting: {
NODE_TLS_REJECT_UNAUTHORIZED: true
NODE_TLS_REJECT_UNAUTHORIZED: true,
script: {
enabled: true,
defaultDir: '../../../scripts/'
},
userBasePath: getUserBasePath(),
rootCaFile: {
certPath: getRootCaCertPath(),
keyPath: getRootCaKeyPath()
}
},
intercepts: {
'github.com': {
'/.*/.*/releases/download/': {
redirect: 'download.fastgit.org'
redirect: 'download.fastgit.org',
desc: 'release文件加速下载跳转地址'
},
'/.*/.*/archive/': {
redirect: 'download.fastgit.org'
},
'/.*/.*/raw/': {
redirect: 'hub.fastgit.org'
replace: '(.+)\\/raw\\/(.+)',
proxy: 'raw.fastgit.org$1/$2'
},
'/.*/.*/blame/': {
redirect: 'hub.fastgit.org'
},
'^/[^/]+/[^/]+$': {
script: [
'jquery',
'github'
],
desc: 'clone加速复制链接脚本'
},
'/.*': {
proxy: 'github.com',
backup: [
'gh.docmirror.top/_proxy'
],
desc: '如果出现dev-sidecar报错可能是备用加速地址dns被污染了需要将本条配置删除'
}
},
'api.github.com': {
'^/_private/browser/stats$': {
success: true,
desc: 'github的访问速度分析上传没有必要直接返回成功'
}
},
'raw.githubusercontent.com': {
'.*': { proxy: 'raw.fastgit.org' }
},
'github11.githubassets.com': {
'.*': { proxy: 'assets.fastgit.org', test: 'https://github.githubassets.com/favicons/favicon.svg' }
'github.githubassets.com': {
'.*': { proxy: 'assets.fastgit.org', test: 'https://github.githubassets.com/favicons/favicon.svg', desc: '静态资源加速' }
},
'customer-stories-feed.github.com': {
'.*': { proxy: 'customer-stories-feed.fastgit.org' }
@ -78,7 +121,13 @@ module.exports = {
'archive.cloudera.com': { '.*': { regexp: '/cdh5/.*', proxy: 'cloudera.proxy.ustclug.org' } },
'downloads.lede-project.org': { '.*': { proxy: 'lede.proxy.ustclug.org' } },
'downloads.openwrt.org': { '.*': { proxy: 'openwrt.proxy.ustclug.org' } },
'secure.gravatar.com': { '.*': { proxy: 'gravatar.proxy.ustclug.org' } }
'secure.gravatar.com': { '.*': { proxy: 'gravatar.proxy.ustclug.org' } },
'*.carbonads.com': {
'/carbon.*': {
abort: true,
desc: '广告拦截'
}
}
},
whiteList: {
'alipay.com': true,
@ -100,11 +149,16 @@ module.exports = {
}
},
mapping: {
'*github.io': 'usa',
'img.shields.io': 'usa',
'*.github.com': 'usa',
'*.githubusercontent.com': 'usa',
'*.githubassets.com': 'usa',
// "解决push的时候需要输入密码的问题",
'github.com': 'usa'
'github.com': 'usa',
'*.vuepress.vuejs.org': 'usa',
'github.docmirror.cn': 'usa',
'gh.docmirror.top': 'usa'
}
}
},

View File

@ -4,14 +4,13 @@ const event = require('./event')
const shell = require('./shell')
const modules = require('./modules')
const lodash = require('lodash')
const proxyServer = require('@docmirror/mitmproxy')
const proxyConfig = proxyServer.config
const log = require('./utils/util.log')
const context = {
config,
shell,
status,
event,
rootCaFile: proxyConfig.getDefaultCACertPath()
log
}
function setupPlugin (key, plugin, context, config) {
@ -43,14 +42,14 @@ module.exports = {
try {
await server.start({ mitmproxyPath })
} catch (err) {
console.error('代理服务启动失败:', err)
log.error('代理服务启动失败:', err)
}
}
if (conf.proxy.enabled) {
try {
await proxy.start()
} catch (err) {
console.error('开启系统代理失败:', err)
log.error('开启系统代理失败:', err)
}
}
try {
@ -60,9 +59,9 @@ module.exports = {
const start = async () => {
try {
await plugin[key].start()
console.log(`插件【${key}】已启动`)
log.info(`插件【${key}】已启动`)
} catch (err) {
console.log(`插件【${key}】启动失败`, err)
log.error(`插件【${key}】启动失败`, err)
}
}
plugins.push(start())
@ -72,7 +71,7 @@ module.exports = {
await Promise.all(plugins)
}
} catch (err) {
console.error('开启插件失败:', err)
log.error('开启插件失败:', err)
}
},
shutdown: async () => {
@ -83,9 +82,9 @@ module.exports = {
const close = async () => {
try {
await plugin[key].close()
console.log(`插件【${key}】已关闭`)
log.info(`插件【${key}】已关闭`)
} catch (err) {
console.log(`插件【${key}】关闭失败`, err)
log.info(`插件【${key}】关闭失败`, err)
}
}
plugins.push(close())
@ -95,23 +94,23 @@ module.exports = {
await Promise.all(plugins)
}
} catch (error) {
console.error('插件关闭失败'.error)
log.error('插件关闭失败'.error)
}
if (status.proxy.enabled) {
try {
await proxy.close()
console.log('系统代理已关闭')
log.info('系统代理已关闭')
} catch (err) {
console.error('系统代理关闭失败', err)
log.error('系统代理关闭失败', err)
}
}
if (status.server.enabled) {
try {
await server.close()
console.log('代理服务已关闭')
log.info('代理服务已关闭')
} catch (err) {
console.error('代理服务关闭失败', err)
log.error('代理服务关闭失败', err)
}
}
},
@ -125,6 +124,7 @@ module.exports = {
shell,
server,
proxy,
plugin
plugin,
log
}
}

View File

@ -1,5 +1,5 @@
const expose = require('./expose.js')
const log = require('./utils/util.log')
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
// 避免异常崩溃
@ -8,11 +8,11 @@ process.on('uncaughtException', function (err) {
// console.error(err.errno)
return
}
console.error('uncaughtException', err)
log.error('uncaughtException', err)
})
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason)
log.error('Unhandled Rejection at: Promise', p, 'reason:', reason)
// application specific logging, throwing an error, or other logic here
})

View File

@ -1,12 +1,12 @@
const nodeConfig = require('./config')
const NodePlugin = function (context) {
const { config, shell, event, rootCaFile } = context
const { config, shell, event, log } = context
const nodeApi = {
async start () {
try {
await nodeApi.setVariables()
} catch (err) {
console.warn('set variables error', err)
log.warn('set variables error', err)
}
const ip = '127.0.0.1'
@ -101,16 +101,17 @@ const NodePlugin = function (context) {
NODE_TLS_REJECT_UNAUTHORIZED: false
*/
const nodeConfig = config.get().plugin.node
const rootCaCertFile = config.get().server.setting.rootCaFile.certPath
if (nodeConfig.setting['strict-ssl']) {
cmds.push('npm config set strict-ssl false')
}
if (nodeConfig.setting.cafile) {
cmds.push(`npm config set cafile "${rootCaFile}"`)
cmds.push(`npm config set cafile "${rootCaCertFile}"`)
}
if (nodeConfig.setting.NODE_EXTRA_CA_CERTS) {
cmds.push(`npm config set NODE_EXTRA_CA_CERTS "${rootCaFile}"`)
env.push({ key: 'NODE_EXTRA_CA_CERTS', value: rootCaFile })
cmds.push(`npm config set NODE_EXTRA_CA_CERTS "${rootCaCertFile}"`)
env.push({ key: 'NODE_EXTRA_CA_CERTS', value: rootCaCertFile })
}
if (nodeConfig.setting.NODE_TLS_REJECT_UNAUTHORIZED) {
@ -123,7 +124,7 @@ const NodePlugin = function (context) {
await shell.setSystemEnv({ list: env })
}
event.fire('status', { key: 'plugin.node.enabled', value: true })
console.info('开启【NPM】代理成功')
log.info('开启【NPM】代理成功')
return ret
},
@ -137,7 +138,7 @@ const NodePlugin = function (context) {
]
const ret = await shell.exec(cmds, { type: 'cmd' })
event.fire('status', { key: 'plugin.node.enabled', value: false })
console.info('关闭【NPM】代理成功')
log.info('关闭【NPM】代理成功')
return ret
}
}

View File

@ -1,5 +1,5 @@
const ProxyPlugin = function (context) {
const { config, event, shell } = context
const { config, event, shell, log } = context
const api = {
async start () {
return api.setProxy()
@ -13,7 +13,7 @@ const ProxyPlugin = function (context) {
const ip = '127.0.0.1'
const port = config.get().server.port
await shell.setSystemProxy({ ip, port })
console.log(`开启系统代理成功:${ip}:${port}`)
log.info(`开启系统代理成功:${ip}:${port}`)
event.fire('status', { key: 'proxy.enabled', value: true })
return { ip, port }
},
@ -21,11 +21,11 @@ const ProxyPlugin = function (context) {
async unsetProxy () {
try {
await shell.setSystemProxy()
event.fire('status', { key: 'proxy.enabled', vlaue: false })
console.log('关闭系统代理成功')
event.fire('status', { key: 'proxy.enabled', value: false })
log.info('关闭系统代理成功')
return true
} catch (err) {
console.error('关闭系统代理失败', err)
log.error('关闭系统代理失败', err)
return false
}
}

View File

@ -3,6 +3,8 @@ const event = require('../../event')
const status = require('../../status')
const lodash = require('lodash')
const fork = require('child_process').fork
const log = require('../../utils/util.log')
const fs = require('fs')
let server
function fireStatus (status) {
event.fire('status', { key: 'server.enabled', value: status })
@ -44,7 +46,10 @@ const serverApi = {
})
}
// fireStatus('ing') // 启动中
const serverProcess = fork(mitmproxyPath, [JSON.stringify(serverConfig)])
const basePath = serverConfig.setting.userBasePath
const runningConfig = basePath + '/running.json'
fs.writeFileSync(runningConfig, JSON.stringify(serverConfig))
const serverProcess = fork(mitmproxyPath, [runningConfig])
server = {
id: serverProcess.pid,
process: serverProcess,
@ -53,7 +58,7 @@ const serverApi = {
}
}
serverProcess.on('message', function (msg) {
console.log('收到子进程消息', msg)
log.info('收到子进程消息', msg)
if (msg.type === 'status') {
fireStatus(msg.event)
} else if (msg.type === 'error') {
@ -63,7 +68,7 @@ const serverApi = {
event.fire('error', { key: 'server', value: 'EADDRINUSE', error: msg.event })
}
})
return { port: config.port }
return { port: runningConfig.port }
},
async kill () {
if (server) {
@ -81,21 +86,21 @@ const serverApi = {
// fireStatus('ing')// 关闭中
server.close((err) => {
if (err) {
console.log('close error', err, ',', err.code, ',', err.message, ',', err.errno)
log.info('close error', err, ',', err.code, ',', err.message, ',', err.errno)
if (err.code === 'ERR_SERVER_NOT_RUNNING') {
console.log('代理服务关闭成功')
log.info('代理服务关闭成功')
resolve()
return
}
console.log('代理服务关闭失败', err)
log.info('代理服务关闭失败', err)
reject(err)
} else {
console.log('代理服务关闭成功')
log.info('代理服务关闭成功')
resolve()
}
})
} else {
console.log('server is null')
log.info('server is null')
resolve()
}
})

View File

@ -7,7 +7,7 @@ const Registry = require('winreg')
// const cmd = require('node-cmd')
const refreshInternetPs = require('./refresh-internet')
const PowerShell = require('node-powershell')
const log = require('../../../utils/util.log')
const _lanIP = [
'localhost',
'127.*',
@ -42,9 +42,9 @@ async function _winUnsetProxy (exec) {
_winAsyncRegSet(regKey, 'ProxyEnable', Registry.REG_DWORD, 0),
_winAsyncRegSet(regKey, 'ProxyServer', Registry.REG_SZ, '')
])
console.log('代理关闭成功等待refresh')
log.info('代理关闭成功等待refresh')
await exec(refreshInternetPs, { type: 'ps' })
console.log('代理关闭refresh完成')
log.info('代理关闭refresh完成')
return true
}
@ -58,7 +58,7 @@ async function _winSetProxy (exec, ip, port) {
for (const string of _lanIP) {
lanIpStr += string + ';'
}
// console.log('lanIps:', lanIpStr, ip, port)
// log.info('lanIps:', lanIpStr, ip, port)
await Promise.all([
_winAsyncRegSet(regKey, 'MigrateProxy', Registry.REG_DWORD, 1),
_winAsyncRegSet(regKey, 'ProxyEnable', Registry.REG_DWORD, 1),
@ -66,9 +66,9 @@ async function _winSetProxy (exec, ip, port) {
_winAsyncRegSet(regKey, 'ProxyServer', Registry.REG_SZ, `${ip}:${port}`),
_winAsyncRegSet(regKey, 'ProxyOverride', Registry.REG_SZ, lanIpStr)
])
console.log('代理设置成功等待refresh')
log.info('代理设置成功等待refresh')
await exec(refreshInternetPs)
console.log('代理设置refresh完成')
log.info('代理设置refresh完成')
return true
}
@ -84,32 +84,16 @@ function _winAsyncRegSet (regKey, name, type, value) {
})
}
async function _winResetWininetProxySettings (script) {
const ps = new PowerShell({
executionPolicy: 'Bypass',
noProfile: true
})
ps.addCommand(script)
try {
const ret = await ps.invoke()
console.log('ps complete', script)
return ret
} finally {
ps.dispose()
}
}
const executor = {
async windows (exec, params) {
if (params == null) {
// 清空代理
console.log('关闭代理')
log.info('关闭代理')
return _winUnsetProxy(exec)
} else {
// 设置代理
const { ip, port } = params
console.log('设置代理', ip, port)
log.info('设置代理', ip, port)
return _winSetProxy(exec, ip, port)
}
},

View File

@ -1,17 +1,16 @@
const Shell = require('../shell')
const execute = Shell.execute
const proxyServer = require('@docmirror/mitmproxy')
const executor = {
async windows (exec) {
const cmds = ['start ' + proxyServer.config.getDefaultCACertPath()]
async windows (exec, { certPath }) {
const cmds = ['start "" "' + certPath + '"']
// eslint-disable-next-line no-unused-vars
const ret = await exec(cmds, { type: 'cmd' })
return true
},
async linux (exec, { port }) {
async linux (exec, { certPath }) {
throw Error('暂未实现此功能')
},
async mac (exec, { port }) {
async mac (exec, { certPath }) {
throw Error('暂未实现此功能')
}
}

View File

@ -4,7 +4,7 @@ const childProcess = require('child_process')
const _exec = childProcess.exec
const exec = util.promisify(_exec)
const PowerShell = require('node-powershell')
const log = require('../utils/util.log')
class SystemShell {
static async exec (cmds, args) {
throw new Error('You have to implement the method exec!')
@ -52,7 +52,7 @@ class WindowsSystemShell extends SystemShell {
try {
const ret = await ps.invoke()
// console.log('ps complete', cmds)
// log.info('ps complete', cmds)
return ret
} finally {
ps.dispose()
@ -64,7 +64,7 @@ class WindowsSystemShell extends SystemShell {
}
// compose += '&& exit'
const ret = await childExec(compose)
// console.log('cmd complete:', compose)
// log.info('cmd complete:', compose)
return ret
}
}
@ -75,13 +75,13 @@ function childExec (composeCmds) {
const childProcess = require('child_process')
childProcess.exec(composeCmds, function (error, stdout, stderr) {
if (error) {
console.error('cmd 命令执行错误:', composeCmds, error, stderr)
log.error('cmd 命令执行错误:', composeCmds, error, stderr)
reject(error)
} else {
// console.log('cmd 命令完成:', stdout)
// log.info('cmd 命令完成:', stdout)
resolve(stdout)
}
// console.log('关闭 cmd')
// log.info('关闭 cmd')
// ps.kill('SIGINT')
})
})

View File

@ -1,5 +1,6 @@
const event = require('./event')
const lodash = require('lodash')
const log = require('./utils/util.log')
const status = {
server: { enabled: false },
proxy: {},
@ -8,7 +9,7 @@ const status = {
event.register('status', (event) => {
lodash.set(status, event.key, event.value)
console.log('status changed:', event)
log.info('status changed:', event)
}, -999)
module.exports = status

View File

@ -1,6 +0,0 @@
const os = require('os')
module.exports = {
isWindows7 () {
const version = os.release()
}
}

View File

@ -1,9 +0,0 @@
const os = require('os')
const util = {
getNodeVersion () {
const version = process.version
console.log(version)
}
}
util.getNodeVersion()
module.exports = util

View File

@ -0,0 +1,11 @@
const log4js = require('log4js')
const config = require('../config/index')
function getDefaultConfigBasePath () {
return config.server.setting.userBasePath
}
log4js.configure({
appenders: { std: { type: 'stdout' }, file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename: getDefaultConfigBasePath() + '/logs/core.log' } },
categories: { default: { appenders: ['file', 'std'], level: 'info' } }
})
const logger = log4js.getLogger('server')
module.exports = logger

View File

@ -0,0 +1,3 @@
const proxyConfig = require('@docmirror/mitmproxy/config.js')
module.exports = {
}

View File

@ -1,4 +1,10 @@
// eslint-disable-next-line no-unused-vars
const server = require('@docmirror/mitmproxy')
const config = JSON.parse(process.argv[2])
const configPath = process.argv[2]
const fs = require('fs')
const path = require('path')
const configJson = fs.readFileSync(configPath)
const config = JSON.parse(configJson)
const scriptDir = '../../gui/extra/scripts/'
config.setting.script.defaultDir = path.join(__dirname, scriptDir)
server.start(config)

View File

@ -6,7 +6,7 @@
abort: true
},
}
}
},
},
plugin: {
node: {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,288 @@
// ==UserScript==
// @name Github 增强 - 高速下载
// @version 1.2.4
// @author X.I.U
// @description 高速下载 Clone、Release、Raw、Code(ZIP) 等文件、项目列表单文件快捷下载 (☁)
// @match https://github.com/*/*
// @match https://github.com/*/*/releases
// @match https://github.com/*/*/releases/*
// @icon https://github.githubassets.com/favicon.ico
// @require https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_openInTab
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_notification
// @license GPL-3.0 License
// @run-at document-end
// @namespace https://greasyfork.org/scripts/412245
// ==/UserScript==
(function () {
console.log('github script loaded')
var download_url = [
'https://gh.con.sh',
'https://gh.api.99988866.xyz',
'https://download.fastgit.org',
'https://pd.zwc365.com/seturl',
'https://g.ioiox.com',
'https://git.yumenaka.net'
]
var download_url_name = ['美国', '美国', '日本东京', '中国香港', '中国香港', '美国洛杉矶']
var clone_url = [
'https://hub.fastgit.org',
'https://gitclone.com',
'https://github.com.cnpmjs.org'
]
var raw_url = [
'https://raw.githubusercontent.com',
'https://cdn.jsdelivr.net',
'https://raw.fastgit.org'
]
var raw_url_name = ['Github 原生', '中国国内', '中国香港', '美国洛杉矶']
var raw_url_tip = [
'',
'注意该加速源存在缓存机制24小时所以文件可能不是最新。&#10;注意:当前分支所有文件总文件大小超过 50MB 时,该加速源不可用。&#10;注意:当前分支名为版本号格式时(如 v1.2.3),该高速下载链接因格式限制不可用。&#10;&#10;',
'注意:单个文件太大时可能会提示超时(实时获取中),请重试。&#10;&#10;',
'注意:经过测试,该加速源存在文件格式限制,如果无法下载说明不支持该文件格式。&#10;&#10;'
]
var svg = [
'<svg class="octicon octicon-file-zip mr-3" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.5 1.75a.25.25 0 01.25-.25h3a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h2.086a.25.25 0 01.177.073l2.914 2.914a.25.25 0 01.073.177v8.586a.25.25 0 01-.25.25h-.5a.75.75 0 000 1.5h.5A1.75 1.75 0 0014 13.25V4.664c0-.464-.184-.909-.513-1.237L10.573.513A1.75 1.75 0 009.336 0H3.75A1.75 1.75 0 002 1.75v11.5c0 .649.353 1.214.874 1.515a.75.75 0 10.752-1.298.25.25 0 01-.126-.217V1.75zM8.75 3a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h-.5zM6 5.25a.75.75 0 01.75-.75h.5a.75.75 0 010 1.5h-.5A.75.75 0 016 5.25zm2 1.5A.75.75 0 018.75 6h.5a.75.75 0 010 1.5h-.5A.75.75 0 018 6.75zm-1.25.75a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h-.5zM8 9.75A.75.75 0 018.75 9h.5a.75.75 0 010 1.5h-.5A.75.75 0 018 9.75zm-.75.75a1.75 1.75 0 00-1.75 1.75v3c0 .414.336.75.75.75h2.5a.75.75 0 00.75-.75v-3a1.75 1.75 0 00-1.75-1.75h-.5zM7 12.25a.25.25 0 01.25-.25h.5a.25.25 0 01.25.25v2.25H7v-2.25z"></path></svg>',
'<svg class="octicon octicon-clippy" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M5.75 1a.75.75 0 00-.75.75v3c0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75v-3a.75.75 0 00-.75-.75h-4.5zm.75 3V2.5h3V4h-3zm-2.874-.467a.75.75 0 00-.752-1.298A1.75 1.75 0 002 3.75v9.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 13.25v-9.5a1.75 1.75 0 00-.874-1.515.75.75 0 10-.752 1.298.25.25 0 01.126.217v9.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-9.5a.25.25 0 01.126-.217z"></path></svg>',
'<svg class="octicon octicon-cloud-download" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path d="M9 12h2l-3 3-3-3h2V7h2v5zm3-8c0-.44-.91-3-4.5-3C5.08 1 3 2.92 3 5 1.02 5 0 6.52 0 8c0 1.53 1 3 3 3h3V9.7H3C1.38 9.7 1.3 8.28 1.3 8c0-.17.05-1.7 1.7-1.7h1.3V5c0-1.39 1.56-2.7 3.2-2.7 2.55 0 3.13 1.55 3.2 1.8v1.2H12c.81 0 2.7.22 2.7 2.2 0 2.09-2.25 2.2-2.7 2.2h-2V11h2c2.08 0 4-1.16 4-3.5C16 5.06 14.08 4 12 4z"></path></svg>'
]
var style = ['padding:0 6px;margin-right: -1px;border-radius: 2px;background-color: #ffffff;border-color: rgba(27, 31, 35, 0.1);font-size: 11px;color: #888888;']
var menu_raw_fast = GM_getValue('xiu2_menu_raw_fast')
var menu_menu_raw_fast_ID; var menu_feedBack_ID
if (menu_raw_fast == null || menu_raw_fast == '中国国内') { menu_raw_fast = 1; GM_setValue('xiu2_menu_raw_fast', 1) }; // 调整上个版本的设置存储变量内容
registerMenuCommand()
// 注册脚本菜单
function registerMenuCommand () {
if (menu_feedBack_ID) { // 如果反馈菜单ID不是 null则删除所有脚本菜单
GM_unregisterMenuCommand(menu_menu_raw_fast_ID)
GM_unregisterMenuCommand(menu_feedBack_ID)
menu_raw_fast = GM_getValue('xiu2_menu_raw_fast')
}
menu_menu_raw_fast_ID = GM_registerMenuCommand(`🔄 [ ${raw_url_name[menu_raw_fast]} ] 加速源 (☁) - 点击切换`, menu_toggle_raw_fast)
menu_feedBack_ID = GM_registerMenuCommand('💬 反馈 & 建议 [Github]', function () { window.GM_openInTab('https://github.com/XIU2/UserScript', { active: true, insert: true, setParent: true }) })
}
// 切换加速源
function menu_toggle_raw_fast () {
if (menu_raw_fast >= raw_url_name.length - 1) { // 如果当前加速源位置大于等于加速源总数,则改为第一个加速源,反之递增下一个加速源
menu_raw_fast = 0
} else {
menu_raw_fast += 1
}
GM_setValue('xiu2_menu_raw_fast', menu_raw_fast)
console.log(11111)
delDownLink() // 删除旧加速源
console.log(22222)
addDownLink() // 添加新加速源
console.log(33333)
GM_notification(`已切换加速源为:${raw_url_name[menu_raw_fast]}`) // 提示消息
registerMenuCommand() // 重新注册脚本菜单
};
addRelease() // Release 加速
addDownloadZIP() // Source Code 加速
addGitClone() // Download ZIP/Code(ZIP) 加速
addRawFile() // Raw 加速
setTimeout(addDownLink, 2000) // 添加 Raw 下载链接(☁),延迟 2 秒执行,避免被 pjax 刷掉
document.addEventListener('pjax:success', function () { // pjax 事件发生后
addRelease() // Release 加速
addDownloadZIP() // Source Code 加速
addGitClone() // Download ZIP/Code(ZIP) 加速
addRawFile() // 添加 Raw 加速按钮
setTimeout(addDownLink, 2000) // 添加 Raw 下载链接(☁),延迟 2 秒执行,避免被 pjax 刷掉
})
// Release
function addRelease () {
$('.Box.Box--condensed').each(function () {
$(this).find('.d-flex.Box-body>a').each(function () {
var href = $(this).attr('href')
var url = [
download_url[0] + '/https://github.com' + href,
download_url[1] + '/https://github.com' + href,
download_url[2] + href,
download_url[3] + '/https://github.com' + href,
download_url[4] + '/https://github.com' + href,
download_url[5] + '/https://github.com' + href
]
var html = `<div style="display: flex;justify-content: flex-end;">
<div><a style="${style[0]}" class="btn" href="${url[0]}" rel="noreferrer noopener nofollow">${download_url_name[0]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[1]}" rel="noreferrer noopener nofollow">${download_url_name[1]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[2]}" rel="noreferrer noopener nofollow">${download_url_name[2]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[3]}" rel="noreferrer noopener nofollow">${download_url_name[3]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[4]}" rel="noreferrer noopener nofollow">${download_url_name[4]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[5]}" rel="noreferrer noopener nofollow">${download_url_name[5]}</a></div>
</div>`
$(this).next().after(html)
})
// 修改[文件大小]元素样式
document.querySelectorAll('small.pl-2.text-gray.flex-shrink-0').forEach(el => el.style.cssText = 'display: flex; justify-content: flex-end; flex-grow: 1; margin-right: 8px;')
// Source Code
$(this).find('.d-block.Box-body>a').each(function () {
var href = $(this).attr('href')
var url = [
download_url[0] + '/https://github.com' + href,
download_url[1] + '/https://github.com' + href,
download_url[2] + href,
download_url[3] + '/https://github.com' + href,
download_url[4] + '/https://github.com' + href,
download_url[5] + '/https://github.com' + href
]
var html = `<div style="display: flex;justify-content: flex-end;flex-grow: 1;">
<div><a style="${style[0]}" class="btn" href="${url[0]}" rel="noreferrer noopener nofollow">${download_url_name[0]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[1]}" rel="noreferrer noopener nofollow">${download_url_name[1]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[2]}" rel="noreferrer noopener nofollow">${download_url_name[2]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[3]}" rel="noreferrer noopener nofollow">${download_url_name[3]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[4]}" rel="noreferrer noopener nofollow">${download_url_name[4]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[5]}" rel="noreferrer noopener nofollow">${download_url_name[5]}</a></div>
</div>`
$(this).after(html)
})
})
// 修改 Source code 样式,使其和加速按钮并列一排
document.querySelectorAll('div.d-block.py-1.py-md-2.Box-body.px-2').forEach(el => el.className = 'd-flex py-1 py-md-2 Box-body px-2')
}
// Download ZIP
function addDownloadZIP () {
$('.dropdown-menu.dropdown-menu-sw.p-0 ul li:last-child').each(function () {
var href = $(this).children('a').attr('href')
var url = [
download_url[0] + '/https://github.com' + href,
download_url[1] + '/https://github.com' + href,
download_url[2] + href,
download_url[3] + '/https://github.com' + href,
download_url[4] + '/https://github.com' + href,
download_url[5] + '/https://github.com' + href
]
var html = `
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[0]}">${svg[0]}Download ZIP ${download_url_name[0]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[1]}">${svg[0]}Download ZIP ${download_url_name[1]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[2]}">${svg[0]}Download ZIP ${download_url_name[2]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[3]}">${svg[0]}Download ZIP ${download_url_name[3]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[4]}">${svg[0]}Download ZIP ${download_url_name[4]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[5]}">${svg[0]}Download ZIP ${download_url_name[5]}</a></li>
`
$(this).after(html)
})
}
// Git Clone
function addGitClone () {
$("[role='tabpanel'] div.input-group").first().each(function () {
var href_split = location.href.split('/')
var url = [
clone_url[0] + '/' + href_split[3] + '/' + href_split[4] + '.git',
clone_url[1] + '/github.com/' + href_split[3] + '/' + href_split[4] + '.git',
clone_url[2] + '/' + href_split[3] + '/' + href_split[4] + '.git'
]
var html = `
<div class="input-group" style="margin-top: 4px;"><input value="${url[0]}" aria-label="${url[0]}" type="text" class="form-control input-monospace input-sm bg-gray-light" data-autoselect="" readonly=""><div class="input-group-button"><clipboard-copy value="${url[0]}" aria-label="Copy to clipboard" class="btn btn-sm" tabindex="0" role="button">${svg[1]}</clipboard-copy></div></div>
<div class="input-group" style="margin-top: 4px;"><input value="${url[1]}" aria-label="${url[1]}" type="text" class="form-control input-monospace input-sm bg-gray-light" data-autoselect="" readonly=""><div class="input-group-button"><clipboard-copy value="${url[1]}" aria-label="Copy to clipboard" class="btn btn-sm" tabindex="0" role="button">${svg[1]}</clipboard-copy></div></div>
<div class="input-group" style="margin-top: 4px;"><input value="${url[2]}" aria-label="${url[2]}" type="text" class="form-control input-monospace input-sm bg-gray-light" data-autoselect="" readonly=""><div class="input-group-button"><clipboard-copy value="${url[2]}" aria-label="Copy to clipboard" class="btn btn-sm" tabindex="0" role="button">${svg[1]}</clipboard-copy></div></div>
`
$(this).after(html)
})
}
// Raw
function addRawFile () {
$('#raw-url').each(function () {
var href = location.href.replace('https://github.com', '')
var href2 = href.replace('/blob/', '/')
var url = [
raw_url[1] + '/gh' + href.replace('/blob/', '@'),
raw_url[2] + href2,
download_url[5] + '/' + raw_url[0] + href2
]
var html = `
<a href="${url[0]}" title="${raw_url_tip[1]}" role="button" rel="noreferrer noopener nofollow" class="btn btn-sm BtnGroup-item">${raw_url_name[1]}</a>
<a href="${url[1]}" title="${raw_url_tip[2]}" role="button" rel="noreferrer noopener nofollow" class="btn btn-sm BtnGroup-item">${raw_url_name[2]}</a>
<a href="${url[2]}" title="${raw_url_tip[3]}" role="button" rel="noreferrer noopener nofollow" class="btn btn-sm BtnGroup-item">${raw_url_name[3]}</a>
`
$(this).after(html)
})
}
// 添加 Raw 下载链接(☁)
function addDownLink () {
// 如果不是项目文件页面,就返回
var files = $('div.Box-row svg.octicon.octicon-file')
if (files.length === 0) return
var files1 = $('a.fileDownLink')
if (files1.length > 0) return
// 鼠标指向则显示
var mouseOverHandler = function (evt) {
var elem = evt.currentTarget
var aElm_new = elem.querySelectorAll('.fileDownLink')
var aElm_now = elem.querySelectorAll('svg.octicon.octicon-file.text-gray-light')
aElm_new.forEach(el => el.style.cssText = 'display: inline')
aElm_now.forEach(el => el.style.cssText = 'display: none')
}
// 鼠标离开则隐藏
var mouseOutHandler = function (evt) {
var elem = evt.currentTarget
var aElm_new = elem.querySelectorAll('.fileDownLink')
var aElm_now = elem.querySelectorAll('svg.octicon.octicon-file.text-gray-light')
aElm_new.forEach(el => el.style.cssText = 'display: none')
aElm_now.forEach(el => el.style.cssText = 'display: inline')
}
// 循环添加
files.each(function (i, fileElm) {
var trElm = fileElm.parentNode.parentNode
var cntElm_a = trElm.querySelector('.css-truncate.css-truncate-target.d-block.width-fit a')
var cntElm_svg = trElm.querySelector('.mr-3.flex-shrink-0 svg.octicon.octicon-file.text-gray-light')
var Name = cntElm_a.innerText
var href = cntElm_a.attributes.href.nodeValue.replace('https://github.com', '')
var href2 = href.replace('/blob/', '/'); var url; var url_name; var url_tip = ''
switch (menu_raw_fast) {
case 0:
url = raw_url[0] + href2
url_name = raw_url_name[0]
url_tip = raw_url_tip[0]
break
case 1:
url = raw_url[1] + '/gh' + href.replace('/blob/', '@')
url_name = raw_url_name[1]
url_tip = raw_url_tip[1]
break
case 2:
url = raw_url[2] + href2
url_name = raw_url_name[2]
url_tip = raw_url_tip[2]
break
case 3:
url = download_url[5] + '/' + raw_url[0] + href2
url_name = download_url_name[5]
url_tip = raw_url_tip[3]
break
}
var html = ` <a href="${url}" download="${Name}" target="_blank" rel="noreferrer noopener nofollow" class="fileDownLink" style="display: none;" title="「${url_name}」&#10;&#10;[Alt + 左键] 或 [右键 - 另存为...] 下载文件。&#10;注意:鼠标点击 [☁] 图标,而不是左侧的文件名!&#10;&#10;${url_tip}提示:点击浏览器右上角 Tampermonkey 扩展图标 - [ ${raw_url_name[menu_raw_fast]} ] 加速源 (☁) 即可切换。">${svg[2]}</a>`
$(cntElm_svg).after(html)
// 绑定鼠标事件
trElm.onmouseover = mouseOverHandler
trElm.onmouseout = mouseOutHandler
})
}
// 删除 Raw 快捷下载(☁)
function delDownLink () {
var aElm = document.querySelectorAll('.fileDownLink')
for (var num = 0; num < aElm.length; num++) {
aElm[num].remove()
};
}
})()

View File

@ -0,0 +1,9 @@
window.__ds_global__={
GM_registerMenuCommand: () => {},
GM_unregisterMenuCommand: () => {},
GM_openInTab: () => {},
GM_getValue: () => {},
GM_setValue: () => {},
GM_notification: () => {}
}
console.log('ds_global loaded')

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "@docmirror/dev-sidecar-gui",
"version": "1.1.0",
"version": "1.2.1",
"private": false,
"license": "MPL-2.0",
"scripts": {
@ -14,8 +14,8 @@
},
"main": "background.js",
"dependencies": {
"@docmirror/dev-sidecar": "^1.1.0",
"@docmirror/mitmproxy": "^1.1.0",
"@docmirror/dev-sidecar": "^1.2.1",
"@docmirror/mitmproxy": "^1.2.1",
"ant-design-vue": "^1.6.5",
"core-js": "^3.6.5",
"electron-baidu-tongji": "^1.0.5",
@ -24,16 +24,13 @@
"es-abstract": "^1.17.7",
"json5": "^2.1.3",
"lodash": "^4.17.20",
"log4js": "^6.3.0",
"sass": "^1.27.1",
"sass-loader": "^10.0.4",
"vue": "^2.6.11",
"vue-json-editor": "^1.4.2",
"vue-router": "^3.4.8"
},
"resolutions": {
"hexoid": "^1.0.0",
"formidable": "2.0.0-canary.20200504.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",

View File

@ -6,7 +6,7 @@ import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import bridge from './bridge/index'
import updateHandle from './bridge/update-handle'
import { ebtMain } from './tongji'
import log from './utils/util.log'
// eslint-disable-next-line no-unused-vars
const isMac = process.platform === 'darwin'
@ -60,7 +60,7 @@ function setTray (app) {
// appTray.setContextMenu(contextMenu)
// appTray.on('double-click', function () {
// console.log('double click')
// log.info('double click')
// win.show()
// })
appTray.on('right-click', function (event, bounds) {
@ -112,7 +112,14 @@ function createWindow () {
})
}
function quit (app) {
async function beforeQuit () {
return bridge.devSidecar.api.shutdown()
}
async function quit (app, callback) {
if (tray) {
tray.displayBalloon({ title: '正在关闭', content: '关闭中,请稍候。。。' })
}
await beforeQuit()
app.quit()
}
@ -122,22 +129,22 @@ app.disableHardwareAcceleration() // 禁用gpu
// 禁止双开
const isFirstInstance = app.requestSingleInstanceLock()
if (!isFirstInstance) {
console.log('is second instance')
log.info('is second instance')
setTimeout(() => {
app.quit()
}, 1000)
} else {
app.on('before-quit', async (event) => {
console.log('before-quit')
event.preventDefault()
// if (tray) {
// tray.displayBalloon({ title: '正在关闭,请稍候...', content: '正在关闭中,请稍候。。。' })
// }
await bridge.devSidecar.api.shutdown()
app.exit()
log.info('before-quit')
// event.preventDefault()
// // if (tray) {
// // tray.displayBalloon({ title: '正在关闭,请稍候...', content: '正在关闭中,请稍候。。。' })
// // }
// await bridge.devSidecar.api.shutdown()
// app.exit()
})
app.on('second-instance', (event, commandLine, workingDirectory) => {
console.log('new app started', commandLine)
log.info('new app started', commandLine)
if (win) {
win.show()
win.focus()
@ -170,7 +177,7 @@ if (!isFirstInstance) {
// try {
// await installExtension(VUEJS_DEVTOOLS)
// } catch (e) {
// console.error('Vue Devtools failed to install:', e.toString())
// log.error('Vue Devtools failed to install:', e.toString())
// }
}
createWindow()
@ -183,11 +190,11 @@ if (!isFirstInstance) {
return true
}
})
// updateUrl = 'https://dev-sidecar.docmirror.cn/update/'
updateUrl = 'http://localhost/dev-sidecar/'
updateUrl = 'https://dev-sidecar.docmirror.cn/update/'
// updateUrl = 'http://localhost/dev-sidecar/'
}
// 自动更新
updateHandle(win, updateUrl)
updateHandle(app, win, beforeQuit, updateUrl)
// 百度分析
ebtMain(ipcMain, isDevelopment)
@ -196,7 +203,7 @@ if (!isFirstInstance) {
// 最小化到托盘
tray = setTray(app)
} catch (err) {
console.log('err', err)
log.info('err', err)
}
})
}

View File

@ -6,10 +6,9 @@ import JSON5 from 'json5'
import path from 'path'
const pk = require('../../package.json')
const mitmproxyPath = path.join(__dirname, 'mitmproxy.js')
const log = require('../utils/util.log')
const getDefaultConfigBasePath = function () {
const userHome = process.env.HOME || process.env.USERPROFILE
return path.resolve(userHome, './.dev-sidecar')
return DevSidecar.api.config.get().server.setting.userBasePath
}
const localApi = {
/**
@ -22,15 +21,17 @@ const localApi = {
lodash.merge(core, local)
const list = []
_deepFindFunction(list, core, '')
// console.log('api list:', list)
// log.info('api list:', list)
return list
},
info: {
get () {
console.log(pk)
return {
version: pk.version
}
},
getConfigDir () {
return getDefaultConfigBasePath()
}
},
/**
@ -81,6 +82,7 @@ const localApi = {
*/
save (newConfig) {
// 对比默认config的异同
DevSidecar.api.config.set(newConfig)
const defConfig = DevSidecar.api.config.getDefault()
const saveConfig = doMerge(defConfig, newConfig)
fs.writeFileSync(_getConfigPath(), JSON5.stringify(saveConfig, null, 2))
@ -178,23 +180,23 @@ export default {
target = lodash.get(DevSidecar.api, api)
}
if (target == null) {
console.log('找不到此接口方法:', api)
log.info('找不到此接口方法:', api)
}
let param
if (args.length >= 2) {
param = args[1]
}
const ret = target(param)
// console.log('api:', api, 'ret:', ret)
// log.info('api:', api, 'ret:', ret)
return ret
})
// 注册从core里来的事件并转发给view
DevSidecar.api.event.register('status', (event) => {
console.log('bridge on status', event)
log.info('bridge on status', event)
win.webContents.send('status', { ...event })
})
DevSidecar.api.event.register('error', (event) => {
console.error('bridge on error', event)
log.error('bridge on error', event)
win.webContents.send('error.core', event)
})

View File

@ -1,4 +1,15 @@
// eslint-disable-next-line no-unused-vars
const server = require('@docmirror/mitmproxy')
const config = JSON.parse(process.argv[2])
const configPath = process.argv[2]
const fs = require('fs')
const path = require('path')
const configJson = fs.readFileSync(configPath)
const config = JSON.parse(configJson)
let scriptDir = '../extra/scripts/'
if (process.env.NODE_ENV === 'development') {
scriptDir = '../extra/scripts/'
}
config.setting.script.defaultDir = path.join(__dirname, scriptDir)
server.start(config)

View File

@ -1,5 +1,6 @@
import { ipcMain, dialog } from 'electron'
import { autoUpdater } from 'electron-updater'
import log from '../utils/util.log'
import path from 'path'
// win是所有窗口的引用
// const path = require('path') // 引入path模块
@ -7,7 +8,7 @@ import path from 'path'
// eslint-disable-next-line no-unused-vars
const isMac = process.platform === 'darwin'
// 检测更新在你想要检查更新的时候执行renderer事件触发后的操作自行编写
function updateHandle (win, updateUrl) {
function updateHandle (app, win, beforeQuit, updateUrl) {
// // 更新前,删除本地安装包 ↓
// const updaterCacheDirName = 'dev-sidecar-updater'
// const updatePendingPath = path.join(autoUpdater.app.baseCachePath, updaterCacheDirName, 'pending')
@ -31,30 +32,30 @@ function updateHandle (win, updateUrl) {
url: updateUrl
})
autoUpdater.on('error', function (error) {
console.log('autoUpdater error', error)
log.info('autoUpdater error', error)
sendUpdateMessage({ key: 'error', value: error, error: error })
// dialog.showErrorBox('Error: ', error == null ? 'unknown' : (error.stack || error).toString())
})
autoUpdater.on('checking-for-update', function () {
console.log('autoUpdater checking-for-update')
log.info('autoUpdater checking-for-update')
sendUpdateMessage({ key: 'checking', value: message.checking })
})
autoUpdater.on('update-available', function (info) {
console.log('autoUpdater update-available')
log.info('autoUpdater update-available')
sendUpdateMessage({ key: 'available', value: info })
})
autoUpdater.on('update-not-available', function (info) {
console.log('autoUpdater update-not-available')
log.info('autoUpdater update-not-available')
sendUpdateMessage({ key: 'notAvailable', value: message.updateNotAva })
})
// 更新下载进度
autoUpdater.on('download-progress', function (progressObj) {
console.log('autoUpdater download-progress')
log.info('autoUpdater download-progress')
win.webContents.send('update', { key: 'progress', value: parseInt(progressObj.percent) })
})
// 更新完成,重启应用
autoUpdater.on('update-downloaded', function (info) {
console.log('download complete', info.version)
log.info('download complete', info.version)
win.webContents.send('update', {
key: 'downloaded',
value: {
@ -67,24 +68,31 @@ function updateHandle (win, updateUrl) {
ipcMain.on('update', (e, arg) => {
if (arg.key === 'doUpdateNow') {
// some code here to handle event
autoUpdater.quitAndInstall()
beforeQuit().then(() => {
autoUpdater.quitAndInstall()
if (app) {
setTimeout(() => {
app.exit()
}, 1000)
}
})
} else if (arg.key === 'checkForUpdate') {
// 执行自动更新检查
console.log('autoUpdater checkForUpdates')
log.info('autoUpdater checkForUpdates')
autoUpdater.checkForUpdates()
} else if (arg.key === 'downloadUpdate') {
// 下载新版本
console.log('autoUpdater downloadUpdate')
log.info('autoUpdater downloadUpdate')
autoUpdater.downloadUpdate()
}
})
// 通过main进程发送事件给renderer进程提示更新信息
function sendUpdateMessage (message) {
console.log('autoUpdater sendUpdateMessage')
log.info('autoUpdater sendUpdateMessage')
win.webContents.send('update', message)
}
console.log('auto update inited')
log.info('auto update inited')
return autoUpdater
}
export default updateHandle

View File

@ -0,0 +1,11 @@
const log4js = require('log4js')
const DevSidecar = require('@docmirror/dev-sidecar')
const getDefaultConfigBasePath = function () {
return DevSidecar.api.config.get().server.setting.userBasePath
}
log4js.configure({
appenders: { std: { type: 'stdout' }, file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename: getDefaultConfigBasePath() + '/logs/gui.log' } },
categories: { default: { appenders: ['file', 'std'], level: 'info' } }
})
const logger = log4js.getLogger('server')
module.exports = logger

View File

@ -17,13 +17,18 @@ const bindApi = (api, param1) => {
})
}
const apiObj = {
on (channel, callback) {
ipcRenderer.on(channel, callback)
},
invoke,
send,
openExternal (href) {
shell.openExternal(href)
ipc: {
on (channel, callback) {
ipcRenderer.on(channel, callback)
},
invoke,
send,
openExternal (href) {
shell.openExternal(href)
},
openPath (file) {
shell.openPath(file)
}
}
}
let inited = false

View File

@ -24,7 +24,6 @@
</template>
<script>
import api from '../api'
export default {
name: 'setup-ca',
components: {
@ -55,7 +54,6 @@ export default {
this.$emit('update:visible', false)
},
async doSetup () {
await api.shell.setupCa()
this.$emit('setup')
}
}

View File

@ -30,6 +30,7 @@ export default {
this.status = this.$status
return this.$api.config.reload().then(ret => {
this.config = ret
console.log('config', this.config)
if (this.ready) {
return this.ready(this.config)
}
@ -39,13 +40,14 @@ export default {
this.applyLoading = true
await this.applyBefore()
await this.saveConfig()
if (this.applyAfter) {
await this.applyAfter()
}
await this.applyAfter()
this.applyLoading = false
},
async applyBefore () {
},
async applyAfter () {
},
resetDefault () {
const key = this.getKey()

View File

@ -1,12 +1,12 @@
function install (app, api) {
api.on('error.core', (event, message) => {
api.ipc.on('error.core', (event, message) => {
console.error('view on error', message)
const key = message.key
if (key === 'server') {
handleServerStartError(message, message.error, app, api)
}
})
api.on('error', (event, message) => {
api.ipc.on('error', (event, message) => {
console.error('error', event, message)
})
}

View File

@ -1,20 +1,23 @@
let updateParams = { }
function install (app, api) {
api.on('update', (event, message) => {
const updateParams = app.$global.update = { fromUser: false, autoDownload: true, progress: 0, downloading: false, newVersion: false }
api.ipc.on('update', (event, message) => {
console.log('on message', event, message)
handleUpdateMessage(message, app)
})
api.update = {
checkForUpdate (params) {
updateParams = params || { fromUser: false, autoDownload: true, progress: 0 }
api.send('update', { key: 'checkForUpdate' })
checkForUpdate (fromUser) {
if (fromUser != null) {
updateParams.fromUser = fromUser
}
api.ipc.send('update', { key: 'checkForUpdate' })
},
downloadUpdate () {
api.send('update', { key: 'downloadUpdate' })
api.ipc.send('update', { key: 'downloadUpdate' })
},
doUpdateNow () {
api.send('update', { key: 'doUpdateNow' })
api.ipc.send('update', { key: 'doUpdateNow' })
}
}
@ -26,6 +29,8 @@ function install (app, api) {
noNewVersion()
} else if (type === 'downloaded') {
// 更新包已下载完成,让用户确认是否更新
updateParams.downloading = false
console.log('updateParams', updateParams)
newUpdateIsReady(message.value)
} else if (type === 'progress') {
progressUpdate(message.value)
@ -49,6 +54,8 @@ function install (app, api) {
updateParams.newVersion = true
if (updateParams.autoDownload !== false) {
app.$message.info('发现新版本,正在下载中...')
updateParams.downloading = true
api.update.downloadUpdate()
return
}
@ -69,6 +76,7 @@ function install (app, api) {
}
function newUpdateIsReady (value) {
updateParams.downloading = false
app.$confirm({
title: '新版本已准备好',
content: `是否立即升级安装v${value.version}?`,

View File

@ -8,9 +8,9 @@
<a-badge :count="_rootCaSetuped?0:1" dot>安装根证书 </a-badge>
</a-button>
<a-button style="margin-right:10px" @click="doCheckUpdate">
<a-button style="margin-right:10px" @click="doCheckUpdate(true)" :loading="update.downloading" :title="'当前版本:'+info.version">
<a-badge :count="update.newVersion?1:0" dot>
检查更新
<span v-if="update.downloading">{{update.progress}}%</span>{{update.downloading?'':''}}
</a-badge>
</a-button>
@ -31,7 +31,7 @@
<a-form style="margin-top:20px" :label-col="{ span: 12 }" :wrapper-col="{ span: 12 }">
<a-form-item v-for=" (item, key) in switchBtns" :key="key" :label="item.label">
<a-switch style="margin-left:10px" :loading="item.loading" v-model="item.status[key].enabled" default-checked v-on:click="item.doClick">
<a-switch style="margin-left:10px" :loading="item.loading" :checked="item.status()" default-checked @change="item.doClick">
<a-icon slot="checkedChildren" type="check"/>
<a-icon slot="unCheckedChildren" type="close"/>
</a-switch>
@ -45,7 +45,7 @@
<div class="flex-l-r star" style="padding:10px;">
<div>如果它解决了你的问题请不要吝啬你的star哟 <a-icon type="smile" theme="outlined" /></div>
<a @click="openExternal('https://gitee.com/docmirror/dev-sidecar')"><img src='https://gitee.com/docmirror/dev-sidecar/badge/star.svg?theme=dark' alt='star'/></a>
<a @click="openExternal('https://github.com/docmirror/dev-sidecar')"><img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github"></a>
<a @click="openExternal('https://github.com/docmirror/dev-sidecar')"><img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github"></a>
</div>
</div>
@ -57,7 +57,6 @@
import lodash from 'lodash'
import setupCa from '../components/setup-ca'
import DsContainer from '../components/container'
export default {
name: 'Index',
components: {
@ -88,6 +87,8 @@ export default {
}
}
},
info: {},
newVersionDownloading: false,
setting: undefined,
server: {
key: '代理服务',
@ -101,29 +102,23 @@ export default {
setupCa: {
visible: false
},
update: {}
update: { downloading: false, progress: 0, newVersion: false }
}
},
async created () {
this.doCheckRootCa()
console.log('index created', this.status, this.$status)
await this.reloadConfig()
const status = await this.$api.status.get()
console.log('status', status)
this.$set(this, 'status', status)
this.$set(this, 'status', this.$status)
this.switchBtns = this.createSwitchBtns()
console.log('switchBtns', this.switchBtns)
if (this.$global.update == null) {
this.$global.update = {
fromUser: false,
autoDownload: true,
progress: 0,
newVersion: false
}
this.update = this.$global.update
this.$set(this, 'update', this.$global.update)
if (!this.update.autoChecked) {
this.update.autoChecked = true
this.doCheckUpdate(false)
}
this.update = this.$global.update
this.$api.info.get().then(ret => {
this.info = ret
})
},
mounted () {
console.log('index mounted')
@ -156,7 +151,9 @@ export default {
openSetupCa () {
this.setupCa.visible = true
},
handleCaSetuped () {
async handleCaSetuped () {
console.log('this.config.server.setting.rootCaFile.certPath', this.config.server.setting.rootCaFile.certPath)
await this.$api.shell.setupCa({ certPath: this.config.server.setting.rootCaFile.certPath })
this.setting.rootCa = this.setting.rootCa || {}
const rootCa = this.setting.rootCa
rootCa.setuped = true
@ -185,7 +182,9 @@ export default {
loading: false,
key: key,
label: label,
status: statusParent,
status: () => {
return statusParent[key].enabled
},
doClick: (checked) => {
this.onSwitchClick(this.switchBtns[key], apiTarget.start, apiTarget.close, checked)
}
@ -229,11 +228,10 @@ export default {
})
},
doCheckUpdate (fromUser = true) {
this.update.fromUser = fromUser
this.$api.update.checkForUpdate(this.update)
this.$api.update.checkForUpdate(fromUser)
},
openExternal (url) {
this.$api.openExternal(url)
this.$api.ipc.openExternal(url)
}
}
}

View File

@ -16,7 +16,7 @@
>
<a-tab-pane tab="基本设置" key="1" >
<a-form-item label="代理服务:" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-checkbox :checked="config.server.enabled" @change="config.server.enabled = $event">
<a-checkbox v-model="config.server.enabled" >
随应用启动
</a-checkbox>
<a-tag v-if="status.proxy.enabled" color="green">
@ -25,16 +25,29 @@
<a-tag v-else color="red">
当前未启动
</a-tag>
<a-button class="md-mr-10" icon="profile" @click="openLog()"></a-button>
</a-form-item>
<a-form-item label="代理端口" :label-col="labelCol" :wrapper-col="wrapperCol" >
<a-input v-model="config.server.port"/>
</a-form-item>
<a-form-item label="校验SSL" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-checkbox :checked="config.server.setting.NODE_TLS_REJECT_UNAUTHORIZED" @change="config.server.setting.NODE_TLS_REJECT_UNAUTHORIZED = $event">
<a-checkbox v-model="config.server.setting.NODE_TLS_REJECT_UNAUTHORIZED">
NODE_TLS_REJECT_UNAUTHORIZED
</a-checkbox>
<div>开启此项之后被代理应用关闭SSL校验也问题不大了</div>
</a-form-item>
<a-form-item label="根证书:" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-input addon-before="Cert" addon-after="" v-model="config.server.setting.rootCaFile.certPath" ></a-input>
<a-input addon-before="Key" addon-after="" v-model="config.server.setting.rootCaFile.keyPath" ></a-input>
</a-form-item>
<a-form-item label="启用脚本" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-tooltip title="关闭后github的clone加速链接复制也将关闭">
<a-checkbox v-model="config.server.setting.script.enabled" >
允许插入并运行脚本
</a-checkbox>
</a-tooltip>
</a-form-item>
</a-tab-pane>
<a-tab-pane tab="拦截设置" key="2" >
<vue-json-editor style="height:100%;" ref="editor" v-model="config.server.intercepts" mode="code" :show-btns="false" :expandedOnStart="true" @json-change="onJsonChange" ></vue-json-editor>
@ -132,6 +145,10 @@ export default {
},
addDnsMapping () {
this.dnsMappings.unshift({ key: '', value: 'usa' })
},
async openLog () {
const dir = await this.$api.info.getConfigDir()
this.$api.ipc.openPath(dir + '/logs/server.log')
}
}
}

View File

@ -12,7 +12,7 @@ const status = {
}
}
async function install (api) {
api.on('status', (event, message) => {
api.ipc.on('status', (event, message) => {
console.log('view on status', event, message)
const value = message.value
const key = message.key

View File

@ -901,12 +901,12 @@
ajv "^6.12.0"
ajv-keywords "^3.4.1"
"@docmirror/dev-sidecar@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@docmirror/dev-sidecar/-/dev-sidecar-1.0.2.tgz#9cb617a5a604a375d6059cf4669d5b2f4bd731df"
integrity sha512-VOo2VYau6eAMbFT+Ymne7tMzrKh4hveNbXmCUmH8Zt5ynbGZXujXozzoVEYOVDXH5T0n+K7qsPh+Cw959LQi9w==
"@docmirror/dev-sidecar@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@docmirror/dev-sidecar/-/dev-sidecar-1.2.0.tgz#2320c31f786afd495b8e12557bea396cb96e0985"
integrity sha512-i96YlPjQtrrAgDW52YOpuVFmQDl1A0Nx3x6/riv2yKVN4nGqAugdJvcAuhNi7BDrJfdLyls2jI35uzN5+bpYfQ==
dependencies:
"@docmirror/mitmproxy" "1.0.2"
"@docmirror/mitmproxy" "^1.2.0"
agentkeepalive "^2.1.1"
charset "^1.0.0"
child_process "^1.0.2"
@ -920,6 +920,7 @@
jschardet "^1.4.1"
json5 "^2.1.3"
lodash "^4.7.0"
log4js "^6.3.0"
lru-cache "^6.0.0"
mkdirp "^0.5.1"
node-cmd "^3.0.0"
@ -932,18 +933,20 @@
validator "^13.1.17"
winreg "^1.2.4"
"@docmirror/mitmproxy@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@docmirror/mitmproxy/-/mitmproxy-1.0.2.tgz#ee25d4205dca58372aa7c9a34d2789d39dda3dc4"
integrity sha512-bDAIIp5aDQhgBnQfvtkYMzRVHIMAWFO9WtgWEBPLwLenXycU4vSMqt2c9Y/ArWlU4Ku+PQpjk2WZHYjmKzlmLA==
"@docmirror/mitmproxy@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@docmirror/mitmproxy/-/mitmproxy-1.2.0.tgz#ab9910031272d8e16fe0e38d434f7b42971bc74e"
integrity sha512-DlzquSNVZOC/p5GwAdDwSiI4KCb3HlUY4A44pGGOBehsNThBoRU8ESKJjbyay9ah4RPSAWTSVYgMEiPZctR+wg==
dependencies:
agentkeepalive "^2.1.1"
child_process "^1.0.2"
colors "^1.1.2"
commander "^2.9.0"
crypto-js "^4.0.0"
debug "^4.1.1"
dns-over-http "^0.2.0"
dns-over-tls "^0.0.8"
is-browser "^2.1.0"
json5 "^2.1.3"
lodash "^4.7.0"
log4js "^6.3.0"
@ -2019,8 +2022,8 @@
accept@^3.1.3:
version "3.1.3"
resolved "https://registry.npm.taobao.org/accept/download/accept-3.1.3.tgz#29c3e2b3a8f4eedbc2b690e472b9ebbdc7385e87"
integrity sha1-KcPis6j07tvCtpDkcrnrvcc4Xoc=
resolved "https://registry.yarnpkg.com/accept/-/accept-3.1.3.tgz#29c3e2b3a8f4eedbc2b690e472b9ebbdc7385e87"
integrity sha512-OgOEAidVEOKPup+Gv2+2wdH2AgVKI9LxsJ4hicdJ6cY0faUuZdZoi56kkXWlHp9qicN1nWQLmW5ZRGk+SBS5xg==
dependencies:
boom "7.x.x"
hoek "6.x.x"
@ -2067,7 +2070,7 @@ address@^1.1.2:
agentkeepalive@^2.1.1:
version "2.2.0"
resolved "https://registry.npm.taobao.org/agentkeepalive/download/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef"
integrity sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8=
aggregate-error@^3.0.0:
@ -2305,7 +2308,7 @@ arr-union@^3.1.0:
array-filter@^1.0.0:
version "1.0.0"
resolved "https://registry.npm.taobao.org/array-filter/download/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=
array-find@^1.0.0:
@ -2362,11 +2365,6 @@ array.prototype.flat@^1.2.3:
define-properties "^1.1.3"
es-abstract "^1.17.0-next.1"
asap@^2.0.0:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
asn1.js@^5.2.0:
version "5.4.1"
resolved "https://registry.npm.taobao.org/asn1.js/download/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
@ -2474,8 +2472,8 @@ autoprefixer@^9.8.6:
available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2:
version "1.0.2"
resolved "https://registry.npm.taobao.org/available-typed-arrays/download/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5"
integrity sha1-awmMqdgDkHnuP3f3t4PESAulE/U=
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5"
integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==
dependencies:
array-filter "^1.0.0"
@ -2671,8 +2669,8 @@ boolean@^3.0.1:
boom@7.x.x:
version "7.3.0"
resolved "https://registry.npm.taobao.org/boom/download/boom-7.3.0.tgz#733a6d956d33b0b1999da3fe6c12996950d017b9"
integrity sha1-czptlW0zsLGZnaP+bBKZaVDQF7k=
resolved "https://registry.yarnpkg.com/boom/-/boom-7.3.0.tgz#733a6d956d33b0b1999da3fe6c12996950d017b9"
integrity sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A==
dependencies:
hoek "6.x.x"
@ -3120,8 +3118,8 @@ chardet@^0.7.0:
charset@^1.0.0:
version "1.0.1"
resolved "https://registry.npm.taobao.org/charset/download/charset-1.0.1.tgz#8d59546c355be61049a8fa9164747793319852bd"
integrity sha1-jVlUbDVb5hBJqPqRZHR3kzGYUr0=
resolved "https://registry.yarnpkg.com/charset/-/charset-1.0.1.tgz#8d59546c355be61049a8fa9164747793319852bd"
integrity sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==
check-types@^8.0.3:
version "8.0.3"
@ -3130,7 +3128,7 @@ check-types@^8.0.3:
child_process@^1.0.2:
version "1.0.2"
resolved "https://registry.npm.taobao.org/child_process/download/child_process-1.0.2.tgz#b1f7e7fc73d25e7fd1d455adc94e143830182b5a"
resolved "https://registry.yarnpkg.com/child_process/-/child_process-1.0.2.tgz#b1f7e7fc73d25e7fd1d455adc94e143830182b5a"
integrity sha1-sffn/HPSXn/R1FWtyU4UODAYK1o=
"chokidar@>=2.0.0 <4.0.0", chokidar@^3.0.2, chokidar@^3.4.1:
@ -3401,8 +3399,8 @@ colorette@^1.2.1:
colors@^1.1.2:
version "1.4.0"
resolved "https://registry.npm.taobao.org/colors/download/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha1-xQSRR51MG9rtLJztMs98fcI2D3g=
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
@ -3418,8 +3416,8 @@ commander@2.17.x:
commander@^2.18.0, commander@^2.20.0, commander@^2.9.0:
version "2.20.3"
resolved "https://registry.npm.taobao.org/commander/download/commander-2.20.3.tgz?cache=0&sync_timestamp=1598576116597&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@~2.19.0:
version "2.19.0"
@ -3640,8 +3638,8 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.npm.taobao.org/cors/download/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha1-6sEdpRWS3Ya58G9uesKTs9+HXSk=
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
@ -3733,6 +3731,11 @@ crypto-browserify@^3.11.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
crypto-js@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.0.0.tgz#2904ab2677a9d042856a2ea2ef80de92e4a36dcc"
integrity sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==
crypto-random-string@^2.0.0:
version "2.0.0"
resolved "https://registry.npm.taobao.org/crypto-random-string/download/crypto-random-string-2.0.0.tgz?cache=0&sync_timestamp=1599139352103&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcrypto-random-string%2Fdownload%2Fcrypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
@ -3944,8 +3947,8 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
debug@^3.1.0, debug@^3.1.1, debug@^3.2.5:
version "3.2.6"
resolved "https://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz?cache=0&sync_timestamp=1600502894812&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha1-6D0X3hbYp++3cX7b5fsQE17uYps=
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
@ -4116,14 +4119,6 @@ detect-node@^2.0.4:
resolved "https://registry.npm.taobao.org/detect-node/download/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha1-AU7o+PZpxcWAI9pkuBecCDooxGw=
dezalgo@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=
dependencies:
asap "^2.0.0"
wrappy "1"
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.npm.taobao.org/diffie-hellman/download/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@ -4159,8 +4154,8 @@ dns-equal@^1.0.0:
dns-over-http@^0.2.0:
version "0.2.0"
resolved "https://registry.npm.taobao.org/dns-over-http/download/dns-over-http-0.2.0.tgz#1f2ead0752621668b67b8fbbfa85b10abd9f4768"
integrity sha1-Hy6tB1JiFmi2e4+7+oWxCr2fR2g=
resolved "https://registry.yarnpkg.com/dns-over-http/-/dns-over-http-0.2.0.tgz#1f2ead0752621668b67b8fbbfa85b10abd9f4768"
integrity sha512-K+SyN2L3ljxJ2MFtOv/vRS+3/YEMLvOuH7MrmO5ejaubi4w02/DLqzoK1kBGKlQrT9ND57pbapeDf+ue8AElEA==
dependencies:
accept "^3.1.3"
cors "^2.8.5"
@ -4174,8 +4169,8 @@ dns-over-http@^0.2.0:
dns-over-tls@^0.0.8:
version "0.0.8"
resolved "https://registry.npm.taobao.org/dns-over-tls/download/dns-over-tls-0.0.8.tgz#0ea3af54c0073e153a989a616c500e79f9f3ac0e"
integrity sha1-DqOvVMAHPhU6mJphbFAOefnzrA4=
resolved "https://registry.yarnpkg.com/dns-over-tls/-/dns-over-tls-0.0.8.tgz#0ea3af54c0073e153a989a616c500e79f9f3ac0e"
integrity sha512-2r1IBUEffIbhjuyOyKCsiMimXFCCEADY9Xfu9+p2NuUpSB6Si03+rPdLL9wxzkD6dI1d7gaJt+ekVIg3tW+ReQ==
dependencies:
dns-packet "^5.2.1"
@ -4189,23 +4184,23 @@ dns-packet@^1.3.1:
dns-packet@^4.1.0, dns-packet@^4.2.0:
version "4.2.0"
resolved "https://registry.npm.taobao.org/dns-packet/download/dns-packet-4.2.0.tgz#3fd6f5ff5a4ec3194ed0b15312693ffe8776b343"
integrity sha1-P9b1/1pOwxlO0LFTEmk//od2s0M=
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-4.2.0.tgz#3fd6f5ff5a4ec3194ed0b15312693ffe8776b343"
integrity sha512-bn1AKpfkFbm0MIioOMHZ5qJzl2uypdBwI4nYNsqvhjsegBhcKJUlCrMPWLx6JEezRjxZmxhtIz/FkBEur2l8Cw==
dependencies:
ip "^1.1.5"
safe-buffer "^5.1.1"
dns-packet@^5.2.1:
version "5.2.1"
resolved "https://registry.npm.taobao.org/dns-packet/download/dns-packet-5.2.1.tgz#26cec0be92252a1b97ed106482921192a7e08f72"
integrity sha1-Js7AvpIlKhuX7RBkgpIRkqfgj3I=
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.2.1.tgz#26cec0be92252a1b97ed106482921192a7e08f72"
integrity sha512-JHj2yJeKOqlxzeuYpN1d56GfhzivAxavNwHj9co3qptECel27B1rLY5PifJAvubsInX5pGLDjAHuCfCUc2Zv/w==
dependencies:
ip "^1.1.5"
dns-socket@^3.0.0:
version "3.0.0"
resolved "https://registry.npm.taobao.org/dns-socket/download/dns-socket-3.0.0.tgz#3418a3c6309656ebdb3eb0a941811a4fb0b828c5"
integrity sha1-NBijxjCWVuvbPrCpQYEaT7C4KMU=
resolved "https://registry.yarnpkg.com/dns-socket/-/dns-socket-3.0.0.tgz#3418a3c6309656ebdb3eb0a941811a4fb0b828c5"
integrity sha512-M0WkByoJ/mTm+HtwBQLsRJPe5uGIC/lYVOp+s6ZzhbZ5iq4GxjFyxYPQhB85dgCLvVb43aJQXHDC9aUgyKGc/Q==
dependencies:
dns-packet "^4.1.0"
@ -5055,8 +5050,8 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
extend@^3.0.1, extend@~3.0.2:
version "3.0.2"
resolved "https://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz?cache=0&sync_timestamp=1589682707348&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fextend%2Fdownload%2Fextend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
external-editor@^3.0.3:
version "3.1.0"
@ -5334,7 +5329,7 @@ for-in@^1.0.2:
foreach@^2.0.5:
version "2.0.5"
resolved "https://registry.npm.taobao.org/foreach/download/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
forever-agent@~0.6.1:
@ -5360,15 +5355,10 @@ form-data@~2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"
formidable@2.0.0-canary.20200504.1, formidable@^1.2.2:
version "2.0.0-canary.20200504.1"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.0.0-canary.20200504.1.tgz#8b060a43f77da82aa7ddc395e1bb4d845b65dc0b"
integrity sha512-//FlGY4V1wl5+Q54Gfx8FHErl0tDrRatftGFOgis2IeH8JFs4Ve/r+hubMRfk3gjSyI8VYLg8dWfa1DqfhMdCg==
dependencies:
dezalgo "1.0.3"
hexoid "1.0.0"
once "1.4.0"
qs "^6.9.3"
formidable@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9"
integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==
forwarded@~0.1.2:
version "0.1.2"
@ -5816,11 +5806,6 @@ hex-color-regex@^1.1.0:
resolved "https://registry.npm.taobao.org/hex-color-regex/download/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
integrity sha1-TAb8y0YC/iYCs8k9+C1+fb8aio4=
hexoid@1.0.0, hexoid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
highlight.js@^9.6.0:
version "9.18.3"
resolved "https://registry.npm.taobao.org/highlight.js/download/highlight.js-9.18.3.tgz#a1a0a2028d5e3149e2380f8a865ee8516703d634"
@ -5837,8 +5822,8 @@ hmac-drbg@^1.0.0:
hoek@6.x.x:
version "6.1.3"
resolved "https://registry.npm.taobao.org/hoek/download/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c"
integrity sha1-c7fTOVLgH+J6OLBFcpS3ndjaJCw=
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c"
integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==
hoopy@^0.1.4:
version "0.1.4"
@ -6033,8 +6018,8 @@ icon-gen@^2.0.0:
iconv-lite@0.4.24, iconv-lite@^0.4.13, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
@ -6292,8 +6277,8 @@ is-binary-path@~2.1.0:
is-browser@^2.1.0:
version "2.1.0"
resolved "https://registry.npm.taobao.org/is-browser/download/is-browser-2.1.0.tgz#fc084d59a5fced307d6708c59356bad7007371a9"
integrity sha1-/AhNWaX87TB9ZwjFk1a61wBzcak=
resolved "https://registry.yarnpkg.com/is-browser/-/is-browser-2.1.0.tgz#fc084d59a5fced307d6708c59356bad7007371a9"
integrity sha512-F5rTJxDQ2sW81fcfOR1GnCXT6sVJC104fCyfj+mjpwNEwaPYSn5fte5jiHmBg3DHsIoL/l8Kvw5VN5SsTRcRFQ==
is-buffer@^1.1.5:
version "1.1.6"
@ -6419,8 +6404,8 @@ is-function@^1.0.1:
is-generator-function@^1.0.7:
version "1.0.7"
resolved "https://registry.npm.taobao.org/is-generator-function/download/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522"
integrity sha1-0hMuUpuwAAp/gHlNS99c1eWBNSI=
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522"
integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==
is-glob@^3.1.0:
version "3.1.0"
@ -6574,8 +6559,8 @@ is-symbol@^1.0.2:
is-typed-array@^1.1.3:
version "1.1.3"
resolved "https://registry.npm.taobao.org/is-typed-array/download/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d"
integrity sha1-pP9aXmcuGlX5nH9U5ZWXr1wd8E0=
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d"
integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==
dependencies:
available-typed-arrays "^1.0.0"
es-abstract "^1.17.4"
@ -6732,8 +6717,8 @@ jsbn@~0.1.0:
jschardet@^1.4.1:
version "1.6.0"
resolved "https://registry.npm.taobao.org/jschardet/download/jschardet-1.6.0.tgz#c7d1a71edcff2839db2f9ec30fc5d5ebd3c1a678"
integrity sha1-x9GnHtz/KDnbL57DD8XV69PBpng=
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.6.0.tgz#c7d1a71edcff2839db2f9ec30fc5d5ebd3c1a678"
integrity sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ==
jsesc@^2.5.1:
version "2.5.2"
@ -7138,8 +7123,8 @@ lodash.uniq@^4.5.0:
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.3, lodash@^4.17.5, lodash@^4.7.0:
version "4.17.20"
resolved "https://registry.npm.taobao.org/lodash/download/lodash-4.17.20.tgz?cache=0&sync_timestamp=1597335994883&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha1-tEqbYpe8tpjxxRo1RaKzs2jVnFI=
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
log-symbols@^2.2.0:
version "2.2.0"
@ -7417,8 +7402,8 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
minimatch@^3.0.2, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
@ -7575,8 +7560,8 @@ nan@^2.12.1:
nanoid@^2.1.0:
version "2.1.11"
resolved "https://registry.npm.taobao.org/nanoid/download/nanoid-2.1.11.tgz?cache=0&sync_timestamp=1603419433040&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnanoid%2Fdownload%2Fnanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
integrity sha1-7CS4p1jVkVYVMbQXagHjq08PAoA=
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
nanomatch@^1.2.9:
version "1.2.13"
@ -7629,12 +7614,12 @@ no-case@^2.2.0:
node-cmd@^3.0.0:
version "3.0.0"
resolved "https://registry.npm.taobao.org/node-cmd/download/node-cmd-3.0.0.tgz#38fff70a4aaa4f659d203eb57862737018e24f6f"
resolved "https://registry.yarnpkg.com/node-cmd/-/node-cmd-3.0.0.tgz#38fff70a4aaa4f659d203eb57862737018e24f6f"
integrity sha1-OP/3CkqqT2WdID61eGJzcBjiT28=
node-dir@^0.1.17:
version "0.1.17"
resolved "https://registry.npm.taobao.org/node-dir/download/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5"
resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5"
integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU=
dependencies:
minimatch "^3.0.2"
@ -7653,8 +7638,8 @@ node-forge@^0.10.0:
node-forge@^0.8.2:
version "0.8.5"
resolved "https://registry.npm.taobao.org/node-forge/download/node-forge-0.8.5.tgz?cache=0&sync_timestamp=1599010773454&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-forge%2Fdownload%2Fnode-forge-0.8.5.tgz#57906f07614dc72762c84cef442f427c0e1b86ee"
integrity sha1-V5BvB2FNxydiyEzvRC9CfA4bhu4=
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.5.tgz#57906f07614dc72762c84cef442f427c0e1b86ee"
integrity sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q==
node-ipc@^9.1.1:
version "9.1.1"
@ -7696,8 +7681,8 @@ node-ipc@^9.1.1:
node-powershell@^4.0.0:
version "4.0.0"
resolved "https://registry.npm.taobao.org/node-powershell/download/node-powershell-4.0.0.tgz#f3a0b1ec4f5619b501b66005f8a663c8373e8da8"
integrity sha1-86Cx7E9WGbUBtmAF+KZjyDc+jag=
resolved "https://registry.yarnpkg.com/node-powershell/-/node-powershell-4.0.0.tgz#f3a0b1ec4f5619b501b66005f8a663c8373e8da8"
integrity sha512-WCZMLgwkjW9G/DZsZwyCEAXhMMzShLRUlnYS+EETRqRLSdUMbuO4xiQxIOeAutwQgvj75NvC58CorHFlx0olIA==
dependencies:
chalk "^2.4.1"
shortid "^2.2.14"
@ -7805,7 +7790,7 @@ oauth-sign@~0.9.0:
object-assign@4.x, object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-copy@^0.1.0:
@ -7911,7 +7896,7 @@ on-headers@~1.0.2:
resolved "https://registry.npm.taobao.org/on-headers/download/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
integrity sha1-dysK5qqlJcOZ5Imt+tkMQD6zwo8=
once@1.4.0, once@^1.3.0, once@^1.3.1, once@^1.4.0:
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
@ -8947,10 +8932,10 @@ qs@6.7.0:
resolved "https://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=
qs@^6.6.0, qs@^6.9.3, qs@^6.9.4:
qs@^6.6.0, qs@^6.9.4:
version "6.9.4"
resolved "https://registry.npm.taobao.org/qs/download/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
integrity sha1-kJCykNH5FyjTwi5UhDykSupatoc=
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==
qs@~6.5.2:
version "6.5.2"
@ -8989,7 +8974,7 @@ raf@^3.4.0:
random-int@^1.0.0:
version "1.0.0"
resolved "https://registry.npm.taobao.org/random-int/download/random-int-1.0.0.tgz#e6a2ed3448ac9c6646a0657443b1c1521592ed08"
resolved "https://registry.yarnpkg.com/random-int/-/random-int-1.0.0.tgz#e6a2ed3448ac9c6646a0657443b1c1521592ed08"
integrity sha1-5qLtNEisnGZGoGV0Q7HBUhWS7Qg=
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
@ -9284,8 +9269,8 @@ request@^2.81.0, request@^2.88.2:
require-context@^1.1.0:
version "1.1.0"
resolved "https://registry.npm.taobao.org/require-context/download/require-context-1.1.0.tgz#f77e60dddb0a296946e6915bf580c73dbe75ab4d"
integrity sha1-935g3dsKKWlG5pFb9YDHPb51q00=
resolved "https://registry.yarnpkg.com/require-context/-/require-context-1.1.0.tgz#f77e60dddb0a296946e6915bf580c73dbe75ab4d"
integrity sha512-nfYSy3Q9W3W1fCo0nief19bDq216IGY9+wOUsmCWAJ5jssyak0r110rvqIj4KJYoUYDxLDaeA66ONOYy4PJEUw==
dependencies:
node-dir "^0.1.17"
@ -9719,8 +9704,8 @@ shell-quote@^1.6.1:
shortid@^2.2.14:
version "2.2.16"
resolved "https://registry.npm.taobao.org/shortid/download/shortid-2.2.16.tgz#b742b8f0cb96406fd391c76bfc18a67a57fe5608"
integrity sha1-t0K48MuWQG/Tkcdr/Bimelf+Vgg=
resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.16.tgz#b742b8f0cb96406fd391c76bfc18a67a57fe5608"
integrity sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==
dependencies:
nanoid "^2.1.0"
@ -10419,8 +10404,8 @@ through2-map@^3.0.0:
through2@^2.0.0, through2@^2.0.1, through2@~2.0.0:
version "2.0.5"
resolved "https://registry.npm.taobao.org/through2/download/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
integrity sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
dependencies:
readable-stream "~2.3.6"
xtend "~4.0.1"
@ -10568,7 +10553,7 @@ tty-browserify@0.0.0:
tunnel-agent@^0.4.3:
version "0.4.3"
resolved "https://registry.npm.taobao.org/tunnel-agent/download/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
integrity sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=
tunnel-agent@^0.6.0:
@ -10887,8 +10872,8 @@ util@^0.11.0:
util@^0.12.3:
version "0.12.3"
resolved "https://registry.npm.taobao.org/util/download/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888"
integrity sha1-lxuwKS0swMiS2rfGpdN8K+xweIg=
resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888"
integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==
dependencies:
inherits "^2.0.3"
is-arguments "^1.0.4"
@ -10927,12 +10912,12 @@ validate-npm-package-license@^3.0.1:
validator@^13.1.17:
version "13.1.17"
resolved "https://registry.npm.taobao.org/validator/download/validator-13.1.17.tgz?cache=0&sync_timestamp=1600459985144&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvalidator%2Fdownload%2Fvalidator-13.1.17.tgz#ad677736950adddd3c37209484a6b2e0966579ad"
integrity sha1-rWd3NpUK3d08NyCUhKay4JZlea0=
resolved "https://registry.yarnpkg.com/validator/-/validator-13.1.17.tgz#ad677736950adddd3c37209484a6b2e0966579ad"
integrity sha512-zL5QBoemJ3jYFb2/j38y7ljhwYGXVLUp8H6W1nVxadnAOvUOytec+L7BHh1oBQ82/TzWXHd+GSaxUWp4lROkLg==
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
vendors@^1.0.0:
@ -11260,8 +11245,8 @@ which-module@^2.0.0:
which-typed-array@^1.1.2:
version "1.1.2"
resolved "https://registry.npm.taobao.org/which-typed-array/download/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2"
integrity sha1-5fmOVr2pPj2sGWsB1HwRVmecALI=
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2"
integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==
dependencies:
available-typed-arrays "^1.0.2"
es-abstract "^1.17.5"
@ -11293,7 +11278,7 @@ widest-line@^3.1.0:
winreg@^1.2.4:
version "1.2.4"
resolved "https://registry.npm.taobao.org/winreg/download/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b"
resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b"
integrity sha1-ugZWKbepJRMOFXeRCM9UCZDpjRs=
word-wrap@~1.2.3:

View File

@ -1,6 +1,6 @@
{
"name": "@docmirror/mitmproxy",
"version": "1.1.0",
"version": "1.2.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@docmirror/mitmproxy",
"version": "1.1.0",
"version": "1.2.1",
"description": "",
"main": "src/index.js",
"keywords": [
@ -15,6 +15,7 @@
"child_process": "^1.0.2",
"colors": "^1.1.2",
"commander": "^2.9.0",
"crypto-js": "^4.0.0",
"debug": "^4.1.1",
"dns-over-http": "^0.2.0",
"dns-over-tls": "^0.0.8",

View File

@ -1,40 +1,35 @@
const mitmproxy = require('./lib/proxy')
const ProxyOptions = require('./options')
const config = require('./lib/proxy/common/config')
function fireError (e) {
process.send({ type: 'error', event: e })
}
function fireStatus (status) {
process.send({ type: 'status', event: status })
}
const proxyConfig = require('./lib/proxy/common/config')
const log = require('./utils/util.log')
const { fireError, fireStatus } = require('./utils/util.process')
let server
function registerProcessListener () {
process.on('message', function (msg) {
console.log('child get msg: ' + JSON.stringify(msg))
log.info('child get msg: ' + JSON.stringify(msg))
if (msg.type === 'action') {
api[msg.event.key](msg.event.params)
}
})
process.on('SIGINT', () => {
console.log('on sigint : closed ')
log.info('on sigint : closed ')
process.exit(0)
})
// 避免异常崩溃
process.on('uncaughtException', function (err) {
if (err.code === 'ECONNABORTED') {
// console.error(err.errno)
// log.error(err.errno)
return
}
console.error('uncaughtException:', err)
log.error('uncaughtException:', err)
})
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason)
process.on('unhandledRejection', (err, p) => {
log.info('Unhandled Rejection at: Promise', p, 'err:', err)
// application specific logging, throwing an error, or other logic here
})
}
@ -42,6 +37,13 @@ function registerProcessListener () {
const api = {
async start (config) {
const proxyOptions = ProxyOptions(config)
const setting = config.setting
if (setting) {
if (setting.userBasePath) {
proxyConfig.setDefaultCABasePath(setting.userBasePath)
}
}
if (proxyOptions.setting && proxyOptions.setting.NODE_TLS_REJECT_UNAUTHORIZED === false) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
} else {
@ -49,17 +51,17 @@ const api = {
}
const newServer = mitmproxy.createProxy(proxyOptions, () => {
fireStatus(true)
console.log('代理服务已启动127.0.0.1:' + proxyOptions.port)
log.info('代理服务已启动127.0.0.1:' + proxyOptions.port)
})
newServer.on('close', () => {
console.log('server will closed ')
log.info('server will closed ')
if (server === newServer) {
server = null
fireStatus(false)
}
})
newServer.on('error', (e) => {
console.log('server error', e)
log.info('server error', e)
// newServer = null
fireError(e)
})
@ -72,20 +74,20 @@ const api = {
if (server) {
server.close((err) => {
if (err) {
console.log('close error', err, ',', err.code, ',', err.message, ',', err.errno)
log.info('close error', err, ',', err.code, ',', err.message, ',', err.errno)
if (err.code === 'ERR_SERVER_NOT_RUNNING') {
console.log('代理服务关闭成功')
log.info('代理服务关闭成功')
resolve()
return
}
reject(err)
} else {
console.log('代理服务关闭成功')
log.info('代理服务关闭成功')
resolve()
}
})
} else {
console.log('server is null')
log.info('server is null')
fireStatus(false)
resolve()
}
@ -95,5 +97,6 @@ const api = {
module.exports = {
...api,
config
config: proxyConfig,
log
}

View File

@ -0,0 +1,3 @@
const { ChoiceCache } = require('./index')
module.exports = new ChoiceCache()

View File

@ -0,0 +1,3 @@
const { ChoiceCache } = require('./index')
module.exports = new ChoiceCache()

View File

@ -0,0 +1,114 @@
const LRU = require('lru-cache')
const cacheSize = 1024
const log = require('../../utils/util.log')
class ChoiceCache {
constructor () {
this.cache = new LRU(cacheSize)
}
get (key) {
return this.cache.get(key)
}
getOrCreate (key, backups) {
let item = this.cache.get(key)
if (item == null) {
item = new DynamicChoice(key)
item.setBackupList(backups)
this.cache.set(key, item)
}
return item
}
}
class DynamicChoice {
constructor (key) {
this.key = key
this.count = {}
this.createTime = new Date()
}
/**
* 设置新的backup列表
* @param backupList
*/
setBackupList (backupList) {
this.backup = backupList
let defaultTotal = backupList.length
for (const item of backupList) {
if (this.count[item]) {
continue
}
this.count[item] = { value: item, total: defaultTotal, error: 0, keepErrorCount: 0, successRate: 1 }
defaultTotal--
}
this.value = backupList.shift()
this.doCount(this.value, false)
}
doRank () {
// 将count里面根据权重排序
const list = []
for (const key in this.count) {
list.push(this.count[key])
}
list.sort((a, b) => {
return b.successRate - a.successRate
})
log.info('do rank', JSON.stringify(list))
const backup = list.map(item => item.value)
this.setBackupList(backup)
}
countStart (value) {
this.doCount(value, false)
}
/**
* 换下一个
* @param count
*/
changeNext (count) {
count.keepErrorCount = 0 // 清空连续失败
if (this.backup > 0) {
this.value = this.backup.shift()
} else {
this.value = null
}
}
/**
* 记录使用次数或错误次数
* @param value
* @param isError
*/
doCount (value, isError) {
let count = this.count[value]
if (count == null) {
count = this.count[value] = { value: value, total: 0, error: 0, keepErrorCount: 0, successRate: 1 }
}
if (isError) {
count.error++
count.keepErrorCount++
} else {
count.total++
}
count.successRate = 1.0 - (count.error / count.total)
if (isError && this.value === value) {
// 连续错误4次切换下一个
if (count.keepErrorCount >= 4) {
this.changeNext(count)
}
// 成功率小于50%,切换下一个
if (count.successRate < 0.51) {
this.changeNext(count)
}
}
}
}
module.exports = {
DynamicChoice,
ChoiceCache
}

View File

@ -1,71 +1,26 @@
const LRU = require('lru-cache')
// const { isIP } = require('validator')
const getLogger = require('../utils/logger')
const logger = getLogger('dns')
const log = require('../../utils/util.log')
const { DynamicChoice } = require('../choice/index')
const cacheSize = 1024
// eslint-disable-next-line no-unused-vars
// function _isIP (v) {
// return v && isIP(v)
// }
class IpCache {
class IpCache extends DynamicChoice {
constructor (hostname) {
this.hostname = hostname
this.count = {}
super(hostname)
this.lookupCount = 0
this.createTime = new Date()
}
/**
* 获取到新的ipList
* @param ipList
*/
setIpList (ipList) {
this.ip = ipList.shift()
this.ipList = ipList
setBackupList (ipList) {
super.setBackupList(ipList)
this.lookupCount++
this.doCount(this.ip, false)
}
/**
* 换下一个ip
* @param count
*/
changeNext (count) {
count.keepErrorCount = 0 // 清空连续失败
if (this.ipList > 0) {
this.ip = this.ipList.shift()
} else {
this.ip = null
}
}
/**
* 记录使用次数或错误次数
* @param ip
* @param isError
*/
doCount (ip, isError) {
let count = this.count[ip]
if (count == null) {
count = this.count[ip] = { total: 0, error: 0, keepErrorCount: 0, successRate: 0 }
}
if (isError) {
count.error++
count.keepErrorCount++
} else {
count.total++
}
count.successRate = 1 - (count.error / count.total)
if (isError && this.ip === ip) {
if (count.keepErrorCount >= 5) {
this.changeNext(count)
}
if (count.successRate < 0.51) {
this.changeNext(count)
}
}
}
}
@ -85,9 +40,9 @@ module.exports = class BaseDNS {
try {
let ipCache = this.cache.get(hostname)
if (ipCache) {
if (ipCache.ip != null) {
ipCache.doCount(ipCache.ip, false)
return ipCache.ip
if (ipCache.value != null) {
ipCache.doCount(ipCache.value, false)
return ipCache.value
}
} else {
ipCache = new IpCache(hostname)
@ -102,13 +57,13 @@ module.exports = class BaseDNS {
}
ipList.push(hostname) // 把原域名加入到统计里去
ipCache.setIpList(ipList)
ipCache.setBackupList(ipList)
logger.debug(`[DNS] ${hostname} -> ${ipCache.ip} (${new Date() - t} ms)`)
log.info(`[DNS] ${hostname} -> ${ipCache.value} (${new Date() - t} ms)`)
return ipCache.ip
return ipCache.value
} catch (error) {
logger.debug(`[DNS] cannot resolve hostname ${hostname} (${error})`)
log.error(`[DNS] cannot resolve hostname ${hostname} (${error})`, error)
return hostname
}
}

View File

@ -1,7 +1,7 @@
const { promisify } = require('util')
const doh = require('dns-over-http')
const BaseDNS = require('./base')
const log = require('../../utils/util.log')
const dohQueryAsync = promisify(doh.query)
module.exports = class DNSOverHTTPS extends BaseDNS {
@ -15,18 +15,18 @@ module.exports = class DNSOverHTTPS extends BaseDNS {
const result = await dohQueryAsync({ url: this.dnsServer }, [{ type: 'A', name: hostname }])
if (result.answers.length === 0) {
// 说明没有获取到ip
console.log('该域名没有ip地址解析', hostname)
log.info('该域名没有ip地址解析', hostname)
return []
}
const ret = result.answers.filter(item => { return item.type === 'A' }).map(item => { return item.data })
if (ret.length === 0) {
console.log('该域名没有ipv4地址解析', hostname)
log.info('该域名没有ipv4地址解析', hostname)
} else {
console.log('获取到域名地址:', hostname, JSON.stringify(ret))
log.info('获取到域名地址:', hostname, JSON.stringify(ret))
}
return ret
} catch (err) {
console.log('dns query error', hostname, err)
log.info('dns query error', hostname, err.message)
return []
}
}

View File

@ -1,9 +1,11 @@
module.exports = {
requestInterceptor (interceptOpt, rOptions, req, res, ssl) {
console.log('abort:', rOptions.hostname, req.url)
requestIntercept (context, interceptOpts, req, res, ssl, next) {
const { rOptions, log } = context
log.info('abort:', rOptions.hostname, req.url)
res.writeHead(403)
res.write('DevSidecar 403: \n\n request abort, this request is matched by abort intercept.\n\n 因配置abort拦截器本请求将取消')
res.end()
return true// 是否结束
},
is (interceptOpt) {
return !!interceptOpt.abort

View File

@ -1,7 +1,41 @@
const url = require('url')
module.exports = {
requestInterceptor (interceptOpt, rOptions, req, res, ssl, next) {
const proxyTarget = interceptOpt.proxy
requestIntercept (context, interceptOpt, req, res, ssl, next) {
const { rOptions, log, RequestCounter } = context
let proxyConf = interceptOpt.proxy
if (RequestCounter && interceptOpt.backup) {
// 优选逻辑
const backup = [proxyConf]
if (interceptOpt.backup) {
for (const bk of interceptOpt.backup) {
backup.push(bk)
}
}
const key = interceptOpt.key
const count = RequestCounter.getOrCreate(key, backup)
if (count.value == null) {
count.doRank()
}
if (count.value == null) {
log.error('count value is null', count)
} else {
count.doCount(count.value)
proxyConf = count.value
context.requestCount = {
key,
value: count.value,
count
}
}
}
let proxyTarget = proxyConf + req.url
if (interceptOpt.replace) {
const regexp = new RegExp(interceptOpt.replace)
proxyTarget = req.url.replace(regexp, proxyConf)
}
// const backup = interceptOpt.backup
const proxy = proxyTarget.indexOf('http') === 0 ? proxyTarget : rOptions.protocol + '//' + proxyTarget
// eslint-disable-next-line node/no-deprecated-api
@ -10,11 +44,13 @@ module.exports = {
rOptions.hostname = URL.host
rOptions.host = URL.host
rOptions.headers.host = URL.host
rOptions.path = URL.path
if (URL.port == null) {
rOptions.port = rOptions.protocol === 'https:' ? 443 : 80
}
console.log('proxy:', rOptions.hostname, req.url, proxyTarget)
log.info('proxy:', rOptions.hostname, proxyTarget)
log.debug('proxy choice:', JSON.stringify(context.requestCount))
return true
},
is (interceptOpt) {
return !!interceptOpt.proxy

View File

@ -1,5 +1,6 @@
module.exports = {
requestInterceptor (interceptOpt, rOptions, req, res, ssl) {
requestIntercept (context, interceptOpt, req, res, ssl, next) {
const { rOptions, log } = context
const url = req.url
let redirect
if (typeof interceptOpt.redirect === 'string') {
@ -7,10 +8,10 @@ module.exports = {
} else {
redirect = interceptOpt.redirect(url)
}
console.log('请求重定向:', rOptions.hostname, url, redirect)
log.info('请求重定向:', rOptions.hostname, url, redirect)
res.writeHead(302, { Location: redirect })
res.end()
return true
return true// 是否结束
},
is (interceptOpt) {
return interceptOpt.redirect // 如果配置中有redirect那么这个配置是需要redirect拦截的

View File

@ -0,0 +1,43 @@
const contextPath = '/____ds_script____/'
const monkey = require('../../monkey')
const CryptoJs = require('crypto-js')
function getScript (key, script) {
const scriptUrl = contextPath + key
const hash = CryptoJs.SHA256(script).toString(CryptoJs.enc.Base64)
return `
<script crossorigin="anonymous" defer="defer" type="application/javascript"
integrity="sha256-${hash}"
src="${scriptUrl}"></script>
`
}
module.exports = {
responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
const { rOptions, log, setting } = context
let keys = interceptOpt.script
if (typeof keys === 'string') {
keys = [keys]
}
try {
let tags = getScript('global', monkey.get(setting.script.defaultDir).global.script)
for (const key of keys) {
const script = monkey.get(setting.script.defaultDir)[key]
if (script == null) {
continue
}
const scriptTag = getScript(key, script.script)
tags += '\r\n' + scriptTag
}
log.info('responseIntercept: insert script', rOptions.hostname, rOptions.path)
return {
head: tags
}
} catch (err) {
log.error('load monkey script error', err)
}
},
is (interceptOpt) {
return interceptOpt.script
}
}

View File

@ -0,0 +1,13 @@
module.exports = {
requestIntercept (context, interceptOpts, req, res, ssl, next) {
const { rOptions, log } = context
log.info('success:', rOptions.hostname, req.url)
res.writeHead(200)
res.write('DevSidecar 200: \n\n request success, this request is matched by success intercept.\n\n 因配置success拦截器本请求将直接返回成功')
res.end()
return true// 是否结束
},
is (interceptOpt) {
return !!interceptOpt.success
}
}

View File

@ -1,7 +1,8 @@
const proxy = require('./impl/proxy')
const redirect = require('./impl/redirect')
const abort = require('./impl/abort')
const modules = [proxy, redirect, abort]
const success = require('./impl/success')
const script = require('./impl/script')
const modules = [proxy, redirect, abort, script, success]
module.exports = modules

View File

@ -0,0 +1,80 @@
const fs = require('fs')
const path = require('path')
const log = require('../../utils/util.log')
let scripts
function buildScript (sc, content) {
const grant = sc.grant
const pre = 'window.addEventListener("load",' +
' ()=> { \r\n'
let grantSc = ''
for (const item of grant) {
grantSc += 'const ' + item + ' = window.__ds_global__[\'' + item + '\']\r\n'
}
const tail = ';' + content + '\r\n' +
'})'
return pre + grantSc + tail
}
function loadScript (content) {
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_openInTab
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_notification
const annoFlag = '// ==/UserScript=='
const arr = content.split(annoFlag)
const start = 0
const confStr = arr[start]
const confItemArr = confStr.split('\n')
const sc = {
grant: [],
match: [],
content: ''
}
for (const string of confItemArr) {
const reg = new RegExp('.*@([^\\s]+)\\s(.+)')
const ret = string.match(reg)
if (ret) {
const key = ret[1].trim()
const value = ret[2].trim()
if (key === 'grant') {
sc.grant.push(value)
} else if (key === 'match') {
sc.match.push(value)
} else {
sc[key] = value
}
}
}
const script = arr[start + 1].trim()
sc.script = buildScript(sc, script)
return sc
}
function readFile (rootDir, script) {
const location = path.join(rootDir, './' + script)
log.debug('script location:', location)
return fs.readFileSync(location).toString()
}
const api = {
get (rootDir) {
if (scripts == null) {
api.load(rootDir)
}
return scripts
},
load (rootDir) {
scripts = {}
scripts.github = loadScript(readFile(rootDir, 'github.script'))
scripts.jquery = { script: readFile(rootDir, 'jquery.min.js') }
scripts.global = { script: readFile(rootDir, 'global.script') }
return scripts
}
}
module.exports = api

View File

@ -1,32 +0,0 @@
#!/usr/bin/env node
require('babel-polyfill')
const mitmproxy = require('../mitmproxy')
const program = require('commander')
const packageJson = require('../../package.json')
// const tlsUtils = require('../tls/tlsUtils')
const fs = require('fs')
const path = require('path')
const colors = require('colors')
fs.existsSync = fs.existsSync || path.existsSync
program
.version(packageJson.version)
.option('-c, --config [value]', 'config file path')
.parse(process.argv)
console.log(program.config)
const configPath = path.resolve(program.config)
if (fs.existsSync(configPath)) {
const configObject = require(configPath)
if (typeof configObject !== 'object') {
console.error(colors.red(`Config Error in ${configPath}`))
} else {
mitmproxy.createProxy(configObject)
}
} else {
console.error(colors.red(`Can not find \`config file\` file: ${configPath}`))
}

View File

@ -1,5 +1,4 @@
const path = require('path')
const config = exports
config.caCertFileName = 'dev-sidecar.ca.crt'
@ -8,10 +7,18 @@ config.caKeyFileName = 'dev-sidecar.ca.key.pem'
config.defaultPort = 1181
config.caName = 'DevSidecar CA'
config.caName = 'This certificate is generated locally'
config.caBasePath = buildDefaultCABasePath()
config.getDefaultCABasePath = function () {
const userHome = process.env.HOME || process.env.USERPROFILE
return config.caBasePath
}
config.setDefaultCABasePath = function (path) {
config.caBasePath = path
}
function buildDefaultCABasePath () {
const userHome = process.env.USERPROFILE
return path.resolve(userHome, './.dev-sidecar')
}

View File

@ -2,7 +2,7 @@ const url = require('url')
const Agent = require('./ProxyHttpAgent')
const HttpsAgent = require('./ProxyHttpsAgent')
const tunnelAgent = require('tunnel-agent')
const log = require('../../../utils/util.log')
const util = exports
const httpsAgent = new HttpsAgent({
keepAlive: true,
@ -34,7 +34,7 @@ util.getOptionsFormRequest = (req, ssl, externalProxy = null) => {
try {
externalProxyUrl = externalProxy(req, ssl)
} catch (e) {
console.error('externalProxy', e)
log.error('externalProxy', e)
}
}
}

View File

@ -1,98 +0,0 @@
const through = require('through2')
const zlib = require('zlib')
// eslint-disable-next-line no-unused-vars
const url = require('url')
const httpUtil = {}
httpUtil.isGzip = function (res) {
const contentEncoding = res.headers['content-encoding']
return !!(contentEncoding && contentEncoding.toLowerCase() === 'gzip')
}
httpUtil.isHtml = function (res) {
const contentType = res.headers['content-type']
return (typeof contentType !== 'undefined') && /text\/html|application\/xhtml\+xml/.test(contentType)
}
// eslint-disable-next-line no-unused-vars
function injectContentIntoHtmlHead (html, content) {
html = html.replace(/(<\/head>)/i, function (match) {
return content + match
})
return html
}
function injectScriptIntoHtmlHead (html, content) {
return html
}
function injectContentIntoHtmlBody (html, content) {
html = html.replace(/(<\/body>)/i, function (match) {
return content + match
})
return html
}
function chunkReplace (_this, chunk, enc, callback, headContent, bodyContent) {
let chunkString = chunk.toString()
if (headContent) {
chunkString = injectScriptIntoHtmlHead(chunkString, headContent)
}
if (bodyContent) {
chunkString = injectContentIntoHtmlBody(chunkString, bodyContent)
}
_this.push(Buffer.alloc(chunkString))
callback()
}
module.exports = class InjectHtmlPlugin {
constructor ({
head,
body
}) {
this.head = head
this.body = body
}
responseInterceptor (req, res, proxyReq, proxyRes, ssl, next) {
if (!this.head && !this.body) {
next()
return
}
const isHtml = httpUtil.isHtml(proxyRes)
const contentLengthIsZero = (() => {
return proxyRes.headers['content-length'] === 0
})()
if (!isHtml || contentLengthIsZero) {
next()
} else {
Object.keys(proxyRes.headers).forEach(function (key) {
if (proxyRes.headers[key] !== undefined) {
let newkey = key.replace(/^[a-z]|-[a-z]/g, (match) => {
return match.toUpperCase()
})
newkey = key
if (isHtml && key === 'content-length') {
// do nothing
} else {
res.setHeader(newkey, proxyRes.headers[key])
}
}
})
res.writeHead(proxyRes.statusCode)
const isGzip = httpUtil.isGzip(proxyRes)
if (isGzip) {
proxyRes.pipe(new zlib.Gunzip())
.pipe(through(function (chunk, enc, callback) {
chunkReplace(this, chunk, enc, callback, this.head, this.body)
})).pipe(new zlib.Gzip()).pipe(res)
} else {
proxyRes.pipe(through(function (chunk, enc, callback) {
chunkReplace(this, chunk, enc, callback, this.head, this.body)
})).pipe(res)
}
}
next()
}
}

View File

@ -0,0 +1,140 @@
const through = require('through2')
const zlib = require('zlib')
// eslint-disable-next-line no-unused-vars
const url = require('url')
var httpUtil = {}
httpUtil.getCharset = function (res) {
const contentType = res.getHeader('content-type')
const reg = /charset=(.*)/
const matched = contentType.match(reg)
if (matched) {
return matched[1]
}
return 'utf-8'
}
httpUtil.isGzip = function (res) {
var contentEncoding = res.headers['content-encoding']
return !!(contentEncoding && contentEncoding.toLowerCase() === 'gzip')
}
httpUtil.isHtml = function (res) {
var contentType = res.headers['content-type']
return (typeof contentType !== 'undefined') && /text\/html|application\/xhtml\+xml/.test(contentType)
}
const HEAD = Buffer.from('</head>')
const HEAD_UP = Buffer.from('</HEAD>')
const BODY = Buffer.from('</body>')
const BODY_UP = Buffer.from('</BODY>')
function chunkByteReplace (_this, chunk, enc, callback, append) {
if (append && append.head) {
const ret = injectScriptIntoHtml([HEAD, HEAD_UP], chunk, append.head)
if (ret != null) {
chunk = ret
}
}
if (append && append.body) {
const ret = injectScriptIntoHtml([BODY, BODY_UP], chunk, append.body)
if (ret != null) {
chunk = ret
}
}
_this.push(chunk)
callback()
}
function injectScriptIntoHtml (tags, chunk, script) {
for (const tag of tags) {
const index = chunk.indexOf(tag)
if (index < 0) {
continue
}
const scriptBuf = Buffer.from(script)
const chunkNew = Buffer.alloc(chunk.length + scriptBuf.length)
chunk.copy(chunkNew, 0, 0, index)
scriptBuf.copy(chunkNew, index, 0)
chunk.copy(chunkNew, index + scriptBuf.length, index)
return chunkNew
}
return null
}
const contextPath = '/____ds_script____/'
const monkey = require('../../monkey')
module.exports = {
requestIntercept (context, req, res, ssl, next) {
const { rOptions, log, setting } = context
if (rOptions.path.indexOf(contextPath) !== 0) {
return
}
const urlPath = rOptions.path
const filename = urlPath.replace(contextPath, '')
const script = monkey.get(setting.script.defaultDir)[filename]
log.info('ds_script', filename, script != null)
res.writeHead(200)
res.write(script.script)
res.end()
return true
},
responseInterceptor (req, res, proxyReq, proxyRes, ssl, next, append) {
if (!append.head && !append.body) {
next()
return
}
const isHtml = httpUtil.isHtml(proxyRes)
const contentLengthIsZero = (() => {
return proxyRes.headers['content-length'] === 0
})()
if (!isHtml || contentLengthIsZero) {
next()
return
} else {
Object.keys(proxyRes.headers).forEach(function (key) {
if (proxyRes.headers[key] !== undefined) {
// let newkey = key.replace(/^[a-z]|-[a-z]/g, (match) => {
// return match.toUpperCase()
// })
const newkey = key
if (isHtml && key === 'content-length') {
// do nothing
return
}
if (isHtml && key === 'content-security-policy') {
// content-security-policy
let policy = proxyRes.headers[key]
const reg = /script-src ([^:]*);/i
const matched = policy.match(reg)
if (matched) {
if (matched[1].indexOf('self') < 0) {
policy = policy.replace('script-src', 'script-src \'self\' ')
}
}
res.setHeader(newkey, policy)
return
}
res.setHeader(newkey, proxyRes.headers[key])
}
})
res.writeHead(proxyRes.statusCode)
const isGzip = httpUtil.isGzip(proxyRes)
if (isGzip) {
proxyRes.pipe(new zlib.Gunzip())
.pipe(through(function (chunk, enc, callback) {
chunkByteReplace(this, chunk, enc, callback, append)
})).pipe(new zlib.Gzip()).pipe(res)
} else {
proxyRes.pipe(through(function (chunk, enc, callback) {
chunkByteReplace(this, chunk, enc, callback, append)
})).pipe(res)
}
}
next()
}
}

View File

@ -1,5 +1,6 @@
const net = require('net')
const url = require('url')
const log = require('../../../utils/util.log')
// const colors = require('colors')
const DnsUtil = require('../../dns/index')
const localIP = '127.0.0.1'
@ -15,7 +16,7 @@ module.exports = function createConnectHandler (sslConnectInterceptor, fakeServe
fakeServerCenter.getServerPromise(hostname, srvUrl.port).then((serverObj) => {
connect(req, cltSocket, head, localIP, serverObj.port)
}, (e) => {
console.error('getServerPromise', e)
log.error('getServerPromise', e)
})
} else {
connect(req, cltSocket, head, hostname, srvUrl.port, dnsConfig)
@ -25,7 +26,7 @@ module.exports = function createConnectHandler (sslConnectInterceptor, fakeServe
function connect (req, cltSocket, head, hostname, port, dnsConfig) {
// tunneling https
// console.log('connect:', hostname, port)
// log.info('connect:', hostname, port)
const start = new Date().getTime()
let isDnsIntercept = null
try {
@ -60,23 +61,26 @@ function connect (req, cltSocket, head, hostname, port, dnsConfig) {
cltSocket.pipe(proxySocket)
})
cltSocket.on('error', (e) => {
log.error('cltSocket error', e.message)
})
proxySocket.on('timeout', () => {
const end = new Date().getTime()
console.log('代理socket timeout', hostname, port, (end - start) + 'ms')
log.info('代理socket timeout', hostname, port, (end - start) + 'ms')
})
proxySocket.on('error', (e) => {
// 连接失败可能被GFW拦截或者服务端拥挤
const end = new Date().getTime()
console.error('代理连接失败:', e.message, hostname, port, (end - start) + 'ms')
log.error('代理连接失败:', e.message, hostname, port, (end - start) + 'ms')
cltSocket.destroy()
if (isDnsIntercept) {
const { dns, ip, hostname } = isDnsIntercept
dns.count(hostname, ip, true)
console.error('记录ip失败次数,用于优选ip', hostname, ip)
log.error('记录ip失败次数,用于优选ip', hostname, ip)
}
})
return proxySocket
} catch (error) {
console.log('connect err', error)
log.error('connect err', error)
}
}

View File

@ -2,7 +2,7 @@ const fs = require('fs')
const forge = require('node-forge')
const FakeServersCenter = require('../tls/FakeServersCenter')
const colors = require('colors')
const log = require('../../../utils/util.log')
module.exports = function createFakeServerCenter ({
caCertPath,
caKeyPath,
@ -20,7 +20,7 @@ module.exports = function createFakeServerCenter ({
caCert = forge.pki.certificateFromPem(caCertPem)
caKey = forge.pki.privateKeyFromPem(caKeyPem)
} catch (e) {
console.log(colors.red('Can not find `CA certificate` or `CA key`.'), e)
log.info(colors.red('Can not find `CA certificate` or `CA key`.'), e)
process.exit(1)
}

View File

@ -3,8 +3,13 @@ const https = require('https')
const commonUtil = require('../common/util')
// const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i
const DnsUtil = require('../../dns/index')
module.exports = function createRequestHandler (requestInterceptor, responseInterceptor, middlewares, externalProxy, dnsConfig) {
const log = require('../../../utils/util.log')
const RequestCounter = require('../../choice/RequestCounter')
const InsertScriptMiddleware = require('../middleware/InsertScriptMiddleware')
const defaultDns = require('dns')
const MAX_SLOW_TIME = 8000 // 超过此时间 则认为太慢了
// create requestHandler function
module.exports = function createRequestHandler (createIntercepts, externalProxy, dnsConfig, setting) {
// return
return function requestHandler (req, res, ssl) {
let proxyReq
@ -17,7 +22,18 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
} else {
req.socket.setKeepAlive(true, 30000)
}
const context = {}
const context = {
rOptions,
log,
RequestCounter,
setting
}
let interceptors = createIntercepts(context)
if (interceptors == null) {
interceptors = []
}
const reqIncpts = interceptors.filter(item => { return item.requestIntercept != null })
const resIncpts = interceptors.filter(item => { return item.responseIntercept != null })
const requestInterceptorPromise = () => {
return new Promise((resolve, reject) => {
@ -25,10 +41,20 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
resolve()
}
try {
if (typeof requestInterceptor === 'function') {
requestInterceptor(rOptions, req, res, ssl, next, context)
if (setting.script.enabled) {
reqIncpts.unshift(InsertScriptMiddleware)
}
if (reqIncpts && reqIncpts.length > 0) {
for (const reqIncpt of reqIncpts) {
const goNext = reqIncpt.requestIntercept(context, req, res, ssl, next)
if (goNext) {
next()
return
}
}
next()
} else {
resolve()
next()
}
} catch (e) {
reject(e)
@ -36,17 +62,21 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
})
}
function countSlow (isDnsIntercept, type) {
if (isDnsIntercept) {
const { dns, ip, hostname } = isDnsIntercept
dns.count(hostname, ip, true)
log.error('记录ip失败次数,用于优选ip', hostname, ip, type)
}
const counter = context.requestCount
if (counter != null) {
counter.count.doCount(counter.value, true)
log.error('记录proxy失败次数', counter.value, type)
}
}
const proxyRequestPromise = async () => {
rOptions.host = rOptions.hostname || rOptions.host || 'localhost'
// if (dnsConfig) {
// const dns = DnsUtil.hasDnsLookup(dnsConfig, rOptions.host)
// if (dns) {
// const ip = await dns.lookup(rOptions.host)
// console.log('使用自定义dns:', rOptions.host, ip, dns.dnsServer)
// rOptions.host = ip
// }
// }
return new Promise((resolve, reject) => {
// use the binded socket for NTLM
if (rOptions.agent && rOptions.customSocketId != null && rOptions.agent.getName) {
@ -62,7 +92,7 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
function onFree () {
const url = `${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${rOptions.path}`
const start = new Date().getTime()
console.log('代理请求:', url, rOptions.method)
log.info('代理请求:', url, rOptions.method)
let isDnsIntercept
if (dnsConfig) {
const dns = DnsUtil.hasDnsLookup(dnsConfig, rOptions.hostname)
@ -73,7 +103,7 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
if (ip !== hostname) {
callback(null, ip, 4)
} else {
rOptions.lookup(hostname, options, callback)
defaultDns.lookup(hostname, options, callback)
}
})
}
@ -82,50 +112,65 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
proxyReq = (rOptions.protocol === 'https:' ? https : http).request(rOptions, (proxyRes) => {
const end = new Date().getTime()
const cost = end - start
if (rOptions.protocol === 'https:') {
console.log('代理请求返回:', url, (end - start) + 'ms')
log.info('代理请求返回:', url, cost + 'ms')
}
if (cost > MAX_SLOW_TIME) {
countSlow(isDnsIntercept, 'to slow ' + cost + 'ms')
}
resolve(proxyRes)
})
proxyReq.on('timeout', () => {
const end = new Date().getTime()
if (isDnsIntercept) {
const { dns, ip, hostname } = isDnsIntercept
dns.count(hostname, ip, true)
console.error('记录ip失败次数,用于优选ip', hostname, ip)
}
console.error('代理请求超时', rOptions.protocol, rOptions.hostname, rOptions.path, (end - start) + 'ms')
reject(new Error(`${rOptions.host}:${rOptions.port}, 代理请求超时`))
const cost = end - start
log.error('代理请求超时', rOptions.protocol, rOptions.hostname, rOptions.path, cost + 'ms')
countSlow(isDnsIntercept, 'to slow ' + cost + 'ms')
proxyReq.end()
proxyReq.destroy()
const error = new Error(`${rOptions.host}:${rOptions.port}, 代理请求超时`)
error.status = 408
reject(error)
})
proxyReq.on('error', (e) => {
const end = new Date().getTime()
if (isDnsIntercept) {
const { dns, ip, hostname } = isDnsIntercept
dns.count(hostname, ip, true)
console.error('记录ip失败次数,用于优选ip', hostname, ip)
}
console.error('代理请求错误', e.errno, rOptions.hostname, rOptions.path, (end - start) + 'ms', e)
const cost = end - start
log.error('代理请求错误', e.code, e.message, rOptions.hostname, rOptions.path, cost + 'ms')
countSlow(isDnsIntercept, 'error:' + e.message)
reject(e)
})
proxyReq.on('aborted', () => {
console.error('代理请求被取消', rOptions.hostname, rOptions.path)
const end = new Date().getTime()
const cost = end - start
log.error('代理请求被取消', rOptions.hostname, rOptions.path, cost + 'ms')
if (cost > MAX_SLOW_TIME) {
countSlow(isDnsIntercept, 'to slow ' + cost + 'ms')
}
if (res.writableEnded) {
return
}
reject(new Error('代理请求被取消'))
})
req.on('aborted', function () {
console.error('请求被取消', rOptions.hostname, rOptions.path)
log.error('请求被取消', rOptions.hostname, rOptions.path)
proxyReq.abort()
if (res.writableEnded) {
return
}
reject(new Error('请求被取消'))
})
req.on('error', function (e, req, res) {
console.error('请求错误:', e.errno, rOptions.hostname, rOptions.path)
log.error('请求错误:', e.errno, rOptions.hostname, rOptions.path)
reject(e)
})
req.on('timeout', () => {
console.error('请求超时', rOptions.hostname, rOptions.path)
log.error('请求超时', rOptions.hostname, rOptions.path)
reject(new Error(`${rOptions.hostname}:${rOptions.port}, 请求超时`))
})
req.pipe(proxyReq)
@ -137,21 +182,45 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
(async () => {
await requestInterceptorPromise()
if (res.finished) {
if (res.writableEnded) {
return false
}
const proxyRes = await proxyRequestPromise()
// proxyRes.on('data', (chunk) => {
// // console.log('BODY: ')
// })
proxyRes.on('error', (error) => {
countSlow(null, 'error:' + error.message)
log.error('proxy res error', error)
})
const responseInterceptorPromise = new Promise((resolve, reject) => {
const next = () => {
resolve()
}
if (!setting.script.enabled) {
next()
return
}
try {
if (typeof responseInterceptor === 'function') {
responseInterceptor(req, res, proxyReq, proxyRes, ssl, next, context)
if (resIncpts && resIncpts.length > 0) {
let head = ''
let body = ''
for (const resIncpt of resIncpts) {
const append = resIncpt.responseIntercept(context, req, res, proxyReq, proxyRes, ssl)
if (append && append.head) {
head += append.head
}
if (append && append.body) {
body += append.body
}
}
InsertScriptMiddleware.responseInterceptor(req, res, proxyReq, proxyRes, ssl, next, { head, body })
} else {
resolve()
next()
}
} catch (e) {
reject(e)
@ -160,10 +229,6 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
await responseInterceptorPromise
if (res.finished) {
return false
}
if (!res.headersSent) { // prevent duplicate set headers
Object.keys(proxyRes.headers).forEach(function (key) {
if (proxyRes.headers[key] !== undefined) {
@ -178,21 +243,20 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
}
})
if (proxyRes.statusCode >= 400) {
countSlow(null, 'status return :' + proxyRes.statusCode)
}
res.writeHead(proxyRes.statusCode)
proxyRes.pipe(res)
}
})().then(
(flag) => {
// do nothing
},
(e) => {
if (!res.finished) {
res.writeHead(500)
res.write(`DevSidecar Warning:\n\n ${e.toString()}`)
res.end()
}
console.error('request error', e.message)
})().catch(e => {
if (!res.writableEnded) {
const status = e.status || 500
res.writeHead(status)
res.write(`DevSidecar Warning:\n\n ${e.toString()}`)
res.end()
log.error('request error', e.message)
}
)
})
}
}

View File

@ -1,7 +1,7 @@
const http = require('http')
const https = require('https')
const util = require('../common/util')
const log = require('../../../utils/util.log')
// copy from node-http-proxy. ^_^
// create connectHandler function
@ -11,7 +11,7 @@ module.exports = function createUpgradeHandler () {
const clientOptions = util.getOptionsFormRequest(req, ssl)
const proxyReq = (ssl ? https : http).request(clientOptions)
proxyReq.on('error', (e) => {
console.error(e)
log.error('upgradeHandler', e)
})
proxyReq.on('response', function (res) {
// if upgrade event isn't going to happen, close the socket
@ -20,12 +20,11 @@ module.exports = function createUpgradeHandler () {
proxyReq.on('upgrade', function (proxyRes, proxySocket, proxyHead) {
proxySocket.on('error', (e) => {
console.log('error-----1111')
console.error(e)
log.error('on upgrade:', e)
})
cltSocket.on('error', function () {
console.log('error-----2222')
cltSocket.on('error', function (e) {
log.error('upgrade socket ', e)
proxySocket.end()
})

View File

@ -1,7 +1,7 @@
const tlsUtils = require('../tls/tlsUtils')
const http = require('http')
const config = require('../common/config')
const colors = require('colors')
const log = require('../../../utils/util.log')
const createRequestHandler = require('./createRequestHandler')
const createConnectHandler = require('./createConnectHandler')
const createFakeServerCenter = require('./createFakeServerCenter')
@ -12,33 +12,35 @@ module.exports = {
caCertPath,
caKeyPath,
sslConnectInterceptor,
requestInterceptor,
responseInterceptor,
createIntercepts,
getCertSocketTimeout = 1 * 1000,
middlewares = [],
externalProxy,
dnsConfig
dnsConfig,
setting
}, callback) {
// Don't reject unauthorized
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
if (!caCertPath && !caKeyPath) {
const rs = this.createCA()
caCertPath = rs.caCertPath
caKeyPath = rs.caKeyPath
if (rs.create) {
console.log(colors.cyan(`CA Cert saved in: ${caCertPath}`))
console.log(colors.cyan(`CA private key saved in: ${caKeyPath}`))
}
log.info(`CA Cert read in: ${caCertPath}`)
log.info(`CA private key read in: ${caKeyPath}`)
if (!caCertPath) {
caCertPath = config.getDefaultCACertPath()
}
if (!caKeyPath) {
caKeyPath = config.getDefaultCAKeyPath()
}
const rs = this.createCA({ caCertPath, caKeyPath })
if (rs.create) {
log.info(`CA Cert saved in: ${caCertPath}`)
log.info(`CA private key saved in: ${caKeyPath}`)
}
port = ~~port
const requestHandler = createRequestHandler(
requestInterceptor,
responseInterceptor,
middlewares,
createIntercepts,
externalProxy,
dnsConfig
dnsConfig,
setting
)
const upgradeHandler = createUpgradeHandler()
@ -59,18 +61,18 @@ module.exports = {
const server = new http.Server()
server.listen(port, () => {
console.log(colors.green(`dev-sidecar启动端口: ${port}`))
log.info(`dev-sidecar启动端口: ${port}`)
server.on('error', (e) => {
console.error(colors.red(e))
log.error('server error', e)
})
server.on('request', (req, res) => {
const ssl = false
// console.log('request,', req.url, req.port, req.host)
// log.info('request,', req.hostname)
requestHandler(req, res, ssl)
})
// tunneling for https
server.on('connect', (req, cltSocket, head) => {
// console.log('connect,', req.url)
// log.info('connect,', req.url)
connectHandler(req, cltSocket, head)
})
// TODO: handler WebSocket
@ -78,6 +80,10 @@ module.exports = {
const ssl = false
upgradeHandler(req, socket, head, ssl)
})
server.on('clientError', (err, socket) => {
log.error('client error', err)
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n')
})
if (callback) {
callback(server)
@ -85,7 +91,7 @@ module.exports = {
})
return server
},
createCA (caBasePath = config.getDefaultCABasePath()) {
return tlsUtils.initCA(caBasePath)
createCA (caPaths) {
return tlsUtils.initCA(caPaths)
}
}

View File

@ -1,20 +0,0 @@
const _ = require('lodash')
module.exports = (middlewares) => {
if (middlewares) {
if (Object.prototype.toString.call(middlewares) !== '[object Array]') {
throw new TypeError('middlewares must be a array')
}
}
//
// const sslConnectInterceptors = []
// const requestInterceptors = []
// const responseInterceptors = []
_.each(middlewares, (m) => {
if (m.buildIn === false || m.buildIn === 'false') {
} else {
// m.name
}
})
}

View File

@ -5,7 +5,7 @@ const forge = require('node-forge')
const pki = forge.pki
// const colors = require('colors')
const tls = require('tls')
const log = require('../../../utils/util.log')
module.exports = class FakeServersCenter {
constructor ({ maxLength = 256, requestHandler, upgradeHandler, caCert, caKey, getCertSocketTimeout }) {
this.queue = []
@ -23,13 +23,13 @@ module.exports = class FakeServersCenter {
if (this.queue.length >= this.maxLength) {
const delServerObj = this.queue.shift()
try {
console.log('超过最大服务数量,删除旧服务', delServerObj)
log.info('超过最大服务数量,删除旧服务', delServerObj)
delServerObj.serverObj.server.close()
} catch (e) {
console.log(e)
log.info(e)
}
}
console.log('add server promise:', serverPromiseObj)
log.info('add server promise:', serverPromiseObj)
this.queue.push(serverPromiseObj)
return serverPromiseObj
}
@ -64,7 +64,7 @@ module.exports = class FakeServersCenter {
SNICallback: (hostname, done) => {
(async () => {
const certObj = await this.certAndKeyContainer.getCertPromise(hostname, port)
console.log('sni callback:', hostname)
log.info('sni callback:', hostname)
done(null, tls.createSecureContext({
key: pki.privateKeyToPem(certObj.key),
cert: pki.certificateToPem(certObj.cert)
@ -88,7 +88,7 @@ module.exports = class FakeServersCenter {
this.requestHandler(req, res, ssl)
})
fakeServer.on('error', (e) => {
console.error(e)
log.error(e)
})
fakeServer.on('listening', () => {
const mappingHostNames = tlsUtils.getMappingHostNamesFormCert(certObj.cert)

View File

@ -9,6 +9,15 @@ const mkdirp = require('mkdirp')
const utils = exports
const pki = forge.pki
// const os = require('os')
// let username = 'dev-sidecar'
// try {
// const user = os.userInfo()
// username = user.username
// } catch (e) {
// console.log('get userinfo error', e)
// }
utils.createCA = function (CN) {
const keys = pki.rsa.generateKeyPair(2046)
const cert = pki.createCertificate()
@ -228,10 +237,7 @@ utils.getMappingHostNamesFormCert = function (cert) {
}
// sync
utils.initCA = function (basePath = config.getDefaultCABasePath()) {
const caCertPath = path.resolve(basePath, config.caCertFileName)
const caKeyPath = path.resolve(basePath, config.caKeyFileName)
utils.initCA = function ({ caCertPath, caKeyPath }) {
try {
fs.accessSync(caCertPath, fs.F_OK)
fs.accessSync(caKeyPath, fs.F_OK)

View File

@ -1,11 +0,0 @@
const debug = require('debug')
module.exports = function getLogger (name) {
return {
debug: debug(`dev-sidecar:${name}:debug`),
info: debug(`dev-sidecar:${name}:info`),
error: debug(`dev-sidecar:${name}:error`)
}
}
debug.enable('dev-sidecar:*')

View File

@ -1,6 +1,8 @@
const interceptors = require('./lib/interceptor')
const dnsUtil = require('./lib/dns')
const lodash = require('lodash')
const log = require('./utils/util.log')
const path = require('path')
function matchHostname (hostMap, hostname) {
const value = hostMap[hostname]
if (value) {
@ -46,57 +48,66 @@ module.exports = (config) => {
const dnsMapping = config.dns.mapping
const serverConfig = config
return {
const setting = serverConfig.setting
const options = {
port: serverConfig.port,
dnsConfig: {
providers: dnsUtil.initDNS(serverConfig.dns.providers),
mapping: dnsMapping
},
setting,
sslConnectInterceptor: (req, cltSocket, head) => {
const hostname = req.url.split(':')[0]
const inWhiteList = matchHostname(whiteList, hostname) != null
if (inWhiteList) {
console.log('白名单域名,不拦截', hostname)
log.info('白名单域名,不拦截', hostname)
return false
}
return !!matchHostname(intercepts, hostname) // 配置了拦截的域名,将会被代理
},
requestInterceptor: (rOptions, req, res, ssl, next, context) => {
createIntercepts: (context) => {
const rOptions = context.rOptions
const hostname = rOptions.hostname
const interceptOpts = matchHostname(intercepts, hostname)
if (!interceptOpts) { // 该域名没有配置拦截器,直接过
next()
return
}
const matchIntercepts = []
for (const regexp in interceptOpts) { // 遍历拦截配置
const interceptOpt = interceptOpts[regexp]
interceptOpt.key = regexp
if (regexp !== true) {
if (!isMatched(req.url, regexp)) {
if (!isMatched(rOptions.path, regexp)) {
continue
}
}
for (const interceptImpl of interceptors) {
for (const impl of interceptors) {
// 根据拦截配置挑选合适的拦截器来处理
if (!interceptImpl.is(interceptOpt) && interceptImpl.requestInterceptor) {
continue
}
try {
const result = interceptImpl.requestInterceptor(interceptOpt, rOptions, req, res, ssl, context)
if (result) { // 拦截成功,其他拦截器就不处理了
return
if (impl.is(interceptOpt)) {
const interceptor = {}
if (impl.requestIntercept) {
// req拦截器
interceptor.requestIntercept = (context, req, res, ssl, next) => {
return impl.requestIntercept(context, interceptOpt, req, res, ssl, next)
}
} else if (impl.responseIntercept) {
// res拦截器
interceptor.responseIntercept = (context, req, res, proxyReq, proxyRes, ssl, next) => {
return impl.responseIntercept(context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next)
}
}
} catch (err) {
// 拦截失败
console.error(err)
matchIntercepts.push(interceptor)
}
}
}
next()
},
responseInterceptor: (req, res, proxyReq, proxyRes, ssl, next, context) => {
next()
return matchIntercepts
}
}
if (setting.rootCaFile) {
options.caCertPath = setting.rootCaFile.certPath
options.caKeyPath = setting.rootCaFile.keyPath
}
return options
}

View File

@ -1,8 +1,9 @@
const os = require('os')
const log = require('util.log')
const util = {
getNodeVersion () {
const version = process.version
console.log(version)
log.info(version)
}
}
util.getNodeVersion()

View File

@ -0,0 +1,8 @@
const log4js = require('log4js')
const proxyConfig = require('../lib/proxy/common/config')
log4js.configure({
appenders: { std: { type: 'stdout', level: 'debug' }, file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename: proxyConfig.getDefaultCABasePath() + '/logs/server.log' } },
categories: { default: { appenders: ['file', 'std'], level: 'info' } }
})
const logger = log4js.getLogger('server')
module.exports = logger

View File

@ -0,0 +1,8 @@
module.exports = {
fireError (e) {
process.send({ type: 'error', event: e })
},
fireStatus (status) {
process.send({ type: 'status', event: status })
}
}

View File

@ -20,4 +20,13 @@ const dnsProviders = dns.initDNS({
// dnsProviders.usa.lookup(hostname)
const hostname1 = 'api.github.com'
dnsProviders.aliyun.lookup(hostname1)
dnsProviders.usa.lookup(hostname1)
const hostname2 = 'hk.docmirror.cn'
dnsProviders.usa.lookup(hostname2)
const hostname3 = 'github.docmirror.cn'
dnsProviders.usa.lookup(hostname3)
const hostname4 = 'gh.docmirror.top'
dnsProviders.usa.lookup(hostname4)
const hostname5 = 'gh2.docmirror.top'
dnsProviders.usa.lookup(hostname5)

View File

@ -0,0 +1,3 @@
const monkey = require('../../../src/lib/monkey/index')
const scripts = monkey.load()
console.log(scripts[0])

View File

@ -0,0 +1,17 @@
const name = '/docmirror/dev-sidecar/raw/master/doc/index.png'
// https://raw.fastgit.org/docmirror/dev-sidecar/master/doc/index.png
const ret = name.replace(/^(.+)\/raw\/(.+)$/, 'raw.fastgit.org$1/$2')
console.log(ret)
const reg = new RegExp('^/[^/]+/[^/]+$')
console.log('/greper/d2-crud-plus/blob/master/.eslintignore'.match(reg))
const chunk = Buffer.from('<head></head>')
const script = '<script>a</script>'
const index = chunk.indexOf('</head>')
const scriptBuf = Buffer.from(script)
const chunkNew = Buffer.alloc(chunk.length + scriptBuf.length)
chunk.copy(chunkNew, 0, 0, index)
scriptBuf.copy(chunkNew, index, 0)
chunk.copy(chunkNew, index + scriptBuf.length, index)
console.log(chunkNew.toString())

View File

@ -0,0 +1,4 @@
const CryptoJs = require('crypto-js')
const ret = CryptoJs.SHA256('111111111111')
console.log(ret.toString(CryptoJs.enc.Base64))
console.log(1 / 2)

View File

@ -2159,6 +2159,11 @@ crypto-browserify@^3.11.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
crypto-js@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.0.0.tgz#2904ab2677a9d042856a2ea2ef80de92e4a36dcc"
integrity sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==
cyclist@^1.0.1:
version "1.0.1"
resolved "https://registry.npm.taobao.org/cyclist/download/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"