refactor: 自动更新
parent
161411f9c6
commit
f0552d5cce
44
README.md
44
README.md
|
@ -9,13 +9,22 @@
|
||||||
### 1、 github的release、source、zip下载加速
|
### 1、 github的release、source、zip下载加速
|
||||||
可解决npm install 时某些安装包下载不下来的问题
|
可解决npm install 时某些安装包下载不下来的问题
|
||||||
|
|
||||||
### 2、 解决git push某些情况下需要临时输入账号密码的问题
|
### 2、 dns优选
|
||||||
通过将api.github.com域名解析到美国服务器
|
根据网络状况智能解析域名ip地址,获取最佳网络速度
|
||||||
|
比如:
|
||||||
|
1. 解决git push 偶尔失败需要输入账号密码的问题
|
||||||
|
2. 解决github头像加载不出来的问题
|
||||||
|
3. 解决gist.github.com访问不到的问题
|
||||||
|
|
||||||
### 3、 github的源代码查看(raw/blame查看)
|
### 3、 github的源代码查看(raw/blame查看)
|
||||||
通过跳转到国内加速链接上
|
通过跳转到国内加速链接上
|
||||||
|
|
||||||
### 4、 Stack Overflow 加速
|
### 4、 Stack Overflow 加速
|
||||||
将ajax.google.com代理到加速代理上 ,recaptcha 加速
|
|
||||||
|
将ajax.google.com代理到加速CDN上
|
||||||
|
recaptcha 图片验证码加速
|
||||||
|
|
||||||
|
|
||||||
### 5、 google cdn 加速
|
### 5、 google cdn 加速
|
||||||
通过代理到加速链接上
|
通过代理到加速链接上
|
||||||
|
|
||||||
|
@ -90,10 +99,16 @@ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keyc
|
||||||
1. yarn 设置淘宝镜像registry
|
1. yarn 设置淘宝镜像registry
|
||||||
2. npm设置官方registry。
|
2. npm设置官方registry。
|
||||||
3. 项目install使用yarn,发布包publish用npm,互不影响
|
3. 项目install使用yarn,发布包publish用npm,互不影响
|
||||||
|
4. 某些库用cnpm也下载不下来的话,可以试试打开dev-sidecar的npm加速
|
||||||
### 其他加速
|
### 其他加速
|
||||||
1. git clone 加速 [fgit-go](https://github.com/FastGitORG/fgit-go)
|
1. git clone 加速
|
||||||
2. github.com代理网站(不能登录) [hub.fastgit.org](https://hub.fastgit.org/)
|
> 使用方式用实际的名称替换{}的内容,即可加速clone
|
||||||
|
> https://hub.fastgit.org/{username}/{reponame}.git
|
||||||
|
> clone 出来的 remote "origin" 为fastgit的地址,需要手动改回来
|
||||||
|
> 你也可以直接使用他们的clone加速工具 [fgit-go](https://github.com/FastGitORG/fgit-go)
|
||||||
|
2. github.com的镜像网站(注意:不能登录)
|
||||||
|
>1. [hub.fastgit.org](https://hub.fastgit.org/)
|
||||||
|
>2. [github.com.cnpmjs.org](https://github.com.cnpmjs.org/) 这个很容易超限
|
||||||
|
|
||||||
|
|
||||||
## api
|
## api
|
||||||
|
@ -128,21 +143,28 @@ const intercepts = {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### DNS配置
|
### DNS优选
|
||||||
某些域名(比如api.github.com)会被解析到新加坡的ip上,新加坡的服务器在上午挺好,到了晚上就卡死,基本不可用。
|
某些域名(比如api.github.com)会被解析到新加坡的ip上,新加坡的服务器在上午挺好,到了晚上就卡死,基本不可用。
|
||||||
所以将这些域名解析到美国服务器上就可以正常访问
|
所以将这些域名解析到美国服务器上就可以正常访问
|
||||||
|
|
||||||
|
另外,配置了dns mapping的域名,将会从dns获取到的ip列表中选择相对快一点的服务器进行访问
|
||||||
|
|
||||||
```js
|
```js
|
||||||
dns: {
|
dns: {
|
||||||
mapping: {
|
mapping: {
|
||||||
// "解决push的时候需要输入密码的问题",
|
//
|
||||||
'api.github.com': 'usa', //配置该域名,使用USA的域名解析服务器
|
'api.github.com': 'usa', // "解决push的时候需要输入密码的问题",
|
||||||
'gist.github.com': 'usa'
|
'gist.github.com': 'usa' // 解决gist无法访问的问题
|
||||||
// "avatars*.githubusercontent.com": "usa"
|
"*.githubusercontent.com": "usa" // 解决github头像经常下载不到的问题
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
注意:暂时只支持IPv4的解析
|
注意:暂时只支持IPv4的解析
|
||||||
|
#### 开启前vs 开启后
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 感谢
|
## 感谢
|
||||||
本项目使用lerna包管理工具
|
本项目使用lerna包管理工具
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
|
@ -1,31 +1,21 @@
|
||||||
{
|
{
|
||||||
"name": "@docmirror/dev-sidecar",
|
"name": "@docmirror/dev-sidecar",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"description": "",
|
"description": "给开发者的加速代理工具",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"depedencies": {},
|
"keywords": ["dev-sidecar","github加速","google加速","代理"],
|
||||||
"keywords": [],
|
|
||||||
"author": "docmirror.cn",
|
"author": "docmirror.cn",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./start",
|
"start": "node ./start"
|
||||||
"serve": "vue-cli-service serve",
|
|
||||||
"build": "vue-cli-service build",
|
|
||||||
"lint": "vue-cli-service lint"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agentkeepalive": "^2.1.1",
|
"agentkeepalive": "^2.1.1",
|
||||||
"babel-core": "^6.8.0",
|
|
||||||
"babel-plugin-transform-async-to-generator": "^6.7.4",
|
|
||||||
"babel-polyfill": "^6.8.0",
|
|
||||||
"babel-preset-es2015": "^6.6.0",
|
|
||||||
"babel-register": "^6.8.0",
|
|
||||||
"charset": "^1.0.0",
|
"charset": "^1.0.0",
|
||||||
"child_process": "^1.0.2",
|
"child_process": "^1.0.2",
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"commander": "^2.9.0",
|
"commander": "^2.9.0",
|
||||||
"core-js": "^3.6.5",
|
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"dns-over-http": "^0.2.0",
|
"dns-over-http": "^0.2.0",
|
||||||
"dns-over-tls": "^0.0.8",
|
"dns-over-tls": "^0.0.8",
|
||||||
|
@ -38,22 +28,17 @@
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"node-cmd": "^3.0.0",
|
"node-cmd": "^3.0.0",
|
||||||
"node-forge": "^0.8.2",
|
"node-forge": "^0.8.2",
|
||||||
"node-mitmproxy": "^3.1.1",
|
|
||||||
"node-powershell": "^4.0.0",
|
"node-powershell": "^4.0.0",
|
||||||
"require-context": "^1.1.0",
|
"require-context": "^1.1.0",
|
||||||
"ssl-root-cas": "^1.3.1",
|
|
||||||
"through2": "^2.0.1",
|
"through2": "^2.0.1",
|
||||||
"tunnel-agent": "^0.4.3",
|
"tunnel-agent": "^0.4.3",
|
||||||
"util": "^0.12.3",
|
"util": "^0.12.3",
|
||||||
"validator": "^13.1.17",
|
"validator": "^13.1.17",
|
||||||
"vue": "^2.6.11",
|
|
||||||
"winreg": "^1.2.4",
|
"winreg": "^1.2.4",
|
||||||
"@docmirror/mitmproxy": "1.0.1"
|
"@docmirror/mitmproxy": "1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
|
||||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
"@vue/cli-service": "~4.5.0",
|
|
||||||
"@vue/eslint-config-standard": "^5.1.2",
|
"@vue/eslint-config-standard": "^5.1.2",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^6.7.2",
|
||||||
|
@ -61,8 +46,7 @@
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-standard": "^4.0.0",
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^6.2.2"
|
||||||
"vue-template-compiler": "^2.6.11"
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"root": true,
|
"root": true,
|
||||||
|
|
|
@ -23,7 +23,7 @@ module.exports = {
|
||||||
'raw.githubusercontent.com': {
|
'raw.githubusercontent.com': {
|
||||||
'.*': { proxy: 'raw.fastgit.org' }
|
'.*': { proxy: 'raw.fastgit.org' }
|
||||||
},
|
},
|
||||||
'github.githubassets.com': {
|
'github11.githubassets.com': {
|
||||||
'.*': { proxy: 'assets.fastgit.org', test: 'https://github.githubassets.com/favicons/favicon.svg' }
|
'.*': { proxy: 'assets.fastgit.org', test: 'https://github.githubassets.com/favicons/favicon.svg' }
|
||||||
},
|
},
|
||||||
'customer-stories-feed.github.com': {
|
'customer-stories-feed.github.com': {
|
||||||
|
@ -100,10 +100,11 @@ module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mapping: {
|
mapping: {
|
||||||
|
'*.github.com': 'usa',
|
||||||
|
'*.githubusercontent.com': 'usa',
|
||||||
|
'*.githubassets.com': 'usa',
|
||||||
// "解决push的时候需要输入密码的问题",
|
// "解决push的时候需要输入密码的问题",
|
||||||
'api.github.com': 'usa',
|
'github.com': 'usa'
|
||||||
'gist.github.com': 'usa'
|
|
||||||
// "avatars*.githubusercontent.com": "usa"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,9 +8,6 @@
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
</head>
|
</head>
|
||||||
<body style="height:100%">
|
<body style="height:100%">
|
||||||
<noscript>
|
|
||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
|
||||||
</noscript>
|
|
||||||
<div id="app" style="height:100%">
|
<div id="app" style="height:100%">
|
||||||
<div style="display: flex;align-items: center;justify-content: center;height:100%;width:100%"><img src="loading-spin.svg"></div>
|
<div style="display: flex;align-items: center;justify-content: center;height:100%;width:100%"><img src="loading-spin.svg"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -75,6 +75,7 @@ function createWindow () {
|
||||||
win = new BrowserWindow({
|
win = new BrowserWindow({
|
||||||
width: 900,
|
width: 900,
|
||||||
height: 700,
|
height: 700,
|
||||||
|
title: 'Dev-Sidecar',
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
enableRemoteModule: true,
|
enableRemoteModule: true,
|
||||||
// preload: path.join(__dirname, 'preload.js'),
|
// preload: path.join(__dirname, 'preload.js'),
|
||||||
|
@ -167,7 +168,17 @@ if (!isFirstInstance) {
|
||||||
}
|
}
|
||||||
createWindow()
|
createWindow()
|
||||||
bridge.init(win)
|
bridge.init(win)
|
||||||
updateHandle(win, 'http://localhost/dev-sidecar/')
|
|
||||||
|
let updateUrl = 'https://dev-sidecar.docmirror.cn/update/'
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
Object.defineProperty(app, 'isPackaged', {
|
||||||
|
get () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
updateUrl = 'http://localhost/dev-sidecar/'
|
||||||
|
}
|
||||||
|
updateHandle(win, updateUrl)
|
||||||
try {
|
try {
|
||||||
// 最小化到托盘
|
// 最小化到托盘
|
||||||
tray = setTray(app)
|
tray = setTray(app)
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import lodash from 'lodash'
|
import lodash from 'lodash'
|
||||||
import DevSidecar from '@docmirror/dev-sidecar'
|
import DevSidecar from '@docmirror/dev-sidecar'
|
||||||
import { ipcMain } from 'electron'
|
import { ipcMain, Menu } from 'electron'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import JSON5 from 'json5'
|
import JSON5 from 'json5'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
const mitmproxyPath = path.join(__dirname, 'mitmproxy.js')
|
const mitmproxyPath = path.join(__dirname, 'mitmproxy.js')
|
||||||
const localApi = {
|
const localApi = {
|
||||||
|
/**
|
||||||
|
* 返回所有api列表,供vue来ipc调用
|
||||||
|
* @returns {[]}
|
||||||
|
*/
|
||||||
getApiList () {
|
getApiList () {
|
||||||
const core = lodash.cloneDeep(DevSidecar.api)
|
const core = lodash.cloneDeep(DevSidecar.api)
|
||||||
const local = lodash.cloneDeep(localApi)
|
const local = lodash.cloneDeep(localApi)
|
||||||
|
@ -15,13 +19,40 @@ const localApi = {
|
||||||
// console.log('api list:', list)
|
// console.log('api list:', list)
|
||||||
return list
|
return list
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 软件设置
|
||||||
|
*/
|
||||||
|
setting: {
|
||||||
|
load () {
|
||||||
|
const settingPath = _getSettingPath()
|
||||||
|
const file = fs.readFileSync(settingPath)
|
||||||
|
const settings = JSON5.parse(file.toString())
|
||||||
|
return settings || {}
|
||||||
|
},
|
||||||
|
save (settings = {}) {
|
||||||
|
const settingPath = _getSettingPath()
|
||||||
|
fs.writeFileSync(settingPath, JSON5.stringify(settings, null, 2))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 启动所有
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
startup () {
|
startup () {
|
||||||
return DevSidecar.api.startup({ mitmproxyPath })
|
return DevSidecar.api.startup({ mitmproxyPath })
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
/**
|
||||||
|
* 启动代理服务
|
||||||
|
* @returns {Promise<{port: *}>}
|
||||||
|
*/
|
||||||
start () {
|
start () {
|
||||||
return DevSidecar.api.server.start({ mitmproxyPath })
|
return DevSidecar.api.server.start({ mitmproxyPath })
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 重启代理服务
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
restart () {
|
restart () {
|
||||||
return DevSidecar.api.server.restart({ mitmproxyPath })
|
return DevSidecar.api.server.restart({ mitmproxyPath })
|
||||||
}
|
}
|
||||||
|
@ -35,15 +66,13 @@ const localApi = {
|
||||||
// 对比默认config的异同
|
// 对比默认config的异同
|
||||||
const defConfig = DevSidecar.api.config.getDefault()
|
const defConfig = DevSidecar.api.config.getDefault()
|
||||||
const saveConfig = doMerge(defConfig, newConfig)
|
const saveConfig = doMerge(defConfig, newConfig)
|
||||||
|
|
||||||
// _merge(defConfig, newConfig, saveConfig, 'intercepts')
|
|
||||||
// _merge(defConfig, newConfig, saveConfig, 'dns.mapping')
|
|
||||||
// _merge(defConfig, newConfig, saveConfig, 'setting.startup.server', true)
|
|
||||||
// _merge(defConfig, newConfig, saveConfig, 'setting.startup.proxy')
|
|
||||||
|
|
||||||
fs.writeFileSync(_getConfigPath(), JSON5.stringify(saveConfig, null, 2))
|
fs.writeFileSync(_getConfigPath(), JSON5.stringify(saveConfig, null, 2))
|
||||||
return saveConfig
|
return saveConfig
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 读取后合并配置
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
reload () {
|
reload () {
|
||||||
const path = _getConfigPath()
|
const path = _getConfigPath()
|
||||||
if (!fs.existsSync(path)) {
|
if (!fs.existsSync(path)) {
|
||||||
|
@ -53,7 +82,7 @@ const localApi = {
|
||||||
const userConfig = JSON5.parse(file.toString())
|
const userConfig = JSON5.parse(file.toString())
|
||||||
DevSidecar.api.config.set(userConfig)
|
DevSidecar.api.config.set(userConfig)
|
||||||
const config = DevSidecar.api.config.get()
|
const config = DevSidecar.api.config.get()
|
||||||
return config
|
return config || {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +98,13 @@ function _deepFindFunction (list, parent, parentKey) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _getSettingPath () {
|
||||||
|
const dir = './config/'
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir)
|
||||||
|
}
|
||||||
|
return dir + 'setting.json5'
|
||||||
|
}
|
||||||
function _getConfigPath () {
|
function _getConfigPath () {
|
||||||
const dir = './config/'
|
const dir = './config/'
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
|
@ -147,6 +183,7 @@ export default {
|
||||||
|
|
||||||
// 合并用户配置
|
// 合并用户配置
|
||||||
localApi.config.reload()
|
localApi.config.reload()
|
||||||
|
// 启动所有
|
||||||
localApi.startup()
|
localApi.startup()
|
||||||
},
|
},
|
||||||
devSidecar: DevSidecar
|
devSidecar: DevSidecar
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ipcMain } from 'electron'
|
import { ipcMain, dialog } from 'electron'
|
||||||
import { autoUpdater } from 'electron-updater'
|
import { autoUpdater } from 'electron-updater'
|
||||||
|
import path from 'path'
|
||||||
// win是所有窗口的引用
|
// win是所有窗口的引用
|
||||||
// const path = require('path') // 引入path模块
|
// const path = require('path') // 引入path模块
|
||||||
// const fs = require('fs-extra')
|
// const fs = require('fs-extra')
|
||||||
|
@ -14,61 +14,77 @@ function updateHandle (win, updateUrl) {
|
||||||
// fs.emptyDir(updatePendingPath)
|
// fs.emptyDir(updatePendingPath)
|
||||||
// // 更新前,删除本地安装包 ↑
|
// // 更新前,删除本地安装包 ↑
|
||||||
const message = {
|
const message = {
|
||||||
error: 'update error',
|
error: '更新失败',
|
||||||
checking: 'updating...',
|
checking: '检查更新中',
|
||||||
updateAva: 'fetch new version and downloading...',
|
updateAva: '发现新版本',
|
||||||
updateNotAva: 'do not to update'
|
updateNotAva: '当前为最新版本,无需更新'
|
||||||
}
|
}
|
||||||
// 本地开发环境,改变app-update.yml地址
|
// 本地开发环境,改变app-update.yml地址
|
||||||
// if (process.env.NODE_ENV === 'development' && !isMac) {
|
if (process.env.NODE_ENV === 'development' && !isMac) {
|
||||||
// autoUpdater.updateConfigPath = path.join(__dirname, 'win-unpacked/resources/app-update.yml')
|
autoUpdater.updateConfigPath = path.join(__dirname, 'win-unpacked/resources/app-update.yml')
|
||||||
// }
|
}
|
||||||
|
autoUpdater.autoDownload = false
|
||||||
|
|
||||||
// 设置服务器更新地址
|
// 设置服务器更新地址
|
||||||
autoUpdater.setFeedURL({
|
autoUpdater.setFeedURL({
|
||||||
provider: 'generic',
|
provider: 'generic',
|
||||||
url: updateUrl
|
url: updateUrl
|
||||||
})
|
})
|
||||||
autoUpdater.on('error', function (err) {
|
autoUpdater.on('error', function (error) {
|
||||||
console.log('autoUpdater error', err)
|
console.log('autoUpdater error', error)
|
||||||
sendUpdateMessage(message.error)
|
sendUpdateMessage({ key: 'error', value: error, error: error })
|
||||||
|
// dialog.showErrorBox('Error: ', error == null ? 'unknown' : (error.stack || error).toString())
|
||||||
})
|
})
|
||||||
autoUpdater.on('checking-for-update', function () {
|
autoUpdater.on('checking-for-update', function () {
|
||||||
console.log('autoUpdater checking-for-update')
|
console.log('autoUpdater checking-for-update')
|
||||||
sendUpdateMessage(message.checking)
|
sendUpdateMessage({ key: 'checking', value: message.checking })
|
||||||
})
|
})
|
||||||
// 准备更新,打开进度条读取页面,关闭其他页面
|
|
||||||
autoUpdater.on('update-available', function (info) {
|
autoUpdater.on('update-available', function (info) {
|
||||||
console.log('autoUpdater update-available')
|
console.log('autoUpdater update-available')
|
||||||
sendUpdateMessage(message.updateAva)
|
sendUpdateMessage({ key: 'available', value: info })
|
||||||
})
|
})
|
||||||
autoUpdater.on('update-not-available', function (info) {
|
autoUpdater.on('update-not-available', function (info) {
|
||||||
console.log('autoUpdater update-not-available')
|
console.log('autoUpdater update-not-available')
|
||||||
sendUpdateMessage(message.updateNotAva)
|
sendUpdateMessage({ key: 'notAvailable', value: message.updateNotAva })
|
||||||
})
|
})
|
||||||
// 更新下载进度
|
// 更新下载进度
|
||||||
autoUpdater.on('download-progress', function (progressObj) {
|
autoUpdater.on('download-progress', function (progressObj) {
|
||||||
console.log('autoUpdater download-progress')
|
console.log('autoUpdater download-progress')
|
||||||
win.webContents.send('download-progress', parseInt(progressObj.percent))
|
win.webContents.send('update', { key: 'progress', value: parseInt(progressObj.percent) })
|
||||||
})
|
})
|
||||||
// 更新完成,重启应用
|
// 更新完成,重启应用
|
||||||
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
|
autoUpdater.on('update-downloaded', function (info) {
|
||||||
ipcMain.on('isUpdateNow', (e, arg) => {
|
console.log('download complete', info.version)
|
||||||
|
win.webContents.send('update', {
|
||||||
|
key: 'downloaded',
|
||||||
|
value: {
|
||||||
|
version: info.version,
|
||||||
|
releaseData: info.releaseDate
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('update', (e, arg) => {
|
||||||
|
if (arg.key === 'doUpdateNow') {
|
||||||
// some code here to handle event
|
// some code here to handle event
|
||||||
autoUpdater.quitAndInstall()
|
autoUpdater.quitAndInstall(true, true)
|
||||||
})
|
} else if (arg.key === 'checkForUpdate') {
|
||||||
win.webContents.send('isUpdateNow')
|
|
||||||
})
|
|
||||||
ipcMain.on('checkForUpdate', () => {
|
|
||||||
// 执行自动更新检查
|
// 执行自动更新检查
|
||||||
console.log('autoUpdater checkForUpdates')
|
console.log('autoUpdater checkForUpdates')
|
||||||
autoUpdater.checkForUpdates()
|
autoUpdater.checkForUpdates()
|
||||||
|
} else if (arg.key === 'downloadUpdate') {
|
||||||
|
// 下载新版本
|
||||||
|
console.log('autoUpdater downloadUpdate')
|
||||||
|
autoUpdater.downloadUpdate()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
// 通过main进程发送事件给renderer进程,提示更新信息
|
// 通过main进程发送事件给renderer进程,提示更新信息
|
||||||
function sendUpdateMessage (text) {
|
function sendUpdateMessage (message) {
|
||||||
console.log('autoUpdater sendUpdateMessage')
|
console.log('autoUpdater sendUpdateMessage')
|
||||||
win.webContents.send('message', text)
|
win.webContents.send('update', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('auto update inited')
|
console.log('auto update inited')
|
||||||
|
return autoUpdater
|
||||||
}
|
}
|
||||||
export default updateHandle
|
export default updateHandle
|
||||||
|
|
|
@ -3,7 +3,6 @@ import App from './view/App.vue'
|
||||||
import antd from 'ant-design-vue'
|
import antd from 'ant-design-vue'
|
||||||
import 'ant-design-vue/dist/antd.css'
|
import 'ant-design-vue/dist/antd.css'
|
||||||
import view from './view'
|
import view from './view'
|
||||||
import { apiInit } from './view/api'
|
|
||||||
import VueRouter from 'vue-router'
|
import VueRouter from 'vue-router'
|
||||||
import routes from './view/router'
|
import routes from './view/router'
|
||||||
import DsContainer from './view/components/container'
|
import DsContainer from './view/components/container'
|
||||||
|
@ -17,16 +16,17 @@ Vue.component(DsContainer)
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
routes // (缩写) 相当于 routes: routes
|
routes // (缩写) 相当于 routes: routes
|
||||||
})
|
})
|
||||||
|
Vue.prototype.$global = {}
|
||||||
apiInit().then((api) => {
|
view.initApi().then(async (api) => {
|
||||||
Vue.prototype.$api = api
|
Vue.prototype.$api = api
|
||||||
|
// 初始化status
|
||||||
|
await view.initPre(api)
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
router,
|
router,
|
||||||
render: h => h(App)
|
render: h => h(App)
|
||||||
}).$mount('#app')
|
}).$mount('#app')
|
||||||
|
|
||||||
view.init(app)
|
view.initModules(app)
|
||||||
})
|
})
|
||||||
|
|
||||||
// fix vue-router NavigationDuplicated
|
// fix vue-router NavigationDuplicated
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
import lodash from 'lodash'
|
import lodash from 'lodash'
|
||||||
import { ipcRenderer } from 'electron'
|
import { ipcRenderer } from 'electron'
|
||||||
const doInvoke = (api, args) => {
|
const invoke = (api, args) => {
|
||||||
return ipcRenderer.invoke('apiInvoke', [api, args]).catch(err => {
|
return ipcRenderer.invoke('apiInvoke', [api, args]).catch(err => {
|
||||||
console.error('api invoke error:', err)
|
console.error('api invoke error:', err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const send = (channel, message) => {
|
||||||
|
console.log('do send,', channel, message)
|
||||||
|
return ipcRenderer.send(channel, message)
|
||||||
|
}
|
||||||
|
|
||||||
const bindApi = (api, param1) => {
|
const bindApi = (api, param1) => {
|
||||||
lodash.set(apiObj, api, (param2) => {
|
lodash.set(apiObj, api, (param2) => {
|
||||||
return doInvoke(api, param2 || param1)
|
return invoke(api, param2 || param1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const apiObj = {
|
const apiObj = {
|
||||||
on (channel, callback) {
|
on (channel, callback) {
|
||||||
ipcRenderer.on(channel, callback)
|
ipcRenderer.on(channel, callback)
|
||||||
},
|
},
|
||||||
doInvoke
|
invoke,
|
||||||
|
send
|
||||||
}
|
}
|
||||||
let inited = false
|
let inited = false
|
||||||
|
|
||||||
export function apiInit () {
|
export function apiInit () {
|
||||||
if (!inited) {
|
if (!inited) {
|
||||||
return doInvoke('getApiList').then(list => {
|
return invoke('getApiList').then(list => {
|
||||||
inited = true
|
inited = true
|
||||||
for (const item of list) {
|
for (const item of list) {
|
||||||
bindApi(item)
|
bindApi(item)
|
||||||
|
@ -30,7 +35,6 @@ export function apiInit () {
|
||||||
return apiObj
|
return apiObj
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ipcRenderer.send('checkForUpdate')
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
resolve(apiObj)
|
resolve(apiObj)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import './status'
|
import api, { apiInit } from './api'
|
||||||
import register from './event'
|
import modules from './modules'
|
||||||
|
import status from './status'
|
||||||
export default {
|
export default {
|
||||||
init (app) {
|
initApi: apiInit,
|
||||||
register(app)
|
async initPre (api) {
|
||||||
|
await status.install(api)
|
||||||
|
},
|
||||||
|
initModules (app) {
|
||||||
|
modules.install(app, api)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import DsContainer from '../components/container'
|
import DsContainer from '../components/container'
|
||||||
import status from '../status'
|
|
||||||
import lodash from 'lodash'
|
import lodash from 'lodash'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
DsContainer
|
DsContainer
|
||||||
|
@ -8,7 +8,7 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
config: undefined,
|
config: undefined,
|
||||||
status: status,
|
status: {},
|
||||||
labelCol: { span: 4 },
|
labelCol: { span: 4 },
|
||||||
wrapperCol: { span: 20 },
|
wrapperCol: { span: 20 },
|
||||||
applyLoading: false
|
applyLoading: false
|
||||||
|
@ -20,8 +20,15 @@ export default {
|
||||||
mounted () {
|
mounted () {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getKey () {
|
||||||
|
if (this.key) {
|
||||||
|
return this.key
|
||||||
|
}
|
||||||
|
throw new Error('请设置key')
|
||||||
|
},
|
||||||
init () {
|
init () {
|
||||||
this.$api.config.reload().then(ret => {
|
this.status = this.$status
|
||||||
|
return this.$api.config.reload().then(ret => {
|
||||||
this.config = ret
|
this.config = ret
|
||||||
if (this.ready) {
|
if (this.ready) {
|
||||||
return this.ready(this.config)
|
return this.ready(this.config)
|
||||||
|
@ -40,11 +47,21 @@ export default {
|
||||||
async applyBefore () {
|
async applyBefore () {
|
||||||
|
|
||||||
},
|
},
|
||||||
reloadDefault (key) {
|
resetDefault () {
|
||||||
this.$api.config.resetDefault(key).then(ret => {
|
const key = this.getKey()
|
||||||
this.config = ret
|
this.$confirm({
|
||||||
}).then(() => {
|
title: '提示',
|
||||||
this.apply()
|
content: '确定要恢复默认设置吗?',
|
||||||
|
cancelText: '取消',
|
||||||
|
okText: '确定',
|
||||||
|
onOk: async () => {
|
||||||
|
this.config = await this.$api.config.resetDefault(key)
|
||||||
|
if (this.ready) {
|
||||||
|
await this.ready(this.config)
|
||||||
|
}
|
||||||
|
await this.apply()
|
||||||
|
},
|
||||||
|
onCancel () {}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
saveConfig () {
|
saveConfig () {
|
||||||
|
|
|
@ -1,19 +1,9 @@
|
||||||
import api from './api'
|
function install (app, api) {
|
||||||
import status from './status'
|
|
||||||
import lodash from 'lodash'
|
|
||||||
function register (app) {
|
|
||||||
api.on('status', (event, message) => {
|
|
||||||
console.log('view on status', event, message)
|
|
||||||
const value = message.value
|
|
||||||
const key = message.key
|
|
||||||
lodash.set(status, key, value)
|
|
||||||
})
|
|
||||||
|
|
||||||
api.on('error.core', (event, message) => {
|
api.on('error.core', (event, message) => {
|
||||||
console.error('view on error', message)
|
console.error('view on error', message)
|
||||||
const key = message.key
|
const key = message.key
|
||||||
if (key === 'server') {
|
if (key === 'server') {
|
||||||
handleServerStartError(message, message.error, app)
|
handleServerStartError(message, message.error, app, api)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
api.on('error', (event, message) => {
|
api.on('error', (event, message) => {
|
||||||
|
@ -21,7 +11,7 @@ function register (app) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleServerStartError (message, err, app) {
|
function handleServerStartError (message, err, app, api) {
|
||||||
if (message.value === 'EADDRINUSE') {
|
if (message.value === 'EADDRINUSE') {
|
||||||
app.$confirm({
|
app.$confirm({
|
||||||
title: '端口被占用,代理服务启动失败',
|
title: '端口被占用,代理服务启动失败',
|
||||||
|
@ -42,4 +32,6 @@ function handleServerStartError (message, err, app) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default register
|
export default {
|
||||||
|
install
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import update from './update'
|
||||||
|
import error from './error'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install (app, api) {
|
||||||
|
error.install(app, api)
|
||||||
|
update.install(app, api)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
let updateParams = { }
|
||||||
|
function install (app, api) {
|
||||||
|
api.on('update', (event, message) => {
|
||||||
|
console.log('on message', event, message)
|
||||||
|
handleUpdateMessage(message, app)
|
||||||
|
})
|
||||||
|
|
||||||
|
api.update = {
|
||||||
|
checkForUpdate (params) {
|
||||||
|
updateParams = params || { fromUser: false, autoDownload: true, progress: 0 }
|
||||||
|
api.send('update', { key: 'checkForUpdate' })
|
||||||
|
},
|
||||||
|
downloadUpdate () {
|
||||||
|
api.send('update', { key: 'downloadUpdate' })
|
||||||
|
},
|
||||||
|
doUpdateNow () {
|
||||||
|
api.send('update', { key: 'doUpdateNow' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateMessage (message) {
|
||||||
|
const type = message.key
|
||||||
|
if (type === 'available') {
|
||||||
|
foundNewVersion(message.value)
|
||||||
|
} else if (type === 'notAvailable') {
|
||||||
|
noNewVersion()
|
||||||
|
} else if (type === 'downloaded') {
|
||||||
|
// 更新包已下载完成,让用户确认是否更新
|
||||||
|
newUpdateIsReady(message.value)
|
||||||
|
} else if (type === 'progress') {
|
||||||
|
progressUpdate(message.value)
|
||||||
|
} else if (type === 'error') {
|
||||||
|
const error = message.error
|
||||||
|
app.$message.error('Error: ' + (error == null ? '未知错误' : (error.stack || error).toString()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function noNewVersion (value) {
|
||||||
|
updateParams.newVersion = false
|
||||||
|
if (updateParams.fromUser) {
|
||||||
|
app.$message.info('当前已经是最新版本')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function progressUpdate (value) {
|
||||||
|
updateParams.progress = value
|
||||||
|
}
|
||||||
|
function foundNewVersion (value) {
|
||||||
|
updateParams.newVersion = true
|
||||||
|
|
||||||
|
if (updateParams.autoDownload !== false) {
|
||||||
|
api.update.downloadUpdate()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
app.$confirm({
|
||||||
|
title: '发现新版本',
|
||||||
|
content: `是否要更新到v${value.version}?`,
|
||||||
|
cancelText: '暂不升级',
|
||||||
|
okText: '升级',
|
||||||
|
// content: h => <div><h4>{value.version}更新内容:</h4><div>{value.releaseNotes}</div></div>,
|
||||||
|
onOk () {
|
||||||
|
console.log('OK')
|
||||||
|
api.update.downloadUpdate()
|
||||||
|
},
|
||||||
|
onCancel () {
|
||||||
|
console.log('Cancel')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function newUpdateIsReady (value) {
|
||||||
|
app.$confirm({
|
||||||
|
title: '新版本已准备好',
|
||||||
|
content: `是否立即升级安装v${value.version}?`,
|
||||||
|
cancelText: '暂不升级',
|
||||||
|
okText: '立即升级',
|
||||||
|
// content: h => <div><h4>{value.version}更新内容:</h4><div>{value.releaseNotes}</div></div>,
|
||||||
|
onOk () {
|
||||||
|
console.log('OK')
|
||||||
|
api.update.doUpdateNow()
|
||||||
|
},
|
||||||
|
onCancel () {
|
||||||
|
console.log('Cancel')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install
|
||||||
|
}
|
|
@ -4,10 +4,13 @@
|
||||||
给开发者的辅助工具
|
给开发者的辅助工具
|
||||||
<span>
|
<span>
|
||||||
<a-button style="margin-right:10px" @click="openSetupCa">安装根证书</a-button>
|
<a-button style="margin-right:10px" @click="openSetupCa">安装根证书</a-button>
|
||||||
|
<a-badge :count="update.newVersion?1:0" dot>
|
||||||
|
<a-button style="margin-right:10px" @click="doCheckUpdate">检查更新</a-button>
|
||||||
|
</a-badge>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div style="display: flex; align-items:center;justify-content:space-around;flex-direction: row">
|
<div v-if="status" style="display: flex; align-items:center;justify-content:space-around;flex-direction: row">
|
||||||
<div style="text-align: center">
|
<div style="text-align: center">
|
||||||
<div class="big_button">
|
<div class="big_button">
|
||||||
<a-button shape="circle" :type="startup.type()" :loading="startup.loading" @click="startup.doClick">
|
<a-button shape="circle" :type="startup.type()" :loading="startup.loading" @click="startup.doClick">
|
||||||
|
@ -38,8 +41,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import api from '../api'
|
|
||||||
import status from '../status'
|
|
||||||
import lodash from 'lodash'
|
import lodash from 'lodash'
|
||||||
import setupCa from '../components/setup-ca'
|
import setupCa from '../components/setup-ca'
|
||||||
import DsContainer from '../components/container'
|
import DsContainer from '../components/container'
|
||||||
|
@ -52,7 +53,7 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
status: status,
|
status: undefined,
|
||||||
startup: {
|
startup: {
|
||||||
loading: false,
|
loading: false,
|
||||||
type: () => {
|
type: () => {
|
||||||
|
@ -60,9 +61,9 @@ export default {
|
||||||
},
|
},
|
||||||
doClick: () => {
|
doClick: () => {
|
||||||
if (this.status.server.enabled) {
|
if (this.status.server.enabled) {
|
||||||
this.apiCall(this.startup, api.shutdown)
|
this.apiCall(this.startup, this.$api.shutdown)
|
||||||
} else {
|
} else {
|
||||||
this.apiCall(this.startup, api.startup)
|
this.apiCall(this.startup, this.$api.startup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -77,39 +78,48 @@ export default {
|
||||||
config: undefined,
|
config: undefined,
|
||||||
setupCa: {
|
setupCa: {
|
||||||
visible: false
|
visible: false
|
||||||
}
|
},
|
||||||
|
update: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
async created () {
|
||||||
console.log('index created')
|
console.log('index created', this.status, this.$status)
|
||||||
this.reloadConfig().then(() => {
|
await this.reloadConfig()
|
||||||
// this.start(true)
|
const status = await this.$api.status.get()
|
||||||
return api.status.get().then(ret => {
|
console.log('status', status)
|
||||||
console.log('status', ret)
|
|
||||||
lodash.merge(status, ret)
|
|
||||||
this.$set(this, 'status', status)
|
this.$set(this, 'status', status)
|
||||||
})
|
|
||||||
}).then(() => {
|
|
||||||
this.switchBtns = this.createSwitchBtns()
|
this.switchBtns = this.createSwitchBtns()
|
||||||
})
|
console.log('switchBtns', this.switchBtns)
|
||||||
|
if (this.$global.update == null) {
|
||||||
|
this.$global.update = {
|
||||||
|
fromUser: false,
|
||||||
|
autoDownload: true,
|
||||||
|
progress: 0,
|
||||||
|
newVersion: false
|
||||||
|
}
|
||||||
|
this.update = this.$global.update
|
||||||
|
this.doCheckUpdate(false)
|
||||||
|
}
|
||||||
|
this.update = this.$global.update
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
console.log('index mounted')
|
console.log('index mounted')
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reloadConfig () {
|
reloadConfig () {
|
||||||
return api.config.reload().then(ret => {
|
return this.$api.config.reload().then(ret => {
|
||||||
this.config = ret
|
this.config = ret
|
||||||
return ret
|
return ret
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
createSwitchBtns () {
|
createSwitchBtns () {
|
||||||
console.log('api,', api)
|
console.log('api,', this.$api)
|
||||||
const btns = {}
|
const btns = {}
|
||||||
btns.server = this.createSwitchBtn('server', '代理服务', api.server, status)
|
const status = this.status
|
||||||
btns.proxy = this.createSwitchBtn('proxy', '系统代理', api.proxy, status)
|
btns.server = this.createSwitchBtn('server', '代理服务', this.$api.server, status)
|
||||||
lodash.forEach(this.status.plugin, (item, key) => {
|
btns.proxy = this.createSwitchBtn('proxy', '系统代理', this.$api.proxy, status)
|
||||||
btns[key] = this.createSwitchBtn(key, this.config.plugin[key].name, api.plugin[key], status.plugin)
|
lodash.forEach(status.plugin, (item, key) => {
|
||||||
|
btns[key] = this.createSwitchBtn(key, this.config.plugin[key].name, this.$api.plugin[key], status.plugin)
|
||||||
})
|
})
|
||||||
return btns
|
return btns
|
||||||
},
|
},
|
||||||
|
@ -145,10 +155,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onServerClick (checked) {
|
onServerClick (checked) {
|
||||||
return this.onSwitchClick(this.server, api.server.start, api.server.close, checked)
|
return this.onSwitchClick(this.server, this.$api.server.start, this.$api.server.close, checked)
|
||||||
},
|
},
|
||||||
start (checked) {
|
start (checked) {
|
||||||
this.apiCall(this.startup, api.startup)
|
this.apiCall(this.startup, this.$api.startup)
|
||||||
},
|
},
|
||||||
openSettings () {
|
openSettings () {
|
||||||
this.settings.visible = true
|
this.settings.visible = true
|
||||||
|
@ -157,12 +167,16 @@ export default {
|
||||||
console.log('config changed', newConfig)
|
console.log('config changed', newConfig)
|
||||||
this.reloadConfig().then(() => {
|
this.reloadConfig().then(() => {
|
||||||
if (this.status.server) {
|
if (this.status.server) {
|
||||||
return api.server.restart()
|
return this.$api.server.restart()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openSetupCa () {
|
openSetupCa () {
|
||||||
this.setupCa.visible = true
|
this.setupCa.visible = true
|
||||||
|
},
|
||||||
|
doCheckUpdate (fromUser = true) {
|
||||||
|
this.update.fromUser = fromUser
|
||||||
|
this.$api.update.checkForUpdate(this.update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
</div>
|
</div>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<div class="footer-bar">
|
<div class="footer-bar">
|
||||||
<a-button class="md-mr-10" icon="sync" @click="reloadDefault('server')">恢复默认</a-button>
|
<a-button class="md-mr-10" icon="sync" @click="resetDefault()">恢复默认</a-button>
|
||||||
<a-button :loading="applyLoading" icon="check" type="primary" @click="apply()">应用</a-button>
|
<a-button :loading="applyLoading" icon="check" type="primary" @click="apply()">应用</a-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -75,6 +75,7 @@ export default {
|
||||||
mixins: [Plugin],
|
mixins: [Plugin],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
key: 'plugin.node',
|
||||||
npmVariables: undefined,
|
npmVariables: undefined,
|
||||||
registry: false
|
registry: false
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
</div>
|
</div>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<div class="footer-bar">
|
<div class="footer-bar">
|
||||||
<a-button class="md-mr-10" icon="sync" @click="reloadDefault('server')">恢复默认</a-button>
|
<a-button class="md-mr-10" icon="sync" @click="resetDefault()">恢复默认</a-button>
|
||||||
<a-button :loading="applyLoading" icon="check" type="primary" @click="apply()">应用</a-button>
|
<a-button :loading="applyLoading" icon="check" type="primary" @click="apply()">应用</a-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -36,7 +36,7 @@ export default {
|
||||||
mixins: [Plugin],
|
mixins: [Plugin],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
key: 'proxy'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
</div>
|
</div>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<div class="footer-bar">
|
<div class="footer-bar">
|
||||||
<a-button class="md-mr-10" icon="sync" @click="reloadDefault('server')">恢复默认</a-button>
|
<a-button class="md-mr-10" icon="sync" @click="resetDefault()">恢复默认</a-button>
|
||||||
<a-button :loading="applyLoading" icon="check" type="primary" @click="apply()">应用</a-button>
|
<a-button :loading="applyLoading" icon="check" type="primary" @click="apply()">应用</a-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -88,6 +88,7 @@ export default {
|
||||||
mixins: [Plugin],
|
mixins: [Plugin],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
key: 'server',
|
||||||
labelCol: { span: 4 },
|
labelCol: { span: 4 },
|
||||||
wrapperCol: { span: 20 },
|
wrapperCol: { span: 20 },
|
||||||
dnsMappings: []
|
dnsMappings: []
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import lodash from 'lodash'
|
||||||
|
import Vue from 'vue'
|
||||||
const status = {
|
const status = {
|
||||||
server: {
|
server: {
|
||||||
enabled: false
|
enabled: false
|
||||||
|
@ -9,4 +11,19 @@ const status = {
|
||||||
node: {}
|
node: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default status
|
async function install (api) {
|
||||||
|
api.on('status', (event, message) => {
|
||||||
|
console.log('view on status', event, message)
|
||||||
|
const value = message.value
|
||||||
|
const key = message.key
|
||||||
|
lodash.set(status, key, value)
|
||||||
|
})
|
||||||
|
const basicStatus = await api.status.get()
|
||||||
|
lodash.merge(status, basicStatus)
|
||||||
|
Vue.prototype.$status = status
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
install,
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
|
@ -3,56 +3,38 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"depedencies": {},
|
"keywords": [
|
||||||
"keywords": [],
|
"dev-sidecar"
|
||||||
|
],
|
||||||
"author": "docmirror.cn",
|
"author": "docmirror.cn",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node start.js",
|
|
||||||
"serve": "vue-cli-service serve",
|
|
||||||
"build": "vue-cli-service build",
|
|
||||||
"lint": "vue-cli-service lint"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agentkeepalive": "^2.1.1",
|
"agentkeepalive": "^2.1.1",
|
||||||
"babel-core": "^6.8.0",
|
|
||||||
"babel-plugin-transform-async-to-generator": "^6.7.4",
|
|
||||||
"babel-polyfill": "^6.8.0",
|
|
||||||
"babel-preset-es2015": "^6.6.0",
|
|
||||||
"babel-register": "^6.8.0",
|
|
||||||
"charset": "^1.0.0",
|
|
||||||
"child_process": "^1.0.2",
|
"child_process": "^1.0.2",
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"commander": "^2.9.0",
|
"commander": "^2.9.0",
|
||||||
"core-js": "^3.6.5",
|
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"dns-over-http": "^0.2.0",
|
"dns-over-http": "^0.2.0",
|
||||||
"dns-over-tls": "^0.0.8",
|
"dns-over-tls": "^0.0.8",
|
||||||
"iconv-lite": "^0.4.13",
|
|
||||||
"is-browser": "^2.1.0",
|
|
||||||
"jschardet": "^1.4.1",
|
|
||||||
"json5": "^2.1.3",
|
"json5": "^2.1.3",
|
||||||
"lodash": "^4.7.0",
|
"lodash": "^4.7.0",
|
||||||
|
"log4js": "^6.3.0",
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^6.0.0",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"node-cmd": "^3.0.0",
|
"node-cmd": "^3.0.0",
|
||||||
"node-forge": "^0.8.2",
|
"node-forge": "^0.8.2",
|
||||||
"node-mitmproxy": "^3.1.1",
|
|
||||||
"node-powershell": "^4.0.0",
|
|
||||||
"require-context": "^1.1.0",
|
"require-context": "^1.1.0",
|
||||||
"ssl-root-cas": "^1.3.1",
|
|
||||||
"through2": "^2.0.1",
|
"through2": "^2.0.1",
|
||||||
"tunnel-agent": "^0.4.3",
|
"tunnel-agent": "^0.4.3",
|
||||||
"util": "^0.12.3",
|
"util": "^0.12.3",
|
||||||
"validator": "^13.1.17",
|
"validator": "^13.1.17"
|
||||||
"vue": "^2.6.11",
|
|
||||||
"winreg": "^1.2.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
"@vue/cli-service": "~4.5.0",
|
|
||||||
"@vue/eslint-config-standard": "^5.1.2",
|
"@vue/eslint-config-standard": "^5.1.2",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^6.7.2",
|
||||||
|
@ -60,8 +42,7 @@
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-standard": "^4.0.0",
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^6.2.2"
|
||||||
"vue-template-compiler": "^2.6.11"
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"root": true,
|
"root": true,
|
||||||
|
|
|
@ -63,15 +63,12 @@ function connect (req, cltSocket, head, hostname, port, dnsConfig) {
|
||||||
proxySocket.on('timeout', () => {
|
proxySocket.on('timeout', () => {
|
||||||
const end = new Date().getTime()
|
const end = new Date().getTime()
|
||||||
console.log('代理socket timeout:', hostname, port, (end - start) + 'ms')
|
console.log('代理socket timeout:', hostname, port, (end - start) + 'ms')
|
||||||
proxySocket.destroy()
|
|
||||||
cltSocket.destroy()
|
|
||||||
})
|
})
|
||||||
proxySocket.on('error', (e) => {
|
proxySocket.on('error', (e) => {
|
||||||
// 连接失败,可能被GFW拦截,或者服务端拥挤
|
// 连接失败,可能被GFW拦截,或者服务端拥挤
|
||||||
const end = new Date().getTime()
|
const end = new Date().getTime()
|
||||||
console.error('代理连接失败:', e.message, hostname, port, (end - start) + 'ms')
|
console.error('代理连接失败:', e.message, hostname, port, (end - start) + 'ms')
|
||||||
cltSocket.destroy()
|
cltSocket.destroy()
|
||||||
|
|
||||||
if (isDnsIntercept) {
|
if (isDnsIntercept) {
|
||||||
const { dns, ip, hostname } = isDnsIntercept
|
const { dns, ip, hostname } = isDnsIntercept
|
||||||
dns.count(hostname, ip, true)
|
dns.count(hostname, ip, true)
|
||||||
|
|
|
@ -39,14 +39,14 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
|
||||||
|
|
||||||
const proxyRequestPromise = async () => {
|
const proxyRequestPromise = async () => {
|
||||||
rOptions.host = rOptions.hostname || rOptions.host || 'localhost'
|
rOptions.host = rOptions.hostname || rOptions.host || 'localhost'
|
||||||
if (dnsConfig) {
|
// if (dnsConfig) {
|
||||||
const dns = DnsUtil.hasDnsLookup(dnsConfig, rOptions.host)
|
// const dns = DnsUtil.hasDnsLookup(dnsConfig, rOptions.host)
|
||||||
if (dns) {
|
// if (dns) {
|
||||||
const ip = await dns.lookup(rOptions.host)
|
// const ip = await dns.lookup(rOptions.host)
|
||||||
console.log('使用自定义dns:', rOptions.host, ip, dns.dnsServer)
|
// console.log('使用自定义dns:', rOptions.host, ip, dns.dnsServer)
|
||||||
rOptions.host = ip
|
// rOptions.host = ip
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// use the binded socket for NTLM
|
// use the binded socket for NTLM
|
||||||
|
@ -64,6 +64,22 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
|
||||||
const url = `${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${rOptions.path}`
|
const url = `${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${rOptions.path}`
|
||||||
const start = new Date().getTime()
|
const start = new Date().getTime()
|
||||||
console.log('代理请求:', url, rOptions.method)
|
console.log('代理请求:', url, rOptions.method)
|
||||||
|
let isDnsIntercept
|
||||||
|
if (dnsConfig) {
|
||||||
|
const dns = DnsUtil.hasDnsLookup(dnsConfig, rOptions.hostname)
|
||||||
|
if (dns) {
|
||||||
|
rOptions.lookup = (hostname, options, callback) => {
|
||||||
|
dns.lookup(hostname).then(ip => {
|
||||||
|
isDnsIntercept = { dns, hostname, ip }
|
||||||
|
if (ip !== hostname) {
|
||||||
|
callback(null, ip, 4)
|
||||||
|
} else {
|
||||||
|
rOptions.lookup(hostname, options, callback)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proxyReq = (rOptions.protocol === 'https:' ? https : http).request(rOptions, (proxyRes) => {
|
proxyReq = (rOptions.protocol === 'https:' ? https : http).request(rOptions, (proxyRes) => {
|
||||||
const end = new Date().getTime()
|
const end = new Date().getTime()
|
||||||
|
@ -75,12 +91,22 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
|
||||||
|
|
||||||
proxyReq.on('timeout', () => {
|
proxyReq.on('timeout', () => {
|
||||||
const end = new Date().getTime()
|
const end = new Date().getTime()
|
||||||
|
if (isDnsIntercept) {
|
||||||
|
const { dns, ip, hostname } = isDnsIntercept
|
||||||
|
dns.count(hostname, ip, true)
|
||||||
|
console.error('记录ip失败次数,用于优选ip:', hostname, ip)
|
||||||
|
}
|
||||||
console.error('代理请求超时', rOptions.protocol, rOptions.hostname, rOptions.path, (end - start) + 'ms')
|
console.error('代理请求超时', rOptions.protocol, rOptions.hostname, rOptions.path, (end - start) + 'ms')
|
||||||
reject(new Error(`${rOptions.host}:${rOptions.port}, 代理请求超时`))
|
reject(new Error(`${rOptions.host}:${rOptions.port}, 代理请求超时`))
|
||||||
})
|
})
|
||||||
|
|
||||||
proxyReq.on('error', (e, req, res) => {
|
proxyReq.on('error', (e, req, res) => {
|
||||||
const end = new Date().getTime()
|
const end = new Date().getTime()
|
||||||
|
if (isDnsIntercept) {
|
||||||
|
const { dns, ip, hostname } = isDnsIntercept
|
||||||
|
dns.count(hostname, ip, true)
|
||||||
|
console.error('记录ip失败次数,用于优选ip:', hostname, ip)
|
||||||
|
}
|
||||||
console.error('代理请求错误', e.errno, rOptions.hostname, rOptions.path, (end - start) + 'ms')
|
console.error('代理请求错误', e.errno, rOptions.hostname, rOptions.path, (end - start) + 'ms')
|
||||||
reject(e)
|
reject(e)
|
||||||
})
|
})
|
||||||
|
@ -92,7 +118,7 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
|
||||||
|
|
||||||
req.on('aborted', function () {
|
req.on('aborted', function () {
|
||||||
console.error('请求被取消', rOptions.hostname, rOptions.path)
|
console.error('请求被取消', rOptions.hostname, rOptions.path)
|
||||||
proxyReq.destroy()
|
proxyReq.abort()
|
||||||
reject(new Error('请求被取消'))
|
reject(new Error('请求被取消'))
|
||||||
})
|
})
|
||||||
req.on('error', function (e, req, res) {
|
req.on('error', function (e, req, res) {
|
||||||
|
|
|
@ -3541,6 +3541,16 @@ dashdash@^1.12.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
assert-plus "^1.0.0"
|
assert-plus "^1.0.0"
|
||||||
|
|
||||||
|
date-format@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf"
|
||||||
|
integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==
|
||||||
|
|
||||||
|
date-format@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95"
|
||||||
|
integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==
|
||||||
|
|
||||||
de-indent@^1.0.2:
|
de-indent@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npm.taobao.org/de-indent/download/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
resolved "https://registry.npm.taobao.org/de-indent/download/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||||
|
@ -4644,7 +4654,7 @@ flat-cache@^2.0.1:
|
||||||
rimraf "2.6.3"
|
rimraf "2.6.3"
|
||||||
write "1.0.3"
|
write "1.0.3"
|
||||||
|
|
||||||
flatted@^2.0.0:
|
flatted@^2.0.0, flatted@^2.0.1:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.npm.taobao.org/flatted/download/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
|
resolved "https://registry.npm.taobao.org/flatted/download/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
|
||||||
integrity sha1-RXWyHivO50NKqb5mL0t7X5wrUTg=
|
integrity sha1-RXWyHivO50NKqb5mL0t7X5wrUTg=
|
||||||
|
@ -4720,6 +4730,15 @@ fs-extra@^7.0.1:
|
||||||
jsonfile "^4.0.0"
|
jsonfile "^4.0.0"
|
||||||
universalify "^0.1.0"
|
universalify "^0.1.0"
|
||||||
|
|
||||||
|
fs-extra@^8.1.0:
|
||||||
|
version "8.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||||
|
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.2.0"
|
||||||
|
jsonfile "^4.0.0"
|
||||||
|
universalify "^0.1.0"
|
||||||
|
|
||||||
fs-minipass@^2.0.0:
|
fs-minipass@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npm.taobao.org/fs-minipass/download/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
|
resolved "https://registry.npm.taobao.org/fs-minipass/download/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
|
||||||
|
@ -4901,7 +4920,7 @@ globby@^9.2.0:
|
||||||
pify "^4.0.1"
|
pify "^4.0.1"
|
||||||
slash "^2.0.0"
|
slash "^2.0.0"
|
||||||
|
|
||||||
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.2:
|
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2:
|
||||||
version "4.2.4"
|
version "4.2.4"
|
||||||
resolved "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz?cache=0&sync_timestamp=1589682809142&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fgraceful-fs%2Fdownload%2Fgraceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
resolved "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz?cache=0&sync_timestamp=1589682809142&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fgraceful-fs%2Fdownload%2Fgraceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
||||||
integrity sha1-Ila94U02MpWMRl68ltxGfKB6Kfs=
|
integrity sha1-Ila94U02MpWMRl68ltxGfKB6Kfs=
|
||||||
|
@ -6055,6 +6074,17 @@ log-symbols@^2.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "^2.0.1"
|
chalk "^2.0.1"
|
||||||
|
|
||||||
|
log4js@^6.3.0:
|
||||||
|
version "6.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb"
|
||||||
|
integrity sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==
|
||||||
|
dependencies:
|
||||||
|
date-format "^3.0.0"
|
||||||
|
debug "^4.1.1"
|
||||||
|
flatted "^2.0.1"
|
||||||
|
rfdc "^1.1.4"
|
||||||
|
streamroller "^2.2.4"
|
||||||
|
|
||||||
loglevel@^1.6.8:
|
loglevel@^1.6.8:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.npm.taobao.org/loglevel/download/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0"
|
resolved "https://registry.npm.taobao.org/loglevel/download/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0"
|
||||||
|
@ -8002,6 +8032,11 @@ retry@^0.12.0:
|
||||||
resolved "https://registry.npm.taobao.org/retry/download/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
resolved "https://registry.npm.taobao.org/retry/download/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
||||||
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
|
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
|
||||||
|
|
||||||
|
rfdc@^1.1.4:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2"
|
||||||
|
integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==
|
||||||
|
|
||||||
rgb-regex@^1.0.1:
|
rgb-regex@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npm.taobao.org/rgb-regex/download/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
|
resolved "https://registry.npm.taobao.org/rgb-regex/download/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
|
||||||
|
@ -8541,6 +8576,15 @@ stream-shift@^1.0.0:
|
||||||
resolved "https://registry.npm.taobao.org/stream-shift/download/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
|
resolved "https://registry.npm.taobao.org/stream-shift/download/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
|
||||||
integrity sha1-1wiCgVWasneEJCebCHfaPDktWj0=
|
integrity sha1-1wiCgVWasneEJCebCHfaPDktWj0=
|
||||||
|
|
||||||
|
streamroller@^2.2.4:
|
||||||
|
version "2.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.4.tgz#c198ced42db94086a6193608187ce80a5f2b0e53"
|
||||||
|
integrity sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==
|
||||||
|
dependencies:
|
||||||
|
date-format "^2.1.0"
|
||||||
|
debug "^4.1.1"
|
||||||
|
fs-extra "^8.1.0"
|
||||||
|
|
||||||
strict-uri-encode@^1.0.0:
|
strict-uri-encode@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npm.taobao.org/strict-uri-encode/download/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
resolved "https://registry.npm.taobao.org/strict-uri-encode/download/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||||
|
|
Loading…
Reference in New Issue