Merge pull request #274 from wangliang181230/feature/sysproxy/excludeIpList
1)系统代理排除列表可配置化;2)启/禁用远程配置功能增强;3)添加重载远程配置功能;4)修复3个BUG;5)几处功能优化;6)配置调整;7)文档调整。pull/276/head
						commit
						3594c68f7b
					
				| 
						 | 
				
			
			@ -6,4 +6,6 @@ gen
 | 
			
		|||
node_modules/
 | 
			
		||||
*.lock
 | 
			
		||||
*.log
 | 
			
		||||
pnpm-lock.yaml
 | 
			
		||||
pnpm-lock.yaml
 | 
			
		||||
package-lock.json
 | 
			
		||||
*.lnk
 | 
			
		||||
							
								
								
									
										101
									
								
								README.md
								
								
								
								
							
							
						
						
									
										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版安装需要在“系统偏好设置->安全性与隐私->通用”中解锁并允许应用安装
 | 
			
		||||
 | 
			
		||||
     
 | 
			
		||||
 | 
			
		||||
#### 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存在,就 `有可能` 可以直连访问。
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
### 默认模式
 | 
			
		||||
### 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系统使用时,首页的系统代理开关无法打开
 | 
			
		||||
#### 1)Mac系统使用时,首页的系统代理开关无法打开
 | 
			
		||||
出现这个问题可能是没有开启系统代理命令的执行权限   
 | 
			
		||||
