Browse Source

# 新特性:

1. 系统代理中的排除列表,可配置化;可在界面中配置,也可在远程配置中配置

# 问题修复:
  1. 空指针异常导致插件关闭失败的问题修复
  2. 偶发的空指针异常导致代理请求失败,原因不明,暂时规避掉

# 配置调整:
  1. 添加几项targets配置,优化1项配置
  2. 添加几项拦截配置
  3. 添加很多项系统代理排除项,并调整白名单配置
  4. 优化dns配置
  5. `.gitignore` 中,排除掉 `package-lock.json`

# 功能优化:
  1. 远程配置内容格式不正确时,不保存它
  2. 部分日志优化
  3. 封装 merge.js 的 doDiff 和 doMarge 两个方法,方便使用和测试,同时添加测试用例 `mergeTest.js`,删除老的 `mergeTest.mjs`;doDiff优化过,使 config.json5 的内容更准确的体现用户自定义配置
  4. 打开链接的方法,全部改为异步

# 文档:
  1. 移除部分 `gitee仓库` 相关的内容,因为gitee的仓库被禁了,部分gitee地址改为github地址
  2. 代码贡献说明中,添加环境准备相关说明;同时添加4个bat脚本文件方便windows用户快速贡献代码
pull/274/head
王良 9 months ago
parent
commit
d0096d209c
  1. 3
      .gitignore
  2. 101
      README.md
  3. 1
      doc/caroot.md
  4. 7065
      packages/core/package-lock.json
  5. 123
      packages/core/src/config.js
  6. 53
      packages/core/src/config/index.js
  7. 95
      packages/core/src/config/remote_config.json5
  8. 4
      packages/core/src/expose.js
  9. 87
      packages/core/src/merge.js
  10. 7
      packages/core/src/modules/plugin/overwall/config.js
  11. 103
      packages/core/src/modules/proxy/index.js
  12. 8
      packages/core/src/modules/server/index.js
  13. 47
      packages/core/src/shell/scripts/set-system-proxy/index.js
  14. 88
      packages/core/test/mergeTest.js
  15. 6
      packages/gui/src/bridge/update/front.js
  16. 2
      packages/gui/src/view/api.js
  17. 4
      packages/gui/src/view/components/setup-ca.vue
  18. 4
      packages/gui/src/view/pages/index.vue
  19. 2
      packages/gui/src/view/pages/plugin/overwall.vue
  20. 35
      packages/gui/src/view/pages/proxy.vue
  21. 4
      packages/gui/src/view/pages/setting.vue
  22. 6
      packages/mitmproxy/src/lib/proxy/mitmproxy/createRequestHandler.js
  23. 3
      packages/mitmproxy/src/options.js
  24. 5
      script/1、installEnv.bat
  25. 3
      script/2、installBootstrap.bat
  26. 3
      script/3、buildAndRun.bat
  27. 3
      script/4、generateSetupFile.bat
  28. 57
      test/mergeTest.mjs

3
.gitignore vendored

@ -6,4 +6,5 @@ gen
node_modules/
*.lock
*.log
pnpm-lock.yaml
pnpm-lock.yaml
package-lock.json

101
README.md