```
 | 
			
		||||
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、浏览器打开提示证书不受信任
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
一般是证书安装位置不对,重新安装证书后,重启浏览器
 | 
			
		||||
 | 
			
		||||
#### 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、查看日志是否有报错
 | 
			
		||||
 如果还是不行,请在下方加作者好友,将服务日志发送给作者进行分析             
 | 
			
		||||
 日志打开方式:加速服务->右边日志按钮->打开日志文件夹    
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,6 @@ DevSidecar在第一次启动时会在本地随机生成一份根证书,当有
 | 
			
		|||
 | 
			
		||||
> 对于应用来源风险:    
 | 
			
		||||
> 请勿从未知网站下载DevSidecar应用,认准官方版本发布地址  
 | 
			
		||||
> [Gitee Release](https://gitee.com/docmirror/dev-sidecar/releases)  
 | 
			
		||||
> [Github Release](https://github.com/docmirror/dev-sidecar/releases)
 | 
			
		||||
> 
 | 
			
		||||
> 或者从源码自行编译安装
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -6,80 +6,32 @@ 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
 | 
			
		||||
}
 | 
			
		||||
function _getRemoteSavePath () {
 | 
			
		||||
function _getRemoteSavePath (prefix = '', version = '') {
 | 
			
		||||
  const dir = getDefaultConfigBasePath()
 | 
			
		||||
  if (!fs.existsSync(dir)) {
 | 
			
		||||
    fs.mkdirSync(dir)
 | 
			
		||||
  }
 | 
			
		||||
  return path.join(dir, 'remote_config.json5')
 | 
			
		||||
  return path.join(dir, prefix + 'remote_config.json' + version)
 | 
			
		||||
}
 | 
			
		||||
function _getConfigPath () {
 | 
			
		||||
  const dir = getDefaultConfigBasePath()
 | 
			
		||||
  if (!fs.existsSync(dir)) {
 | 
			
		||||
    fs.mkdirSync(dir)
 | 
			
		||||
  }
 | 
			
		||||
  return dir + '/config.json5'
 | 
			
		||||
  return dir + '/config.json'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,25 @@ const configApi = {
 | 
			
		|||
          return
 | 
			
		||||
        }
 | 
			
		||||
        if (response && response.statusCode === 200) {
 | 
			
		||||
          fs.writeFileSync(_getRemoteSavePath(), body)
 | 
			
		||||
          const originalRemoteSavePath = _getRemoteSavePath('original_', '5')
 | 
			
		||||
          fs.writeFileSync(originalRemoteSavePath, body)
 | 
			
		||||
          log.info('保存原来的远程配置文件成功:', originalRemoteSavePath)
 | 
			
		||||
 | 
			
		||||
          // 尝试解析远程配置,如果解析失败,则不保存它
 | 
			
		||||
          let remoteConfig
 | 
			
		||||
          try {
 | 
			
		||||
            remoteConfig = JSON5.parse(body)
 | 
			
		||||
          } catch (e) {
 | 
			
		||||
            log.error('远程配置内容格式不正确:', body)
 | 
			
		||||
            remoteConfig = null
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (remoteConfig != null) {
 | 
			
		||||
            const remoteSavePath = _getRemoteSavePath()
 | 
			
		||||
            fs.writeFileSync(remoteSavePath, JSON.stringify(remoteConfig, null, '\t'))
 | 
			
		||||
            log.info('保存远程配置文件成功:', remoteSavePath)
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          resolve()
 | 
			
		||||
        } else {
 | 
			
		||||
          const message = '下载远程配置失败:' + response.message + ',code:' + response.statusCode
 | 
			
		||||
| 
						 | 
				
			
			@ -128,48 +98,82 @@ 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 {}
 | 
			
		||||
  },
 | 
			
		||||
  readRemoteConfigStr () {
 | 
			
		||||
    if (get().app.remoteConfig.enabled !== true) {
 | 
			
		||||
      return '{}'
 | 
			
		||||
    }
 | 
			
		||||
    try {
 | 
			
		||||
      const path = _getRemoteSavePath()
 | 
			
		||||
      if (fs.existsSync(path)) {
 | 
			
		||||
        log.info('读取远程配置文件内容:', path)
 | 
			
		||||
        const file = fs.readFileSync(path)
 | 
			
		||||
        return file.toString()
 | 
			
		||||
      } else {
 | 
			
		||||
        log.warn('远程配置文件不存在:', path)
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      log.warn('远程配置内容读取失败:', e)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return '{}'
 | 
			
		||||
  },
 | 
			
		||||
  /**
 | 
			
		||||
   * 保存自定义的 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())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 计算新配置与默认配置(启用远程配置时,含远程配置)的差异,并保存到 config.json 中
 | 
			
		||||
    const diffConfig = mergeApi.doDiff(defConfig, newConfig)
 | 
			
		||||
    const configPath = _getConfigPath()
 | 
			
		||||
    fs.writeFileSync(configPath, JSON.stringify(diffConfig, null, '\t'))
 | 
			
		||||
    log.info('保存自定义配置文件成功:', configPath)
 | 
			
		||||
 | 
			
		||||
    // 重载配置
 | 
			
		||||
    const allConfig = configApi.reload()
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      diffConfig,
 | 
			
		||||
      allConfig
 | 
			
		||||
    }
 | 
			
		||||
    const saveConfig = doMerge(defConfig, newConfig)
 | 
			
		||||
    fs.writeFileSync(_getConfigPath(), JSON5.stringify(saveConfig, null, 2))
 | 
			
		||||
    configApi.reload()
 | 
			
		||||
    return saveConfig
 | 
			
		||||
  },
 | 
			
		||||
  doMerge,
 | 
			
		||||
  doMerge: mergeApi.doMerge,
 | 
			
		||||
  doDiff: mergeApi.doDiff,
 | 
			
		||||
  /**
 | 
			
		||||
   * 读取后合并配置
 | 
			
		||||
   * 读取 config.json 后,合并配置
 | 
			
		||||
   * @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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -179,21 +183,18 @@ const configApi = {
 | 
			
		|||
  get,
 | 
			
		||||
  set (newConfig) {
 | 
			
		||||
    if (newConfig == null) {
 | 
			
		||||
      return
 | 
			
		||||
      return configTarget
 | 
			
		||||
    }
 | 
			
		||||
    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
 | 
			
		||||
  },
 | 
			
		||||
  getDefault () {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
      '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
 | 
			
		||||
      'qq.com': true,
 | 
			
		||||
      '*.qq.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']
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,50 @@
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    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
 | 
			
		||||
  "proxy": {
 | 
			
		||||
    "excludeIpList": {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "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
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,7 +54,187 @@ module.exports = {
 | 
			
		|||
    name: '系统代理',
 | 
			
		||||
    use: 'local',
 | 
			
		||||
    other: [],
 | 
			
		||||
    setEnv: false
 | 
			
		||||
    setEnv: false,
 | 
			
		||||
    excludeIpList: {
 | 
			
		||||
      // region 常用国内可访问域名
 | 
			
		||||
 | 
			
		||||
      // 中国大陆
 | 
			
		||||
      '*.cn': true,
 | 
			
		||||
      'cn.*': true,
 | 
			
		||||
      '*china*': true,
 | 
			
		||||
 | 
			
		||||
      // 系统之家
 | 
			
		||||
      '*.xitongzhijia.net': true,
 | 
			
		||||
 | 
			
		||||
      // CSDN
 | 
			
		||||
      '*.csdn.net': true,
 | 
			
		||||
 | 
			
		||||
      // 百度
 | 
			
		||||
      '*.baidu.com': true,
 | 
			
		||||
      '*.baiducontent.com': true,
 | 
			
		||||
      '*.bdimg.com': true,
 | 
			
		||||
      '*.bdstatic.com': true,
 | 
			
		||||
      '*.bdydns.com': true,
 | 
			
		||||
 | 
			
		||||
      // 腾讯
 | 
			
		||||
      '*.tencent.com': true,
 | 
			
		||||
      '*.qq.com': true,
 | 
			
		||||
      '*.weixin.com': true,
 | 
			
		||||
      '*.weixinbridge.com': true,
 | 
			
		||||
      '*.wechat.com': true,
 | 
			
		||||
      '*.idqqimg.com': true,
 | 
			
		||||
      '*.gtimg.com': true,
 | 
			
		||||
      '*.qpic.com': true,
 | 
			
		||||
      '*.qlogo.com': true,
 | 
			
		||||
      '*.myapp.com': true,
 | 
			
		||||
      '*.myqcloud.com': true,
 | 
			
		||||
 | 
			
		||||
      // 阿里
 | 
			
		||||
      '*.aliyun.com': true,
 | 
			
		||||
      '*.alipay.com': true,
 | 
			
		||||
      '*.taobao.com': true,
 | 
			
		||||
      '*.tmall.com': true,
 | 
			
		||||
      '*.alipayobjects.com': true,
 | 
			
		||||
      '*.dingtalk.com': true,
 | 
			
		||||
      '*.mmstat.com': true,
 | 
			
		||||
      '*.alicdn.com': true,
 | 
			
		||||
      '*.hdslb.com': true,
 | 
			
		||||
 | 
			
		||||
      // Gitee
 | 
			
		||||
      'gitee.com': true,
 | 
			
		||||
      '*.gitee.com': true,
 | 
			
		||||
      '*.gitee.io': true,
 | 
			
		||||
      '*.giteeusercontent.com': true,
 | 
			
		||||
 | 
			
		||||
      // Mozilla Firefox
 | 
			
		||||
      '*.mozilla.org': true,
 | 
			
		||||
      '*.mozilla.com': true,
 | 
			
		||||
      '*.mozilla.net': true,
 | 
			
		||||
      '*.firefox.com': true,
 | 
			
		||||
      '*.firefox.org': true,
 | 
			
		||||
      '*.mozillademos.org': true,
 | 
			
		||||
      '*.mozillians.org': true,
 | 
			
		||||
      '*.mozillians.net': true,
 | 
			
		||||
      '*.mozillians.com': true,
 | 
			
		||||
 | 
			
		||||
      // OSS
 | 
			
		||||
      '*.sonatype.org': true,
 | 
			
		||||
      // Maven镜像
 | 
			
		||||
      '*.maven.org': true,
 | 
			
		||||
      // Maven Repository
 | 
			
		||||
      '*.mvnrepository.com': true,
 | 
			
		||||
      'challenges.cloudflare.com': true, // 在访问 mvnrepository.com 的人机校验时使用,国内可直接访问,所以不需要代理,代理了反而变慢了。
 | 
			
		||||
 | 
			
		||||
      // 苹果
 | 
			
		||||
      '*.apple.com': true,
 | 
			
		||||
      '*.icloud.com': true,
 | 
			
		||||
 | 
			
		||||
      // 微软
 | 
			
		||||
      '*.microsoft.com': true,
 | 
			
		||||
      '*.windows.com': true,
 | 
			
		||||
      '*.office.com': true,
 | 
			
		||||
      '*.office.net': true,
 | 
			
		||||
      '*.live.com': true,
 | 
			
		||||
      '*.msn.com': true,
 | 
			
		||||
 | 
			
		||||
      // WPS
 | 
			
		||||
      '*.wps.com': true,
 | 
			
		||||
 | 
			
		||||
      // 奇虎
 | 
			
		||||
      '*.qihoo.com': true,
 | 
			
		||||
      '*.qihucdn.com': true,
 | 
			
		||||
      // 360
 | 
			
		||||
      '*.360.com': true,
 | 
			
		||||
      '*.360safe.com': true,
 | 
			
		||||
      '*.360buyimg.com': true,
 | 
			
		||||
      '*.360buy.com': true,
 | 
			
		||||
 | 
			
		||||
      // 京东
 | 
			
		||||
      '*.jd.com': true,
 | 
			
		||||
      '*.jcloud.com': true,
 | 
			
		||||
      '*.jcloudcs.com': true,
 | 
			
		||||
      '*.jcloudcache.com': true,
 | 
			
		||||
      '*.jcloudcdn.com': true,
 | 
			
		||||
      '*.jcloudlb.com': true,
 | 
			
		||||
 | 
			
		||||
      // 哔哩哔哩
 | 
			
		||||
      '*.bilibili.com': true,
 | 
			
		||||
      '*.bilivideo.com.com': true,
 | 
			
		||||
      '*.biliapi.net': true,
 | 
			
		||||
 | 
			
		||||
      // 移动
 | 
			
		||||
      '*.10086.com': true,
 | 
			
		||||
      '*.10086cloud.com': true,
 | 
			
		||||
 | 
			
		||||
      // 移动:139邮箱
 | 
			
		||||
      '*.139.com': true,
 | 
			
		||||
 | 
			
		||||
      // 迅雷
 | 
			
		||||
      '*.xunlei.com': true,
 | 
			
		||||
 | 
			
		||||
      // 网站ICP备案查询
 | 
			
		||||
      '*.icpapi.com': true,
 | 
			
		||||
 | 
			
		||||
      // AGE动漫
 | 
			
		||||
      '*.agedm.*': true,
 | 
			
		||||
      '*.zhimg.com': true,
 | 
			
		||||
      '*.bdxiguaimg.com': true,
 | 
			
		||||
      '*.toutiaoimg.com': true,
 | 
			
		||||
      '*.bytecdntp.com': true,
 | 
			
		||||
      '*.bytegoofy.com': true,
 | 
			
		||||
      '*.toutiao.com': true,
 | 
			
		||||
      '*.toutiaovod.com': true,
 | 
			
		||||
      '*.aliyuncs.com': true,
 | 
			
		||||
      '*.127.net': true,
 | 
			
		||||
      '43.240.74.134': true,
 | 
			
		||||
 | 
			
		||||
      // ZzzFun
 | 
			
		||||
      '*.zzzfun.one': true,
 | 
			
		||||
      '*.zzzfun.vip': true,
 | 
			
		||||
 | 
			
		||||
      // 必应
 | 
			
		||||
      '*.bing.com': true,
 | 
			
		||||
 | 
			
		||||
      // 我的个人域名
 | 
			
		||||
      '*.easyj.icu': true,
 | 
			
		||||
 | 
			
		||||
      // 未知公司
 | 
			
		||||
      '*.bcebos.com': true,
 | 
			
		||||
      'icannwiki.org': true,
 | 
			
		||||
      '*.icannwiki.org': true,
 | 
			
		||||
      '*.sectigo.com': true,
 | 
			
		||||
      '*.pingdom.net': true,
 | 
			
		||||
 | 
			
		||||
      // endregion
 | 
			
		||||
 | 
			
		||||
      // 本地地址,无需代理
 | 
			
		||||
      localhost: true,
 | 
			
		||||
      'localhost.*': true, // 部分VPN会在host中添加这种格式的域名指向127.0.0.1,所以也排除掉
 | 
			
		||||
      '127.*': true,
 | 
			
		||||
      'test.*': true, // 本地开发时,测试用的虚拟域名格式,无需代理
 | 
			
		||||
 | 
			
		||||
      // 服务器端常用地址,无需代理
 | 
			
		||||
      '10.*': true,
 | 
			
		||||
      '172.16.*': true,
 | 
			
		||||
      '172.17.*': true,
 | 
			
		||||
      '172.18.*': true,
 | 
			
		||||
      '172.19.*': true,
 | 
			
		||||
      '172.20.*': true,
 | 
			
		||||
      '172.21.*': true,
 | 
			
		||||
      '172.22.*': true,
 | 
			
		||||
      '172.23.*': true,
 | 
			
		||||
      '172.24.*': true,
 | 
			
		||||
      '172.25.*': true,
 | 
			
		||||
      '172.26.*': true,
 | 
			
		||||
      '172.27.*': true,
 | 
			
		||||
      '172.28.*': true,
 | 
			
		||||
      '172.29.*': true,
 | 
			
		||||
      '172.30.*': true,
 | 
			
		||||
      '172.31.*': true,
 | 
			
		||||
 | 
			
		||||
      // 局域网地址,无需代理
 | 
			
		||||
      '192.168.*': true
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  status: {
 | 
			
		||||
    enabled: false,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,9 +69,10 @@ 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, JSON.stringify(serverConfig, null, '\t'))
 | 
			
		||||
    log.info('保存运行时配置文件成功:', runningConfigPath)
 | 
			
		||||
    const serverProcess = fork(mitmproxyPath, [runningConfigPath])
 | 
			
		||||
    server = {
 | 
			
		||||
      id: serverProcess.pid,
 | 
			
		||||
      process: serverProcess,
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +107,7 @@ const serverApi = {
 | 
			
		|||
        event.fire('speed', msg.event)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    return { port: runningConfig.port }
 | 
			
		||||
    return { port: serverConfig.port }
 | 
			
		||||
  },
 | 
			
		||||
  async kill () {
 | 
			
		||||
    if (server) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -53,24 +25,31 @@ async function _winUnsetProxy (exec, setEnv) {
 | 
			
		|||
    regKey.get('HTTPS_PROXY', (err) => {
 | 
			
		||||
      if (!err) {
 | 
			
		||||
        regKey.remove('HTTPS_PROXY', async (err) => {
 | 
			
		||||
          log.info('删除环境变量https_proxy', err)
 | 
			
		||||
          log.warn('删除环境变量https_proxy失败:', err)
 | 
			
		||||
          await exec('setx DS_REFRESH "1"')
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    log.error(e)
 | 
			
		||||
    log.error('启动系统代理失败:', e)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _winSetProxy (exec, ip, port, setEnv) {
 | 
			
		||||
  let lanIpStr = ''
 | 
			
		||||
  for (const string of _lanIP) {
 | 
			
		||||
    lanIpStr += string + ';'
 | 
			
		||||
  // 延迟加载config
 | 
			
		||||
  if (config == null) {
 | 
			
		||||
    config = require('../../../config.js')
 | 
			
		||||
  }
 | 
			
		||||
  // http=127.0.0.1:8888;https=127.0.0.1:8888 考虑这种方式
 | 
			
		||||
 | 
			
		||||
  let excludeIpStr = ''
 | 
			
		||||
  for (const ip in config.get().proxy.excludeIpList) {
 | 
			
		||||
    if (config.get().proxy.excludeIpList[ip] === true) {
 | 
			
		||||
      excludeIpStr += ip + ';'
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const proxyPath = extraPath.getProxyExePath()
 | 
			
		||||
  await execFile(proxyPath, ['global', `http=http://${ip}:${port};https=http://${ip}:${port}`, lanIpStr])
 | 
			
		||||
  await execFile(proxyPath, ['global', `http=http://${ip}:${port};https=http://${ip}:${port}`, excludeIpStr])
 | 
			
		||||
 | 
			
		||||
  if (setEnv) {
 | 
			
		||||
    log.info('同时设置 https_proxy')
 | 
			
		||||
| 
						 | 
				
			
			@ -96,8 +75,7 @@ const executor = {
 | 
			
		|||
      return _winUnsetProxy(exec, setEnv)
 | 
			
		||||
    } else {
 | 
			
		||||
      // 设置代理
 | 
			
		||||
 | 
			
		||||
      log.info('设置代理', ip, port, setEnv)
 | 
			
		||||
      log.info('设置代理:', ip, port, setEnv)
 | 
			
		||||
      return _winSetProxy(exec, ip, port, setEnv)
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,9 +3,11 @@ const server = require('@docmirror/mitmproxy')
 | 
			
		|||
const JSON5 = require('json5')
 | 
			
		||||
const path = require('path')
 | 
			
		||||
const home = process.env.USER_HOME || process.env.HOME || 'C:/Users/Administrator/'
 | 
			
		||||
let configPath = path.join(home, '.dev-sidecar/running.json')
 | 
			
		||||
let configPath
 | 
			
		||||
if (process.argv && process.argv.length > 3) {
 | 
			
		||||
  configPath = process.argv[2]
 | 
			
		||||
} else {
 | 
			
		||||
  configPath = path.join(home, '.dev-sidecar/running.json')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const fs = require('fs')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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')
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +64,8 @@ const localApi = {
 | 
			
		|||
    },
 | 
			
		||||
    save (setting = {}) {
 | 
			
		||||
      const settingPath = _getSettingsPath()
 | 
			
		||||
      fs.writeFileSync(settingPath, JSON5.stringify(setting, null, 2))
 | 
			
		||||
      fs.writeFileSync(settingPath, JSON.stringify(setting, null, '\t'))
 | 
			
		||||
      log.info('保存setting配置文件成功', settingPath)
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +109,7 @@ function _getSettingsPath () {
 | 
			
		|||
  if (!fs.existsSync(dir)) {
 | 
			
		||||
    fs.mkdirSync(dir)
 | 
			
		||||
  }
 | 
			
		||||
  return dir + '/setting.json5'
 | 
			
		||||
  return dir + '/setting.json'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function invoke (api, param) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,8 +26,8 @@ export function apiInit (app) {
 | 
			
		|||
      },
 | 
			
		||||
      invoke,
 | 
			
		||||
      send,
 | 
			
		||||
      openExternal (href) {
 | 
			
		||||
        shell.openExternal(href)
 | 
			
		||||
      async openExternal (href) {
 | 
			
		||||
        await shell.openExternal(href)
 | 
			
		||||
      },
 | 
			
		||||
      openPath (file) {
 | 
			
		||||
        shell.openPath(file)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -190,7 +190,7 @@ export default {
 | 
			
		|||
    },
 | 
			
		||||
    doSave () {
 | 
			
		||||
      return api.config.save(this.targetConfig).then(ret => {
 | 
			
		||||
        this.$emit('change', ret)
 | 
			
		||||
        this.$emit('change', ret.diffConfig)
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    deleteDnsMapping (item, index) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,8 +73,8 @@ export default {
 | 
			
		|||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    openExternal (url) {
 | 
			
		||||
      this.$api.ipc.openExternal(url)
 | 
			
		||||
    async openExternal (url) {
 | 
			
		||||
      await this.$api.ipc.openExternal(url)
 | 
			
		||||
    },
 | 
			
		||||
    afterVisibleChange (val) {
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ export default {
 | 
			
		|||
  },
 | 
			
		||||
  data () {
 | 
			
		||||
    return {
 | 
			
		||||
      key: undefined,
 | 
			
		||||
      config: undefined,
 | 
			
		||||
      status: {},
 | 
			
		||||
      labelCol: { span: 4 },
 | 
			
		||||
| 
						 | 
				
			
			@ -31,16 +32,19 @@ export default {
 | 
			
		|||
      this.status = this.$status
 | 
			
		||||
 | 
			
		||||
      const config = await this.$api.config.reload()
 | 
			
		||||
      this.$set(this, 'config', config)
 | 
			
		||||
      this.setConfig(config)
 | 
			
		||||
      this.systemPlatform = await this.$api.info.getSystemPlatform()
 | 
			
		||||
      console.log('config', this.config, this.systemPlatform)
 | 
			
		||||
      // eslint-disable-next-line no-debugger
 | 
			
		||||
 | 
			
		||||
      this.printConfig()
 | 
			
		||||
 | 
			
		||||
      if (this.ready) {
 | 
			
		||||
        return this.ready(this.config)
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    async apply () {
 | 
			
		||||
      if (this.applyLoading === true) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      this.applyLoading = true
 | 
			
		||||
      await this.applyBefore()
 | 
			
		||||
      await this.saveConfig()
 | 
			
		||||
| 
						 | 
				
			
			@ -71,8 +75,11 @@ export default {
 | 
			
		|||
      })
 | 
			
		||||
    },
 | 
			
		||||
    saveConfig () {
 | 
			
		||||
      return this.$api.config.save(this.config).then(() => {
 | 
			
		||||
      return this.$api.config.save(this.config).then((ret) => {
 | 
			
		||||
        this.$message.info('设置已保存')
 | 
			
		||||
        this.setConfig(ret.allConfig)
 | 
			
		||||
        this.printConfig('after saveConfig(), ')
 | 
			
		||||
        return ret
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    getConfig (key) {
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +89,12 @@ export default {
 | 
			
		|||
      }
 | 
			
		||||
      return value
 | 
			
		||||
    },
 | 
			
		||||
    setConfig (newConfig) {
 | 
			
		||||
      this.$set(this, 'config', newConfig)
 | 
			
		||||
    },
 | 
			
		||||
    printConfig (prefix = '') {
 | 
			
		||||
      console.log(`${prefix}${this.key} page config:`, this.config, this.systemPlatform)
 | 
			
		||||
    },
 | 
			
		||||
    getStatus (key) {
 | 
			
		||||
      const value = lodash.get(this.status, key)
 | 
			
		||||
      if (value == null) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,8 +333,8 @@ export default {
 | 
			
		|||
    doCheckUpdate (fromUser = true) {
 | 
			
		||||
      this.$api.update.checkForUpdate(fromUser)
 | 
			
		||||
    },
 | 
			
		||||
    openExternal (url) {
 | 
			
		||||
      this.$api.ipc.openExternal(url)
 | 
			
		||||
    async openExternal (url) {
 | 
			
		||||
      await this.$api.ipc.openExternal(url)
 | 
			
		||||
    },
 | 
			
		||||
    onShutdownTipClose (e) {
 | 
			
		||||
      this.$confirm({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -105,8 +105,8 @@ export default {
 | 
			
		|||
  mounted () {
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    openExternal (url) {
 | 
			
		||||
      this.$api.ipc.openExternal(url)
 | 
			
		||||
    async openExternal (url) {
 | 
			
		||||
      await this.$api.ipc.openExternal(url)
 | 
			
		||||
    },
 | 
			
		||||
    async applyAfter () {
 | 
			
		||||
      if (this.status.server.enabled) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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">解决OneNote、MicrosoftStore、Outlook等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 excludeIpList" :key='index'>
 | 
			
		||||
            <a-col :span="22">
 | 
			
		||||
              <a-input :disabled="item.value === false" v-model="item.key"></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">
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +77,7 @@
 | 
			
		|||
        <div>1、此设置用于解决OneNote、MicrosoftStore、Outlook等UWP应用无法访问网络的问题。</div>
 | 
			
		||||
        <div>2、点击右上方按钮,打开EnableLoopback,然后按下图所示操作即可</div>
 | 
			
		||||
        <div>3、注意:此操作需要<b style="color:red">DevSidecar以管理员身份启动</b>,才能打开下面的EnableLoopback设置界面</div>
 | 
			
		||||
        <img style="margin-top:20px;border:1px solid #eee" width="80%" src="loopback.png" />
 | 
			
		||||
        <img style="margin-top:20px;border:1px solid #eee" width="80%" src="loopback.png"/>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </a-drawer>
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +93,8 @@ export default {
 | 
			
		|||
  data () {
 | 
			
		||||
    return {
 | 
			
		||||
      key: 'proxy',
 | 
			
		||||
      loopbackVisible: false
 | 
			
		||||
      loopbackVisible: false,
 | 
			
		||||
      excludeIpList: []
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  async created () {
 | 
			
		||||
| 
						 | 
				
			
			@ -78,6 +102,15 @@ export default {
 | 
			
		|||
  mounted () {
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    async openExternal (url) {
 | 
			
		||||
      await this.$api.ipc.openExternal(url)
 | 
			
		||||
    },
 | 
			
		||||
    ready () {
 | 
			
		||||
      this.initExcludeIpList()
 | 
			
		||||
    },
 | 
			
		||||
    async applyBefore () {
 | 
			
		||||
      this.submitExcludeIpList()
 | 
			
		||||
    },
 | 
			
		||||
    async applyAfter () {
 | 
			
		||||
      await this.$api.proxy.restart()
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +124,33 @@ export default {
 | 
			
		|||
        }
 | 
			
		||||
        this.$message.error('打开失败:' + e.message)
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    getProxyConfig () {
 | 
			
		||||
      return this.config.proxy
 | 
			
		||||
    },
 | 
			
		||||
    initExcludeIpList () {
 | 
			
		||||
      this.excludeIpList = []
 | 
			
		||||
      for (const key in this.config.proxy.excludeIpList) {
 | 
			
		||||
        const value = this.config.proxy.excludeIpList[key]
 | 
			
		||||
        this.excludeIpList.push({
 | 
			
		||||
          key, value
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    addExcludeIp () {
 | 
			
		||||
      this.excludeIpList.unshift({ key: '', value: true })
 | 
			
		||||
    },
 | 
			
		||||
    delExcludeIp (item, index) {
 | 
			
		||||
      this.excludeIpList.splice(index, 1)
 | 
			
		||||
    },
 | 
			
		||||
    submitExcludeIpList () {
 | 
			
		||||
      const excludeIpList = {}
 | 
			
		||||
      for (const item of this.excludeIpList) {
 | 
			
		||||
        if (item.key) {
 | 
			
		||||
          excludeIpList[item.key] = item.value
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.config.proxy.excludeIpList = excludeIpList
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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">
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +35,13 @@
 | 
			
		|||
      <a-form-item label="远程配置地址" :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
        <a-input v-model="config.app.remoteConfig.url"></a-input>
 | 
			
		||||
      </a-form-item>
 | 
			
		||||
      <a-form-item label="重载远程配置" :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
        <a-button :disabled="config.app.remoteConfig.enabled === false" :loading="reloadLoading" icon="sync" @click="reloadRemoteConfig()">重载远程配置</a-button>
 | 
			
		||||
        <div class="form-help">
 | 
			
		||||
          注意,部分远程配置文件所在站点,修改内容后可能需要等待一段时间才能生效。
 | 
			
		||||
          <br/>如果重载远程配置后发现下载的还是修改前的内容,请稍等片刻再重试。
 | 
			
		||||
        </div>
 | 
			
		||||
      </a-form-item>
 | 
			
		||||
      <a-form-item  label="首页提示" :label-col="labelCol" :wrapper-col="wrapperCol">
 | 
			
		||||
        <a-radio-group v-model="config.app.showShutdownTip"
 | 
			
		||||
                       default-value="true" button-style="solid">
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +91,8 @@ export default {
 | 
			
		|||
  mixins: [Plugin],
 | 
			
		||||
  data () {
 | 
			
		||||
    return {
 | 
			
		||||
      key: 'app'
 | 
			
		||||
      key: 'app',
 | 
			
		||||
      reloadLoading: false
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  created () {
 | 
			
		||||
| 
						 | 
				
			
			@ -93,16 +101,55 @@ export default {
 | 
			
		|||
  mounted () {
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    openExternal (url) {
 | 
			
		||||
      this.$api.ipc.openExternal(url)
 | 
			
		||||
    async openExternal (url) {
 | 
			
		||||
      await this.$api.ipc.openExternal(url)
 | 
			
		||||
    },
 | 
			
		||||
    onAutoStartChange () {
 | 
			
		||||
      this.$api.autoStart.enabled(this.config.app.autoStart.enabled)
 | 
			
		||||
      this.saveConfig()
 | 
			
		||||
    },
 | 
			
		||||
    onRemoteConfigEnabledChange () {
 | 
			
		||||
      this.saveConfig()
 | 
			
		||||
      this.$message.info('请重启加速服务')
 | 
			
		||||
    async reloadAndRestart () {
 | 
			
		||||
      this.$api.config.reload()
 | 
			
		||||
      if (this.status.server.enabled || this.status.proxy.enabled) {
 | 
			
		||||
        await this.$api.proxy.restart()
 | 
			
		||||
        await this.$api.server.restart()
 | 
			
		||||
        this.$message.info('代理服务和系统代理重启成功')
 | 
			
		||||
      } else {
 | 
			
		||||
        this.$message.info('代理服务和系统代理未启动,无需重启')
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    async onRemoteConfigEnabledChange () {
 | 
			
		||||
      await this.saveConfig()
 | 
			
		||||
      if (this.config.app.remoteConfig.enabled === true) {
 | 
			
		||||
        this.reloadLoading = true
 | 
			
		||||
        this.$message.info('开始下载远程配置')
 | 
			
		||||
        await this.$api.config.downloadRemoteConfig()
 | 
			
		||||
        this.$message.info('下载远程配置成功,开始重启代理服务和系统代理')
 | 
			
		||||
        await this.reloadAndRestart()
 | 
			
		||||
        this.reloadLoading = false
 | 
			
		||||
      } else {
 | 
			
		||||
        this.$message.info('开始重启代理服务和系统代理')
 | 
			
		||||
        await this.reloadAndRestart()
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    async reloadRemoteConfig () {
 | 
			
		||||
      this.reloadLoading = true
 | 
			
		||||
 | 
			
		||||
      const remoteConfig = {}
 | 
			
		||||
 | 
			
		||||
      await this.$api.config.readRemoteConfigStr().then((ret) => { remoteConfig.old = ret })
 | 
			
		||||
      await this.$api.config.downloadRemoteConfig()
 | 
			
		||||
      await this.$api.config.readRemoteConfigStr().then((ret) => { remoteConfig.new = ret })
 | 
			
		||||
 | 
			
		||||
      if (remoteConfig.old === remoteConfig.new) {
 | 
			
		||||
        this.$message.info('远程配置没有变化,不做任何处理。')
 | 
			
		||||
        this.$message.info('如果您确实修改了远程配置,请稍等片刻再重试!')
 | 
			
		||||
      } else {
 | 
			
		||||
        this.$message.info('获取到了最新的远程配置,开始重启代理服务和系统代理')
 | 
			
		||||
        await this.reloadAndRestart()
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.reloadLoading = false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
cd ../
 | 
			
		||||
 | 
			
		||||
npm install cnpm -g --registry=https://registry.npm.taobao.org
 | 
			
		||||
cnpm install lerna@6 -g
 | 
			
		||||
cnpm install phantomjs -g
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
cd ../
 | 
			
		||||
 | 
			
		||||
lerna bootstrap
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
cd ../packages/gui
 | 
			
		||||
 | 
			
		||||
npm run electron
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
cd ../packages/gui
 | 
			
		||||
 | 
			
		||||
npm run electron:build
 | 
			
		||||
| 
						 | 
				
			
			@ -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…
	
		Reference in New Issue