@ -5,7 +5,6 @@
通过本地代理的方式将https请求代理到一些国内的加速通道上
<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>
<a href='https://gitee.com/docmirror/dev-sidecar'><img src='./doc/gitee.png' alt='star'/></a>
>
> Gitee上的同步项目已被封禁,此项目将不再更新与维护 【狗头保命】
@ -26,7 +25,6 @@
> 注意:由于electron无法监听windows的关机事件,开着ds情况下直接重启电脑,会导致无法上网,你可以手动启动ds即可恢复网络,你也可以将ds设置为开机自启。
>
> 关于此问题的更多讨论请前往:
> https://gitee.com/docmirror/dev-sidecar/issues/I49OUL
> https://github.com/docmirror/dev-sidecar/issues/109
>
@ -40,17 +38,17 @@
## 一、 特性
### 1、 dns优选(解决***污染问题)
### 1.1、 dns优选(解决***污染问题)
* 根据网络状况智能解析最佳域名ip地址,获取最佳网络速度
* 解决一些网站和库无法访问或访问速度慢的问题
* 建议遇到打开比较慢的国外网站,可以优先尝试将该域名添加到dns设置中(注意:被***封杀的无效)
### 2、 请求拦截
### 1.2、 请求拦截
* 拦截打不开的网站,代理到加速镜像站点上去。
* 可配置多个镜像站作为备份
* 具备测速机制,当访问失败或超时之后,自动切换到备用站点,使得目标服务高可用
### 3、 github加速
### 1.3、 github加速
* github 直连加速 (通过修改sni实现,感谢 [fastGithub](https://github.com/dotnetcore/FastGithub) 提供的思路)
* release、source、zip下载加速
* clone 加速
@ -67,11 +65,11 @@
> 由于此脚本在ds中是打包在本地的,更新会不及时,你可以直接通过浏览器安装油猴插件使用此脚本,从而获得最新更新(ds本地的可以通过`加速服务->基本设置->启用脚本`进行关闭)。
### 4、 Stack Overflow 加速
### 1.4、 Stack Overflow 加速
* 将ajax.google.com代理到加速CDN上
* recaptcha 图片验证码加速
### 5、 npm加速
### 1.5、 npm加速
* 支持开启npm代理
* 官方与淘宝npm registry一键切换,
* 某些npm install的时候,并且使用cnpm也无法安装时,可以尝试开启npm代理再试
@ -87,11 +85,10 @@
## 二、快速开始
支持windows、Mac、Linux(Ubuntu)
### DevSidecar桌面应用
### 2.1、DevSidecar桌面应用
#### 1 下载安装包
#### 1下载安装包
* release下载
[Gitee Release](https://gitee.com/docmirror/dev-sidecar/releases)
[Github Release](https://github.com/docmirror/dev-sidecar/releases)
> Windows: 请选择DevSidecar-x.x.x.exe
@ -104,13 +101,13 @@
> 注意:由于没有买应用证书,所以应用在下载安装时会有“未知发行者”等安全提示,选择保留即可。
#### 2 安装后打开
#### 2安装后打开
> 注意:mac版安装需要在“系统偏好设置->安全性与隐私->通用”中解锁并允许应用安装
![](./doc/index.png)
#### 3 安装根证书
#### 3安装根证书
第一次打开会提示安装证书,根据提示操作即可
@ -122,12 +119,12 @@
> 火狐浏览器需要[手动安装证书](#3浏览器打开提示证书不受信任)
#### 4 开始加速吧
#### 4开始加速吧
去试试打开github
### 开启前 vs 开启后
### 2.2、开启前 vs 开启后
| | 开启前 | 开启后 |
| ---- | ---- | ---- |
@ -138,14 +135,14 @@
## 三、模式说明
### 安全模式
### 3.1、安全模式
* 此模式:关闭拦截、关闭增强、开启dns优选、开启测速
* 最安全,无需安装证书,可以在浏览器地址栏左侧查看域名证书
* 功能也最弱,只有特性1,相当于查询github的国外ip,手动改hosts一个意思。
* github的可访问性不稳定,取决于IP测速,如果有绿色ip存在,就 `有可能` 可以直连访问。
![](./doc/speed.png)
### 默认模式
### 3.2、默认模式
* 此模式:开启拦截、关闭增强、开启dns优选、开启测速
* 需要安装证书,通过修改sni直连访问github
* 功能上包含特性1/2/3/4。
@ -156,7 +153,8 @@
* 建议遇到打开比较慢的国外网站,可以尝试将该域名添加到dns设置中(注意:被***封杀的无效)
### 其他加速
1. git clone 加速
#### 1)git clone 加速
方式1:快捷复制:
> 开启脚本支持,然后在复制clone链接下方,即可复制到加速链接
@ -167,14 +165,14 @@
> 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/) 这个很容易超限
#### 2)github.com的镜像网站(注意:不能登录)
> 1. [hub.fastgit.org](https://hub.fastgit.org/)
> 2. [github.com.cnpmjs.org](https://github.com.cnpmjs.org/) 这个很容易超限
## 五、api
### 拦截配置
### 5.1、拦截配置
没有配置域名的不会拦截,其他根据配置进行拦截处理
```js
const intercepts = {
@ -210,32 +208,32 @@ const intercepts = {
}
```
### DNS优选配置
### 5.2、DNS优选配置
某些域名解析出来的ip会无法访问,(比如api.github.com会被解析到新加坡的ip上,新加坡的服务器在上午挺好,到了晚上就卡死,基本不可用)
通过从dns上获取ip列表,切换不同的ip进行尝试,最终会挑选到一个最快的ip
```js
dns: {
mapping: {
//
'api.github.com': 'usa', // "解决push的时候需要输入密码的问题",
'gist.github.com': 'usa' // 解决gist无法访问的问题
"*.githubusercontent.com": "usa" // 解决github头像经常下载不到的问题
'*.githubusercontent.com': 'usa' // 解决github头像经常下载不到的问题
}
},
}
```
注意:暂时只支持IPv4的解析
## 六、问题排查
### 1、dev-sidecar的前两个开关没有处于打开状态
### 6.1、dev-sidecar的前两个开关没有处于打开状态
1. 尝试将开关按钮手动打开
2. 请尝试右键dev-sidecar图标,点退出。再重新打开
3. 如果还不行,请将日志发送给作者
如果是mac系统,可能是下面的原因
#### 1.1 Mac系统使用时,首页的系统代理开关无法打开
#### 1Mac系统使用时,首页的系统代理开关无法打开
出现这个问题可能是没有开启系统代理命令的执行权限
```
networksetup -setwebproxy 'WiFi' 127.0.0.1 1181
@ -248,7 +246,7 @@ networksetup -setwebproxy 'WiFi' 127.0.0.1 1181
>系统偏好设置—>安全性与隐私—> 通用—> 高级—> 访问系统范围的偏好设置需要输入管理员密码(取消勾选)
### 2、没有加速效果
### 6.2、没有加速效果
>本应用仅支持https加速,请务必确认你访问的网站地址是https开头的
@ -268,23 +266,23 @@ networksetup -setwebproxy 'WiFi' 127.0.0.1 1181
正常情况下ds在“系统代理”开关打开时,会自动设置系统代理。
### 3、浏览器打开提示证书不受信任
### 6.3、浏览器打开提示证书不受信任
![](./doc/crt-error.png)
一般是证书安装位置不对,重新安装证书后,重启浏览器
#### 3.1 windows: 请确认证书已正确安装在“信任的根证书颁发机构”下
#### 1)windows: 请确认证书已正确安装在“信任的根证书颁发机构”下
#### 3.2 mac: 请确认证书已经被安装并已经设置信任。
#### 2)mac: 请确认证书已经被安装并已经设置信任。
#### 3.3 火狐浏览器:火狐浏览器不走系统的根证书,需要在选项中添加根证书
#### 3火狐浏览器:火狐浏览器不走系统的根证书,需要在选项中添加根证书
> 1、火狐浏览器->选项->隐私与安全->证书->查看证书
> 2、证书颁发机构->导入
> 3、选择证书文件`C:\Users(用户)\Administrator(你的账号)\.dev-sidecar\dev-sidecar.ca.crt`(Mac或linux为`~/.dev-sidecar`目录)
> 4、勾选信任由此证书颁发机构来标识网站,确定即可
### 4. 打开github显示连接超时
### 6.4、打开github显示连接超时
```html
DevSidecar Warning:
Error: www.github.com:443, 代理请求超时
@ -293,18 +291,18 @@ Error: www.github.com:443, 代理请求超时
2、如果是安全模式,则是因为不稳定导致的,等一会再刷新试试
3、如果是增强模式,则是由于访问人数过多,正常现象
### 5、查看日志是否有报错
### 6.5、查看日志是否有报错
如果还是不行,请在下方加作者好友,将服务日志发送给作者进行分析
日志打开方式:加速服务->右边日志按钮->打开日志文件夹
![](./doc/log.png)
### 6、某些原本可以打开的网站打不开了
### 6.6、某些原本可以打开的网站打不开了
1、可以尝试关闭pac
2、可以将域名加入白名单
### 7、应用意外关闭导致没有网络了
### 6.7、应用意外关闭导致没有网络了
应用开启后会自动修改系统代理设置,正常退出会自动关闭系统代理
当应用意外关闭时,可能会因为没有将系统代理恢复,从而导致完全无法上网。
@ -314,7 +312,7 @@ Error: www.github.com:443, 代理请求超时
3、如果你是因为开着ds的情况下重启电脑导致无法上网,你可以设置ds为开机自启
### 8、卸载应用后上不了网,git请求不了
### 6.8、卸载应用后上不了网,git请求不了
如果你在卸载应用前,没有正常退出app,就有可能无法上网。请按如下步骤操作恢复您的网络:
1、关闭系统代理设置,参见:[手动关闭系统代理设置](./doc/recover.md)
@ -329,12 +327,33 @@ npm config delete proxy
npm config delete https-proxy
```
## 七、在其他程序使用
* [java程序使用](./doc/other.md#Java程序使用)
## 八、贡献代码
### 开发调试模式启动
### 8.1、准备环境
#### 1)安装 `nodejs`
推荐安装 nodejs 16.x版本,其他版本未做测试
#### 2)安装 `lerna``phantomjs`
运行如下命令即可安装所需依赖:
> 注:lerna指定为6.x版本,更高版本会导致打包失败(不兼容导致)
```shell
npm install cnpm -g --registry=https://registry.npm.taobao.org
cnpm install lerna@6 -g
cnpm install phantomjs -g
```
### 8.2、开发调试模式启动
运行如下命令即可开发模式启动
```shell
@ -342,7 +361,7 @@ git clone https://github.com/docmirror/dev-sidecar
cd dev-sidecar
npm install lerna -g
# 注意不要使用 `npm install` 来安装依赖,因为 `lerna bootstrap` 会自动安装依赖
lerna bootstrap
cd packages/gui
@ -352,13 +371,13 @@ npm run electron
```
> 如果electron依赖包下载不动,可以开启ds的npm加速
### 打包成可执行文件
### 8.3、打包成可执行文件
```shell
# 先执行上面的步骤,然后运行如下命令打包成可执行文件
npm run electron:build
```
### 提交pr
### 8.4、提交pr
如果你想将你的修改贡献出来,请提交pr

1
doc/caroot.md

@ -46,7 +46,6 @@ DevSidecar在第一次启动时会在本地随机生成一份根证书,当有
> 对于应用来源风险:
> 请勿从未知网站下载DevSidecar应用,认准官方版本发布地址
> [Gitee Release](https://gitee.com/docmirror/dev-sidecar/releases)
> [Github Release](https://github.com/docmirror/dev-sidecar/releases)
>
> 或者从源码自行编译安装

7065
packages/core/package-lock.json generated

File diff suppressed because it is too large Load Diff

123
packages/core/src/config.js

@ -6,22 +6,14 @@ const JSON5 = require('json5').default
const request = require('request')
const path = require('path')
const log = require('./utils/util.log')
const mergeApi = require('./merge.js')
let configTarget = lodash.cloneDeep(defConfig)
function get () {
return configTarget
}
function _deleteDisabledItem (target) {
lodash.forEach(target, (item, key) => {
if (item == null) {
delete target[key]
}
if (lodash.isObject(item)) {
_deleteDisabledItem(item)
}
})
}
const getDefaultConfigBasePath = function () {
return get().server.setting.userBasePath
}
@ -40,46 +32,6 @@ function _getConfigPath () {
return dir + '/config.json5'
}
function doMerge (defObj, newObj) {
if (newObj == null) {
return defObj
}
const defObj2 = { ...defObj }
const newObj2 = {}
for (const key in newObj) {
const newValue = newObj[key]
const defValue = defObj[key]
if (newValue != null && defValue == null) {
newObj2[key] = newValue
continue
}
if (lodash.isEqual(newValue, defValue)) {
delete defObj2[key]
continue
}
if (lodash.isArray(newValue)) {
delete defObj2[key]
newObj2[key] = newValue
continue
}
if (lodash.isObject(newValue)) {
newObj2[key] = doMerge(defValue, newValue)
delete defObj2[key]
continue
} else {
// 基础类型,直接覆盖
delete defObj2[key]
newObj2[key] = newValue
continue
}
}
// defObj 里面剩下的是被删掉的
lodash.forEach(defObj2, (defValue, key) => {
newObj2[key] = null
})
return newObj2
}
let timer
const configApi = {
async startAutoDownloadRemoteConfig () {
@ -112,7 +64,19 @@ const configApi = {
return
}
if (response && response.statusCode === 200) {
fs.writeFileSync(_getRemoteSavePath(), body)
// 尝试解析远程配置,如果解析失败,则不保存它
let remoteConfig
try {
remoteConfig = JSON5.parse(body)
} catch (e) {
log.error('远程配置内容格式不正确:', body)
remoteConfig = null
}
if (remoteConfig != null) {
fs.writeFileSync(_getRemoteSavePath(), body)
}
resolve()
} else {
const message = '下载远程配置失败:' + response.message + ',code:' + response.statusCode
@ -128,13 +92,15 @@ const configApi = {
}
try {
const path = _getRemoteSavePath()
log.info('读取合并远程配置文件:', path)
if (fs.existsSync(path)) {
log.info('读取远程配置文件:', path)
const file = fs.readFileSync(path)
return JSON5.parse(file.toString())
} else {
log.warn('远程配置文件不存在:', path)
}
} catch (e) {
log.info('远程配置读取有误', e)
log.warn('远程配置读取失败:', e)
}
return {}
@ -142,34 +108,39 @@ const configApi = {
/**
* 保存自定义的 config
* @param newConfig
* @param remoteConfig //远程配置
*/
save (newConfig) {
// 对比默认config的异同
// configApi.set(newConfig)
const defConfig = configApi.getDefault()
let defConfig = configApi.getDefault()
// 如果开启了远程配置,则读取远程配置,合并到默认配置中
if (get().app.remoteConfig.enabled === true) {
doMerge(defConfig, configApi.readRemoteConfig())
defConfig = mergeApi.doMerge(defConfig, configApi.readRemoteConfig())
}
const saveConfig = doMerge(defConfig, newConfig)
fs.writeFileSync(_getConfigPath(), JSON5.stringify(saveConfig, null, 2))
// 计算新配置与默认配置(启用远程配置时,含远程配置)的差异,并保存到 config.json5 中
const diffConfig = mergeApi.doDiff(defConfig, newConfig)
fs.writeFileSync(_getConfigPath(), JSON5.stringify(diffConfig, null, 2))
configApi.reload()
return saveConfig
return diffConfig
},
doMerge,
doMerge: mergeApi.doMerge,
doDiff: mergeApi.doDiff,
/**
* 读取后合并配置
* 读取 config.json5 合并配置
* @returns {*}
*/
reload () {
const path = _getConfigPath()
let userConfig
if (!fs.existsSync(path)) {
return configApi.get()
userConfig = {}
} else {
const file = fs.readFileSync(path)
userConfig = JSON5.parse(file.toString())
}
const file = fs.readFileSync(path)
const userConfig = JSON5.parse(file.toString())
configApi.set(userConfig)
const config = configApi.get()
const config = configApi.set(userConfig)
return config || {}
},
update (partConfig) {
@ -181,17 +152,13 @@ const configApi = {
if (newConfig == null) {
return
}
const merged = lodash.cloneDeep(newConfig)
const clone = lodash.cloneDeep(defConfig)
function customizer (objValue, srcValue) {
if (lodash.isArray(objValue)) {
return srcValue
}
}
lodash.mergeWith(merged, clone, customizer)
lodash.mergeWith(merged, configApi.readRemoteConfig(), customizer)
lodash.mergeWith(merged, newConfig, customizer)
_deleteDisabledItem(merged)
const merged = lodash.cloneDeep(defConfig)
const remoteConfig = configApi.readRemoteConfig()
mergeApi.doMerge(merged, remoteConfig)
mergeApi.doMerge(merged, newConfig)
mergeApi.deleteNullItems(merged)
configTarget = merged
log.info('加载配置完成')
return configTarget

53
packages/core/src/config/index.js

@ -1,14 +1,18 @@
const path = require('path')
function getUserBasePath () {
const userHome = process.env.USERPROFILE || process.env.HOME || '/'
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 = {
app: {
mode: 'default',
@ -17,7 +21,7 @@ module.exports = {
},
remoteConfig: {
enabled: true,
url: 'https://gitee.com/docmirror/dev-sidecar/raw/master/packages/core/src/config/remote_config.json5'
url: 'https://github.com/docmirror/dev-sidecar/raw/master/packages/core/src/config/remote_config.json5'
},
dock: {
hideWhenWinClose: false
@ -84,22 +88,29 @@ module.exports = {
'github.githubassets.com': {
'.*': {
proxy: 'github.githubassets.com',
backup: [
'assets.fastgit.org'
],
sni: 'assets.fastgit.org'
sni: 'baidu.com'
}
},
'camo.githubusercontent.com': {
'.*': {
proxy: 'camo.githubusercontent.com',
sni: 'baidu.com'
}
},
'collector.github.com': {
'.*': {
proxy: 'collector.github.com',
sni: 'baidu.com'
}
},
'customer-stories-feed.github.com': {
'.*': { proxy: 'customer-stories-feed.fastgit.org' }
},
'raw.githubusercontent.com': {
'.*': {
proxy: 'raw.githubusercontent.com',
sni: 'baidu.com'
}
// '.*': { proxy: 'raw.fastgit.org' }
},
'user-images.githubusercontent.com': {
'.*': {
@ -191,18 +202,25 @@ module.exports = {
}
},
whiteList: {
'*.cn': true,
'cn.*': true,
'*china*': true,
'dingtalk.com': true,
'*.dingtalk.com': true,
'qq.com': true,
'*.qq.com': true,
'apple.com': true,
'*.apple.com': true,
'microsoft.com': true,
'*.microsoft.com': true,
'alipay.com': true,
'*.alipay.com': true,
'pay.weixin.qq.com': true,
'www.baidu.com': true
'baidu.com': true,
'*.baidu.com': true
},
// sniList: {
sniList: {
// 'github.com': 'abaidu.com'
// },
},
dns: {
providers: {
aliyun: {
@ -227,20 +245,15 @@ module.exports = {
}
},
mapping: {
// 'assets.fastgit.org': 'usa',
'*github*.com': 'quad9',
'*github.io': 'quad9',
'*stackoverflow.com': 'quad9',
'*.electronjs.org': 'quad9',
'*amazonaws.com': 'quad9',
'*githubusercontent.com': 'quad9',
'*yarnpkg.com': 'quad9',
'*cloudfront.net': 'quad9',
'*cloudflare.com': 'quad9',
'*github.io': 'quad9',
'img.shields.io': 'quad9',
'*.githubusercontent.com': 'quad9',
'*.githubassets.com': 'quad9',
// "解决push的时候需要输入密码的问题",
'github.com': 'quad9',
'*github.com': 'quad9',
'*.vuepress.vuejs.org': 'quad9',
'gh.docmirror.top': 'quad9',
'*v2ex.com': 'quad9',
@ -250,7 +263,7 @@ module.exports = {
},
speedTest: {
enabled: true,
interval: 60000,
interval: 300000,
hostnameList: ['github.com'],
dnsProviders: ['usa', 'quad9', 'rubyfish']
}

95
packages/core/src/config/remote_config.json5

@ -1,10 +1,22 @@
{
server: {
intercepts: {
'github.githubassets.com': {
'.*': {
proxy: 'github.githubassets.com',
sni: 'baidu.com',
"server": {
"intercepts": {
"github.githubassets.com": {
".*": {
"proxy": "github.githubassets.com",
"sni": "baidu.com"
}
},
"camo.githubusercontent.com": {
".*": {
"proxy": "camo.githubusercontent.com",
"sni": "baidu.com"
}
},
"collector.github.com": {
".*": {
"proxy": "collector.github.com",
"sni": "baidu.com"
}
},
"www.gstatic.com": {
@ -13,43 +25,46 @@
}
}
},
dns: {
mapping: {
'*jetbrains.com': 'quad9',
'*azureedge.net': 'quad9',
'*stackoverflow.com': 'quad9'
},
speedTest: {
interval: 300000,
"dns": {
"mapping": {
"*jetbrains.com": "quad9",
"*azureedge.net": "quad9",
"*stackoverflow.com": "quad9"
},
"speedTest": {
"interval": 300000
}
}
},
plugin: {
overwall: {
targets: {
'*azureedge.net': true,
'github.com': true,
'*wikimedia.org': true,
'v2ex.com': true,
'*cloudfront.net': true,
'*bing.com': true,
'*discourse-cdn.com': true,
'*gravatar.com': true,
'*docker.com': true,
'*vueuse.org': true,
'*elastic.co': true,
'*optimizely.com': true,
'*stackpathcdn.com': true,
'*fastly.net': true,
'*cloudflare.com': true,
'*233v2.com': true,
'*v2fly.org': true,
'*telegram.org': true,
'*amazon.com': true,
'*googleapis.com': true,
'*cloudflareinsights.com': true,
'*.intlify.dev': true,
'*segment.io': true
"plugin": {
"overwall": {
"targets": {
"*github*.com": true,
"*wikimedia.org": true,
"v2ex.com": true,
"*azureedge.net": true,
"*cloudfront.net": true,
"*bing.com": true,
"*discourse-cdn.com": true,
"*gravatar.com": true,
"*docker.com": true,
"*vueuse.org": true,
"*elastic.co": true,
"*optimizely.com": true,
"*stackpathcdn.com": true,
"*fastly.net": true,
"*cloudflare.com": true,
"*233v2.com": true,
"*v2fly.org": true,
"*telegram.org": true,
"*amazon.com": true,
"*googleapis.com": true,
"*.google-analytics.com": true,
"*cloudflareinsights.com": true,
"*.intlify.dev": true,
"*segment.io": true,
"*.shields.io": true,
"*.jsdelivr.net": true
}
}
}

4
packages/core/src/expose.js

@ -83,7 +83,7 @@ async function shutdown () {
try {
const plugins = []
for (const key in plugin) {
if (status.plugin[key].enabled && plugin[key].close) {
if (status.plugin[key] && status.plugin[key].enabled && plugin[key].close) {
const close = async () => {
try {
await plugin[key].close()
@ -99,7 +99,7 @@ async function shutdown () {
await Promise.all(plugins)
}
} catch (error) {
log.error('插件关闭失败'.error)
log.error('插件关闭失败', error)
}
if (status.proxy.enabled) {

87
packages/core/src/merge.js

@ -0,0 +1,87 @@
const lodash = require('lodash')
/**
* 找出 newObj 相对于 oldObj 有差异的部分
*
* @param oldObj
* @param newObj
* @returns {{}|*}
*/
function doDiff (oldObj, newObj) {
if (newObj == null) {
return oldObj
}
// 临时的对象,用于找出被删除的数据
const tempObj = { ...oldObj }
// 删除空项,使差异对象更干净一些,体现出用户自定义内容
deleteNullItems(tempObj)
// 保存差异的对象
const diffObj = {}
// 读取新对象,并解析
for (const key in newObj) {
const newValue = newObj[key]
const oldValue = oldObj[key]
// 新值不为空,旧值为空时,直接取新值
if (newValue != null && oldValue == null) {
diffObj[key] = newValue
continue
}
// 新旧值相等时,忽略
if (lodash.isEqual(newValue, oldValue)) {
delete tempObj[key]
continue
}
// 新的值为数组时,直接取新值
if (lodash.isArray(newValue)) {
diffObj[key] = newValue
delete tempObj[key]
continue
}
// 新的值为对象时,递归合并
if (lodash.isObject(newValue)) {
diffObj[key] = doDiff(oldValue, newValue)
delete tempObj[key]
continue
}
// 基础类型,直接覆盖
delete tempObj[key]
diffObj[key] = newValue
}
// tempObj 里面剩下的是被删掉的数据
lodash.forEach(tempObj, (oldValue, key) => {
// 将被删除的属性设置为null,目的是为了merge时,将被删掉的对象设置为null,达到删除的目的
diffObj[key] = null
})
return diffObj
}
function deleteNullItems (target) {
lodash.forEach(target, (item, key) => {
if (item == null) {
delete target[key]
}
if (lodash.isObject(item)) {
deleteNullItems(item)
}
})
}
module.exports = {
doMerge: function (oldObj, newObj) {
return lodash.mergeWith(oldObj, newObj, function (objValue, srcValue) {
if (lodash.isArray(objValue)) {
return srcValue
}
})
},
doDiff,
deleteNullItems
}

7
packages/core/src/modules/plugin/overwall/config.js

@ -10,7 +10,7 @@ module.exports = {
}
},
targets: {
'github.com': true,
'*github*.com': true,
'*wikimedia.org': true,
'v2ex.com': true,
'*azureedge.net': true,
@ -30,9 +30,12 @@ module.exports = {
'*telegram.org': true,
'*amazon.com': true,
'*googleapis.com': true,
'*.google-analytics.com': true,
'*cloudflareinsights.com': true,
'*.intlify.dev': true,
'*segment.io': true
'*segment.io': true,
'*.shields.io': true,
'*.jsdelivr.net': true
},
pac: {
enabled: true,

103
packages/core/src/modules/proxy/index.js

@ -54,7 +54,108 @@ module.exports = {
name: '系统代理',
use: 'local',
other: [],
setEnv: false
setEnv: false,
excludeIpList: [
// region 中国大陆,可直接访问,无需代理
// 中国大陆域名,大部分可直接访问,无需代理
'*.cn',
'cn.*',
// CSDN
'*.csdn.net',
// 百度
'*.baidu.com',
// 腾讯
'*.tencent.com',
'*.qq.com',
'*.weixin.com',
'*.wechat.com',
// 阿里
'*.alipay.com',
'*.taobao.com',
'*.tmall.com',
'*.aliyun.com',
'*.dingtalk.com', // 不排除会导致钉钉的团队文档打不开(原因未知)
// Gitee
'gitee.com',
'*.gitee.com',
'*.gitee.io',
// OSS
'*.sonatype.org',
// Maven镜像
'*.maven.org',
// Maven Repository
'*.mvnrepository.com',
'challenges.cloudflare.com', // 在访问 mvnrepository.com 的人机校验时使用,国内可直接访问,所以不需要代理,代理了反而变慢了。
// 苹果
'*.apple.com',
'*.icloud.com',
// 微软
'*.microsoft.com',
'*.windows.com',
'*.office.com',
'*.office.net',
'*.live.com',
'*.msn.com',
// WPS
'*.wps.com',
// 奇虎
'*.qihoo.com',
'*.qihucdn.com',
// 360
'*.360.com',
'*.360safe.com',
'*.360buyimg.com',
'*.360buy.com',
// 京东
'*.jd.com',
'*.jcloud.com',
'*.jcloudcs.com',
'*.jcloudcache.com',
'*.jcloudcdn.com',
'*.jcloudlb.com',
// endregion
// 本地地址,无需代理
'localhost',
'localhost.*', // 部分VPN会在host中添加这种格式的域名指向127.0.0.1,所以也排除掉
'127.*',
'test.*', // 本地开发时,测试用的虚拟域名格式,无需代理
// 服务器端常用地址,无需代理
'10.*',
'172.16.*',
'172.17.*',
'172.18.*',
'172.19.*',
'172.20.*',
'172.21.*',
'172.22.*',
'172.23.*',
'172.24.*',
'172.25.*',
'172.26.*',
'172.27.*',
'172.28.*',
'172.29.*',
'172.30.*',
'172.31.*',
// 局域网地址,无需代理
'192.168.*'
]
},
status: {
enabled: false,

8
packages/core/src/modules/server/index.js

@ -69,9 +69,9 @@ const serverApi = {
serverConfig.plugin = allConfig.plugin
// fireStatus('ing') // 启动中
const basePath = serverConfig.setting.userBasePath
const runningConfig = path.join(basePath, '/running.json')
fs.writeFileSync(runningConfig, JSON5.stringify(serverConfig, null, 2))
const serverProcess = fork(mitmproxyPath, [runningConfig])
const runningConfigPath = path.join(basePath, '/running.json')
fs.writeFileSync(runningConfigPath, JSON5.stringify(serverConfig, null, 2))
const serverProcess = fork(mitmproxyPath, [runningConfigPath])
server = {
id: serverProcess.pid,
process: serverProcess,
@ -106,7 +106,7 @@ const serverApi = {
event.fire('speed', msg.event)
}
})
return { port: runningConfig.port }
return { port: serverConfig.port }
},
async kill () {
if (server) {

47
packages/core/src/shell/scripts/set-system-proxy/index.js

@ -6,38 +6,10 @@ const Registry = require('winreg')
const execute = Shell.execute
const execFile = Shell.execFile
const refreshInternetPs = require('./refresh-internet')
const PowerShell = require('node-powershell')
const log = require('../../../utils/util.log')
const path = require('path')
const childProcess = require('child_process')
const util = require('util')
const fs = require('fs')
const _exec = util.promisify(childProcess.exec)
const extraPath = require('../extra-path/index')
const _lanIP = [
'localhost',
'127.*',
'10.*',
'172.16.*',
'172.17.*',
'172.18.*',
'172.19.*',
'172.20.*',
'172.21.*',
'172.22.*',
'172.23.*',
'172.24.*',
'172.25.*',
'172.26.*',
'172.27.*',
'172.28.*',
'172.29.*',
'172.30.*',
'172.31.*',
'192.168.*'
]
// '<-loopback>'
let config = null
async function _winUnsetProxy (exec, setEnv) {
// eslint-disable-next-line no-constant-condition
@ -59,14 +31,23 @@ async function _winUnsetProxy (exec, setEnv) {
}
})
} catch (e) {
log.error(e)
log.error('启动系统代理失败:', e)
}
}
async function _winSetProxy (exec, ip, port, setEnv) {
// 延迟加载config
if (config == null) {
config = require('../../../config.js')
}
let lanIpStr = ''
for (const string of _lanIP) {
lanIpStr += string + ';'
for (const excludeIpPattern of config.get().proxy.excludeIpList) {
// 跳过起注释作用的数据
if (excludeIpPattern.indexOf('#') >= 0) {
continue
}
lanIpStr += excludeIpPattern + ';'
}
// http=127.0.0.1:8888;https=127.0.0.1:8888 考虑这种方式
const proxyPath = extraPath.getProxyExePath()

88
packages/core/test/mergeTest.js

@ -0,0 +1,88 @@
const lodash = require('lodash')
const mergeApi = require('../src/merge.js')
// 默认配置
const defConfig = {
a: {
aa: { value: 1 },
bb: { value: 2 }
},
b: { c: 2 },
c: 1,
d: [1, 2, 3],
e: {
aa: 2,
ee: 5
},
f: {
x: 1
},
g: [1, 2],
h: null,
i: null
}
// 自定义配置
const customConfig = {
a: {
bb: { value: 2 },
cc: { value: 3 }
},
b: { c: 2 },
c: null,
d: [1, 2, 3, 4],
e: {
aa: 2,
ee: 5,
ff: 6
},
f: {},
g: [1, 2],
h: null
}
// doDiff
const doDiffResult = mergeApi.doDiff(defConfig, customConfig)
console.log('doDiffResult:', JSON.stringify(doDiffResult, null, 2))
console.log('\r')
// 校验doDiff结果
const doDiffExpect = {
a: {
aa: null,
cc: { value: 3 }
},
c: null,
d: [1, 2, 3, 4],
e: {
ff: 6
},
f: {
x: null
}
}
console.log('check diff result:', lodash.isEqual(doDiffResult, doDiffExpect))
console.log('\r')
// doMerge
const doMergeResult = mergeApi.doMerge(defConfig, doDiffResult)
// delete null item
mergeApi.deleteNullItems(doMergeResult)
console.log('running:', JSON.stringify(doMergeResult, null, 2))
// 校验doMerge结果
const doMergeExpect = {
a: {
bb: { value: 2 },
cc: { value: 3 }
},
b: { c: 2 },
d: [1, 2, 3, 4],
e: {
aa: 2,
ee: 5,
ff: 6
},
f: {},
g: [1, 2]
}
console.log('check merge result:', lodash.isEqual(doMergeResult, doMergeExpect))
console.log('\r')

6
packages/gui/src/bridge/update/front.js

@ -63,14 +63,10 @@ function install (app, api) {
function openGithubUrl () {
api.ipc.openExternal('https://github.com/docmirror/dev-sidecar/releases')
}
function openGiteeUrl () {
api.ipc.openExternal('https://gitee.com/docmirror/dev-sidecar/releases')
}
return <div>
<div>请前往github或gitee项目release页面下载新版本手动安装</div>
<div>请前往github项目release页面下载新版本手动安装</div>
<ol>
<li><a onClick={openGithubUrl}>Github release</a></li>
<li><a onClick={openGiteeUrl}>Gitee release</a></li>
</ol>
</div>
}

2
packages/gui/src/view/api.js

@ -26,7 +26,7 @@ export function apiInit (app) {
},
invoke,
send,
openExternal (href) {
async openExternal (href) {
shell.openExternal(href)
},
openPath (file) {

4
packages/gui/src/view/components/setup-ca.vue

@ -13,7 +13,7 @@
<template slot="title">
{{title}}
<a-button type="primary" style="float:right" @click="doSetup()">点此去安装</a-button>
<a-button style="float:right;margin-right:10px;" @click="openExternal('https://gitee.com/docmirror/dev-sidecar/blob/master/doc/caroot.md')">为什么要安装证书</a-button>
<a-button style="float:right;margin-right:10px;" @click="openExternal('https://github.com/docmirror/dev-sidecar/blob/master/doc/caroot.md')">为什么要安装证书</a-button>
</template>
<div>
<b>本应用在非安全模式下必须安装和信任CA根证书</b>该证书是应用启动时本地随机生成的<br/>
@ -73,7 +73,7 @@ export default {
}
},
methods: {
openExternal (url) {
async openExternal (url) {
this.$api.ipc.openExternal(url)
},
afterVisibleChange (val) {

4
packages/gui/src/view/pages/index.vue

@ -95,8 +95,6 @@
<div>如果它解决了你的问题请不要吝啬你的star哟点这里
<a-icon style="margin-right:10px;" type="arrow-right" 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>
</div>
@ -335,7 +333,7 @@ export default {
doCheckUpdate (fromUser = true) {
this.$api.update.checkForUpdate(fromUser)
},
openExternal (url) {
async openExternal (url) {
this.$api.ipc.openExternal(url)
},
onShutdownTipClose (e) {

2
packages/gui/src/view/pages/plugin/overwall.vue

@ -105,7 +105,7 @@ export default {
mounted () {
},
methods: {
openExternal (url) {
async openExternal (url) {
this.$api.ipc.openExternal(url)
},
async applyAfter () {

35
packages/gui/src/view/pages/proxy.vue

@ -17,6 +17,9 @@
<a-tag v-else color="red">
当前未启动
</a-tag>
<div class="form-help">
<a @click="openExternal('https://github.com/docmirror/dev-sidecar/blob/master/doc/recover.md')">卸载与恢复网络说明</a>
</div>
</a-form-item>
<a-form-item v-if="isWindows()" label="设置环境变量" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-checkbox v-model="config.proxy.setEnv" >
@ -29,6 +32,26 @@
<a-button @click="loopbackVisible=true">去设置</a-button>
<div class="form-help">解决OneNoteMicrosoftStoreOutlook等UWP应用开启代理后无法访问网络的问题</div>
</a-form-item>
<a-form-item label="排除地址配置" :label-col="labelCol" :wrapper-col="wrapperCol">
<div>
<a-row :gutter="10">
<a-col :span="22">
<span>访问的域名或IP符合下列格式时将跳过系统代理</span>
</a-col>
<a-col :span="2">
<a-button type="primary" icon="plus" @click="addExcludeIp()" />
</a-col>
</a-row>
<a-row :gutter="10" v-for="(item,index) of getProxyConfig().excludeIpList" :key='index'>
<a-col :span="22">
<a-input v-model="getProxyConfig().excludeIpList[index]"></a-input>
</a-col>
<a-col :span="2">
<a-button type="danger" icon="minus" @click="delExcludeIp(item,index)" />
</a-col>
</a-row>
</div>
</a-form-item>
</div>
<template slot="footer">
<div class="footer-bar">
@ -78,6 +101,9 @@ export default {
mounted () {
},
methods: {
async openExternal (url) {
this.$api.ipc.openExternal(url)
},
async applyAfter () {
await this.$api.proxy.restart()
},
@ -91,6 +117,15 @@ export default {
}
this.$message.error('打开失败:' + e.message)
}
},
getProxyConfig () {
return this.config.proxy
},
addExcludeIp () {
this.getProxyConfig().excludeIpList.unshift('')
},
delExcludeIp (item, index) {
this.getProxyConfig().excludeIpList.splice(index, 1)
}
}
}

4
packages/gui/src/view/pages/setting.vue

@ -12,7 +12,7 @@
本应用开机自启
</a-checkbox>
<div class="form-help">
windows下建议开启开机自启<a @click="openExternal('https://gitee.com/docmirror/dev-sidecar/blob/master/doc/recover.md')">更多说明参考</a>
windows下建议开启开机自启<a @click="openExternal('https://github.com/docmirror/dev-sidecar/blob/master/doc/recover.md')">更多说明参考</a>
</div>
</a-form-item>
<a-form-item v-if="systemPlatform ==='mac'" label="隐藏Dock图标" :label-col="labelCol" :wrapper-col="wrapperCol">
@ -93,7 +93,7 @@ export default {
mounted () {
},
methods: {
openExternal (url) {
async openExternal (url) {
this.$api.ipc.openExternal(url)
},
onAutoStartChange () {

6
packages/mitmproxy/src/lib/proxy/mitmproxy/createRequestHandler.js

@ -17,7 +17,11 @@ module.exports = function createRequestHandler (createIntercepts, middlewares, e
const rOptions = commonUtil.getOptionsFormRequest(req, ssl, externalProxy)
rOptions.agent.options.rejectUnauthorized = setting.verifySsl
if (rOptions.agent) {
rOptions.agent.options.rejectUnauthorized = setting.verifySsl
} else if (rOptions.agent !== false) {
log.error('rOptions.agent 的值有问题:', rOptions)
}
if (rOptions.headers.connection === 'close') {
req.socket.setKeepAlive(false)

3
packages/mitmproxy/src/options.js

@ -16,6 +16,9 @@ module.exports = (config) => {
if (!setting.script.dirAbsolutePath) {
setting.script.dirAbsolutePath = path.join(setting.rootDir, setting.script.defaultDir)
}
if (setting.verifySsl !== false) {
setting.verifySsl = true
}
const overwallConfig = serverConfig.plugin.overwall
if (!overwallConfig.pac.pacFileAbsolutePath) {

5
script/1、installEnv.bat

@ -0,0 +1,5 @@
cd ../
npm install cnpm -g --registry=https://registry.npm.taobao.org
cnpm install lerna@6 -g
cnpm install phantomjs -g

3
script/2、installBootstrap.bat

@ -0,0 +1,3 @@
cd ../
lerna bootstrap

3
script/3、buildAndRun.bat

@ -0,0 +1,3 @@
cd ../packages/gui
npm run electron

3
script/4、generateSetupFile.bat

@ -0,0 +1,3 @@
cd ../packages/gui
npm run electron:build

57
test/mergeTest.mjs

@ -1,57 +0,0 @@
import lodash from "lodash";
const defConfig = {a:{aa:1,bb:2},b:{c:2},d:[1,2,3],e:{ee:1,aa:2}}
const newConfig = {a:{aa:1,bb:2},d:[5],e:{bb:2,ee:2,aa:2}}
const result = { d: [ 5 ],e:{ee:2} }
const load = {a:1,d:[5,1,2,3]}
const DELETE = '____DELETE____'
// lodash.mergeWith(defConfig,newConfig, (objValue, srcValue, key, object, source, stack) => {
// console.log('stack', stack,'key',key)
//
// if (lodash.isArray(srcValue)) {
// return srcValue
// }
// if(lodash.isEqual(objValue,srcValue)){
// //如何删除
// return DELETE
// }
// })
function doMerge (defObj, newObj) {
const defObj2 = { ...defObj }
const newObj2 = {}
lodash.forEach(newObj,(newValue,key)=>{
// const newValue = newObj[key]
const defValue = defObj[key]
if (lodash.isEqual(newValue, defValue)) {
delete defObj2[key]
return
}
if (lodash.isArray(newValue)) {
delete defObj2[key]
newObj2[key] = newValue
return
}
if (lodash.isObject(newValue)) {
newObj2[key] = doMerge(defValue, newValue)
delete defObj2[key]
} else {
// 基础类型,直接覆盖
delete defObj2[key]
newObj2[key] = newValue
}
})
// defObj 里面剩下的是被删掉的
lodash.forEach(defObj2, (defValue, key) => {
newObj2[key] = null
})
return newObj2
}
console.log(doMerge(defConfig,newConfig))
Loading…
Cancel
Save