Browse Source

refactor: mirrors

pull/180/head
xiaojunnuo 4 years ago
parent
commit
aa4f029f62
  1. 68
      README.md
  2. 28
      config/index.json5
  3. 13366
      packages/core/package-lock.json
  4. 31
      packages/core/src/config.js
  5. 9
      packages/core/src/config/index.js
  6. 12
      packages/core/src/expose.js
  7. 1
      packages/core/src/lib/interceptor/impl/abort.js
  8. 1
      packages/core/src/lib/proxy/mitmproxy/index.js
  9. 2
      packages/core/src/lib/proxy/tls/CertAndKeyContainer.js
  10. 32
      packages/core/src/server/index.js
  11. 10
      packages/core/src/shell/index.js
  12. 31
      packages/core/src/shell/scripts/get-env.js
  13. 21
      packages/core/src/shell/scripts/kill-by-port.js
  14. 26
      packages/core/src/shell/scripts/set-env.js
  15. 21
      packages/core/src/shell/scripts/setup-ca.js
  16. 111
      packages/core/src/shell/shell.js
  17. 20
      packages/core/src/shell/test.js
  18. 5
      packages/core/src/switch/proxy/impl/system-proxy.js
  19. 2
      packages/core/src/switch/proxy/impl/yarn-proxy.js
  20. 6
      packages/core/start.js
  21. BIN
      packages/gui/build/icons/1024x1024.png
  22. BIN
      packages/gui/build/icons/128x128.png
  23. BIN
      packages/gui/build/icons/16x16.png
  24. BIN
      packages/gui/build/icons/24x24.png
  25. BIN
      packages/gui/build/icons/256x256.png
  26. BIN
      packages/gui/build/icons/32x32.png
  27. BIN
      packages/gui/build/icons/48x48.png
  28. BIN
      packages/gui/build/icons/512x512.png
  29. BIN
      packages/gui/build/icons/64x64.png
  30. BIN
      packages/gui/build/icons/icon.icns
  31. BIN
      packages/gui/build/icons/icon.ico
  32. BIN
      packages/gui/extra/favicon.ico
  33. BIN
      packages/gui/extra/icons/1024x1024.png
  34. BIN
      packages/gui/extra/icons/128x128.png
  35. BIN
      packages/gui/extra/icons/16x16.png
  36. BIN
      packages/gui/extra/icons/24x24.png
  37. BIN
      packages/gui/extra/icons/256x256.png
  38. BIN
      packages/gui/extra/icons/32x32.png
  39. BIN
      packages/gui/extra/icons/48x48.png
  40. BIN
      packages/gui/extra/icons/512x512.png
  41. BIN
      packages/gui/extra/icons/64x64.png
  42. BIN
      packages/gui/extra/icons/icon.icns
  43. BIN
      packages/gui/extra/icons/icon.ico
  44. 237
      packages/gui/package-lock.json
  45. 4
      packages/gui/package.json
  46. BIN
      packages/gui/public/favicon.ico
  47. BIN
      packages/gui/public/icon-1.png
  48. BIN
      packages/gui/public/icon.png
  49. 18
      packages/gui/public/logo/logo-fff.svg
  50. 6
      packages/gui/public/logo/logo-simple.svg
  51. BIN
      packages/gui/public/logo/logo-simple2.png
  52. BIN
      packages/gui/public/setup.png
  53. 25
      packages/gui/src/background.js
  54. 30
      packages/gui/src/bridge/index.js
  55. 13
      packages/gui/src/main.js
  56. 38
      packages/gui/src/view/api.js
  57. 31
      packages/gui/src/view/components/App.vue
  58. 76
      packages/gui/src/view/components/settings.vue
  59. 61
      packages/gui/src/view/components/setup-ca.vue
  60. 45
      packages/gui/src/view/event.js
  61. 4
      packages/gui/src/view/index.js
  62. 9
      packages/gui/src/view/status.js

68
README.md

@ -4,19 +4,22 @@
解决一些网站和库无法访问或访问速度慢的问题
## 特性
### 1、 解决git push某些情况下需要临时输入账号密码的问题
### 2、 github的release、source、zip下载加速
### 1、 github的release、source、zip下载加速
可解决npm install 时某些安装包下载不下来的问题
### 3、 github的源代码查看(raw/blame查看)
### 2、 解决git push某些情况下需要临时输入账号密码的问题
通过将api.github.com域名解析到美国服务器
### 3、 github的源代码查看(raw/blame查看)
通过跳转到国内加速链接上
### 4、 Stack Overflow 加速
将ajax.google.com代理到加速代理上
### 5、 google cdn 加速
通过代理到加速链接上
### 6、 gist.github.com 加速
### 6、 更多加速配置
等你来提issue
## 快速开始
@ -85,13 +88,62 @@ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keyc
### npm加速
1. yarn 设置淘宝镜像registry
2. npm设置官方registry。
3. 项目install使用yarn,publish用npm,互不影响
3. 项目install使用yarn,发布包publish用npm,互不影响
### 其他加速
1. git clone 加速 [fgit-go](https://github.com/FastGitORG/fgit-go)
2. github.com代理网站(不能登录) [hub.fastgit.org](https://hub.fastgit.org/)
## api
### 拦截配置
没有配置域名的不会拦截,其他根据配置进行拦截处理
```js
'github.com': [
{ // 此条配置 release archive 下载链接替换,
regexp: [ //需要拦截的url
'/.*/.*/releases/download/',
'/.*/.*/archive/'
],
//拦截类型
// redirect:url, 临时重定向(url会变,一些下载资源可以通过此方式配置)
// proxy:url, 代理(url不会变,没有跨域问题)
// abort:true, 取消请求(适用于被GFW封锁的资源,找不到替代,直接取消请求,快速失败,节省时间)
redirect: 'https://download.fastgit.org' //跳转到加速下载链接上
},
'ajax.googleapis.com': [
{
regexp:'.*' // .* 拦截全部url路径,可省略
proxy: 'https://ajax.loli.net' //代理到加速链接上(url不会变,没有跨域问题,适用于一些静态资源比如js、css的请求)
}
],
'clients*.google.com': [
{
abort: true //取消请求,被GFW封锁的资源,找不到替代,直接取消请求,快速失败,节省时间
}
]
],
```
### DNS配置
某些域名(比如api.github.com)会被解析到新加坡的ip上,新加坡的服务器在上午挺好,到了晚上就卡死,基本不可用。
所以将这些域名解析到美国服务器上就可以正常访问
```js
dns: {
mapping: {
// "解决push的时候需要输入密码的问题",
'api.github.com': 'usa', //配置该域名,使用USA的域名解析服务器
'gist.github.com': 'usa'
// "avatars*.githubusercontent.com": "usa"
}
},
```
注意:暂时只支持IPv4的解析
## 开发计划
1. 桌面端,右下角小图标
2. √ google cdn加速

28
config/index.json5

@ -1,28 +0,0 @@
{
server: {
port: 1181
},
"intercepts": {
'notify3.note.youdao.com': [
{
regexp: '.*',
redirect: 'https://localhost:99999'
}
]
},
"dns": {
"mapping": {
//"avatars*.githubusercontent.com": "usa"
}
},
// setting: {
// startup: {
// // 开机启动
// server: true,
// proxy: {
// system: true,
// npm: true
// }
// }
// }
}

13366
packages/core/package-lock.json generated

File diff suppressed because it is too large Load Diff

31
packages/core/src/config.js

@ -1,7 +1,7 @@
const Shell = require('./shell')
const lodash = require('lodash')
const defConfig = require('./config/index.js')
let configTarget = lodash.cloneDeep(defConfig)
function _deleteDisabledItem (target, objKey) {
const obj = lodash.get(target, objKey)
for (const key in obj) {
@ -10,7 +10,7 @@ function _deleteDisabledItem (target, objKey) {
}
}
}
module.exports = {
const configApi = {
get () {
return configTarget
},
@ -33,5 +33,32 @@ module.exports = {
},
resetDefault () {
configTarget = lodash.cloneDeep(defConfig)
},
getMirrorEnv () {
const envMap = Shell.getEnv()
const list = []
const mirrors = configTarget.mirrors
for (const key in mirrors) {
const exists = envMap[key] != null
list.push({
key,
value: mirrors[key],
exists
})
}
console.log('mirrors:', list)
return list
},
setupMirrors () {
const list = configApi.getMirrorEnv()
const noSetList = list.filter(item => {
return !item.exists
})
console.log('mirrors will set:', noSetList)
if (list.length > 0) {
Shell.setEnv({ list: noSetList })
}
}
}
module.exports = configApi

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

@ -104,6 +104,12 @@ module.exports = {
// "avatars*.githubusercontent.com": "usa"
}
},
mirrors: {
SASS_BINARY_SITE: 'https://npm.taobao.org/mirrors/node-sass/',
PHANTOMJS_CDNURL: 'https://npm.taobao.org/mirrors/phantomjs/',
ELECTRON_MIRROR: 'https://npm.taobao.org/mirrors/electron/',
CYPRESS_DOWNLOAD_MIRROR: 'https://cdn.cypress.io'
},
setting: {
startup: { // 开机启动
server: true,
@ -111,6 +117,9 @@ module.exports = {
system: true,
npm: true,
yarn: true
},
mirrors: {
set: true
}
}
}

12
packages/core/src/expose.js

@ -3,7 +3,7 @@ const proxy = require('./switch/proxy/index.js')
const status = require('./status')
const config = require('./config')
const event = require('./event')
const shell = require('./shell')
async function proxyStartup ({ ip, port }) {
for (const key in proxy) {
if (config.get().setting.startup.proxy[key]) {
@ -30,10 +30,15 @@ module.exports = {
config.set(newConfig)
}
try {
if (config.get().setting.startup.server) {
const startup = config.get().setting.startup
if (startup.server) {
server.start(newConfig)
}
await proxyStartup({ ip: '127.0.0.1', port: config.get().server.port })
if (startup.mirrors.set) {
await config.setupMirrors()
}
} catch (error) {
console.log(error)
}
@ -49,6 +54,7 @@ module.exports = {
console.log(error)
}
},
event
event,
shell
}
}

1
packages/core/src/lib/interceptor/impl/abort.js

@ -1,4 +1,3 @@
const url = require('url')
module.exports = {
requestInterceptor (interceptOpt, rOptions, req, res, ssl) {
req.abort()

1
packages/core/src/lib/proxy/mitmproxy/index.js

@ -6,7 +6,6 @@ const createRequestHandler = require('./createRequestHandler')
const createConnectHandler = require('./createConnectHandler')
const createFakeServerCenter = require('./createFakeServerCenter')
const createUpgradeHandler = require('./createUpgradeHandler')
module.exports = {
createProxy ({
port = config.defaultPort,

2
packages/core/src/lib/proxy/tls/CertAndKeyContainer.js

@ -51,7 +51,7 @@ module.exports = class CertAndKeyContainer {
}
}
let certObj
const fast = false
const fast = true
if (fast) {
certObj = tlsUtils.createFakeCertificateByDomain(this.caKey, this.caCert, hostname)
_resolve(certObj)

32
packages/core/src/server/index.js

@ -5,29 +5,49 @@ const logger = getLogger('proxy')
const config = require('../config')
const event = require('../event')
let server
module.exports = {
const serverApi = {
async start (newConfig) {
config.set(newConfig)
const proxyOptions = ProxyOptions(config.get())
server = mitmproxy.createProxy(proxyOptions, () => {
event.fire('status', { key: 'server', value: true })
server.on('close', () => {
event.fire('status', { key: 'server', value: false })
})
})
server.on('close', () => {
event.fire('status', { key: 'server', value: false })
})
server.on('error', (e) => {
event.fire('error', { key: 'server.start', error: e })
})
server.config = config.get()
return server.config
},
close () {
async close () {
try {
if (server) {
server.close()
return new Promise((resolve, reject) => {
server.close((err) => {
if (err) {
reject(err)
} else {
resolve()
}
})
})
}
} catch (err) {
logger.error(err)
}
},
async restart () {
try {
await serverApi.close()
} catch (err) {
console.log('stop error', err)
}
await serverApi.start()
},
getServer () {
return server
}
}
module.exports = serverApi

10
packages/core/src/shell/index.js

@ -0,0 +1,10 @@
const killByPort = require('./scripts/kill-by-port')
const setupCa = require('./scripts/setup-ca')
const getEnv = require('./scripts/get-env')
const setEnv = require('./scripts/set-env')
module.exports = {
killByPort,
setupCa,
getEnv,
setEnv
}

31
packages/core/src/shell/scripts/get-env.js

@ -0,0 +1,31 @@
/**
* 获取环境变量
*/
const Shell = require('../shell')
const execute = Shell.execute
const executor = {
async windows (exec) {
const ret = await exec(['set'], { type: 'cmd' })
const map = {}
if (ret != null) {
const lines = ret.split('\r\n')
for (const item of lines) {
const kv = item.split('=')
if (kv.length > 1) {
map[kv[0]] = kv[1]
}
}
}
return map
},
async linux (exec, { port }) {
throw Error('暂未实现此功能')
},
async mac (exec, { port }) {
throw Error('暂未实现此功能')
}
}
module.exports = async function (args) {
return execute(executor, args)
}

21
packages/core/src/shell/scripts/kill-by-port.js

@ -0,0 +1,21 @@
const Shell = require('../shell')
const execute = Shell.execute
const executor = {
async windows (exec, { port }) {
const cmds = [`for /f "tokens=5" %a in ('netstat -aon ^| find ":${port}" ^| find "LISTENING"') do taskkill /f /pid %a`]
// eslint-disable-next-line no-unused-vars
const ret = await exec(cmds, { type: 'cmd' })
return true
},
async linux (exec, { port }) {
throw Error('暂未实现此功能')
},
async mac (exec, { port }) {
throw Error('暂未实现此功能')
}
}
module.exports = async function (args) {
return execute(executor, args)
}

26
packages/core/src/shell/scripts/set-env.js

@ -0,0 +1,26 @@
/**
* 设置环境变量
*/
const Shell = require('../shell')
const execute = Shell.execute
const executor = {
async windows (exec, { list }) {
const cmds = []
for (const item of list) {
// [Environment]::SetEnvironmentVariable('FOO', 'bar', 'Machine')
cmds.push(`[Environment]::SetEnvironmentVariable('${item.key} ', '${item.value}', 'Machine')`)
}
const ret = await exec(cmds, { type: 'ps' })
return ret
},
async linux (exec, { port }) {
throw Error('暂未实现此功能')
},
async mac (exec, { port }) {
throw Error('暂未实现此功能')
}
}
module.exports = async function (args) {
return execute(executor, args)
}

21
packages/core/src/shell/scripts/setup-ca.js

@ -0,0 +1,21 @@
const Shell = require('../shell')
const execute = Shell.execute
const proxyConfig = require('../../lib/proxy/common/config')
const executor = {
async windows (exec) {
const cmds = ['start ' + proxyConfig.getDefaultCACertPath()]
// eslint-disable-next-line no-unused-vars
const ret = await exec(cmds, { type: 'cmd' })
return true
},
async linux (exec, { port }) {
throw Error('暂未实现此功能')
},
async mac (exec, { port }) {
throw Error('暂未实现此功能')
}
}
module.exports = async function (args) {
return execute(executor, args)
}

111
packages/core/src/shell/shell.js

@ -0,0 +1,111 @@
const util = require('util')
const os = require('os')
const childProcess = require('child_process')
const _exec = childProcess.exec
const exec = util.promisify(_exec)
const Shell = require('node-powershell')
class SystemShell {
static async exec (cmds, args) {
throw new Error('You have to implement the method exec!')
}
}
class LinuxSystemShell extends SystemShell {
static async exec (cmds) {
if (cmds instanceof String) {
cmds = [cmds]
}
for (const cmd of cmds) {
await exec(cmd)
}
}
}
class DarwinSystemShell extends SystemShell {
static async exec (cmds) {
if (cmds instanceof String) {
cmds = [cmds]
}
for (const cmd of cmds) {
await exec(cmd)
}
}
}
class WindowsSystemShell extends SystemShell {
static async exec (cmds, { type = 'ps' }) {
if (cmds instanceof String) {
cmds = [cmds]
}
if (type === 'ps') {
const ps = new Shell({
executionPolicy: 'Bypass',
noProfile: true
})
for (const cmd of cmds) {
console.log('ps:', cmd)
ps.addCommand(cmd)
}
const ret = await ps.invoke()
console.log('ps complete:', ret)
return ret
} else {
let compose = 'chcp 65001 '
for (const cmd of cmds) {
compose += ' && ' + cmd
}
return new Promise((resolve, reject) => {
childProcess.exec(compose, function (error, stdout, stderr) {
if (error) {
console.error('cmd 命令执行错误:', compose, error, stderr)
reject(error)
} else {
const data = stdout
resolve(data)
}
})
})
}
}
}
function getSystemShell () {
switch (getSystemPlatform()) {
case 'mac':
return DarwinSystemShell
case 'linux':
return LinuxSystemShell
case 'windows':
return WindowsSystemShell
case 'unknown os':
default:
throw new Error(`UNKNOWN OS TYPE ${os.platform()}`)
}
}
function getSystemPlatform () {
switch (os.platform()) {
case 'darwin':
return 'mac'
case 'linux':
return 'linux'
case 'win32':
case 'win64':
return 'windows'
case 'unknown os':
default:
throw new Error(`UNKNOWN OS TYPE ${os.platform()}`)
}
}
async function execute (executor, args) {
return executor[getSystemPlatform()](getSystemShell().exec, args)
}
module.exports = {
getSystemShell,
getSystemPlatform,
execute
}

20
packages/core/src/shell/test.js

@ -0,0 +1,20 @@
const cmd1 = require('node-cmd')
cmd1.get('set',
function (err, data, stderr) {
console.log('cmd complete:', err, data, stderr)
if (err) {
console.error('cmd 命令执行错误:', err, stderr)
} else {
console.log('cmd 命令执行结果:', data)
}
}
)
// var process = require('child_process')
//
// var cmd = 'set'
// process.exec(cmd, function (error, stdout, stderr) {
// console.log('error:' + error)
// console.log('stdout:' + stdout)
// console.log('stderr:' + stderr)
// })

5
packages/core/src/switch/proxy/impl/system-proxy.js

@ -1,14 +1,11 @@
const util = require('util')
const os = require('os')
const path = require('path')
const childProcess = require('child_process')
const _exec = childProcess.exec
const spawn = childProcess.spawn
const Registry = require('winreg')
// const cmd = require('node-cmd')
console.log('childProcess', childProcess)
const exec = util.promisify(_exec)
const setproxyPs = require('./set-internet-proxy')
const refreshInternetPs = require('./refresh-internet')
const Shell = require('node-powershell')
@ -89,7 +86,7 @@ class WindowsSystemProxy extends SystemProxy {
for (const string of _lanIP) {
lanIpStr += string + ';'
}
console.log('lanIps:', lanIpStr, ip, port)
// console.log('lanIps:', lanIpStr, ip, port)
await Promise.all([
WindowsSystemProxy._asyncRegSet(regKey, 'MigrateProxy', Registry.REG_DWORD, 1),
WindowsSystemProxy._asyncRegSet(regKey, 'ProxyEnable', Registry.REG_DWORD, 1),

2
packages/core/src/switch/proxy/impl/yarn-proxy.js

@ -43,7 +43,7 @@ class WindowsSystemProxy extends SystemProxy {
console.log('yarn https proxy unset success')
await winExec('yarn config delete ca')
console.log('yarn cafile unset success')
console.log('yarn ca unset success')
// await winExec(' yarn config delete strict-ssl')
// console.log('yarn strict-ssl true success')

6
packages/core/start.js

@ -1,8 +1,8 @@
const DevSidercar = require('.')
require('json5/lib/register')
const config = require('../../config/index.json5')
// require('json5/lib/register')
// const config = require('../../config/index.json5')
// 启动服务
DevSidercar.api.startup(config)
DevSidercar.api.startup()
async function onClose () {
console.log('on sigint ')
await DevSidercar.api.shutdown()

BIN
packages/gui/build/icons/1024x1024.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
packages/gui/build/icons/128x128.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
packages/gui/build/icons/16x16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

BIN
packages/gui/build/icons/24x24.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 B

BIN
packages/gui/build/icons/256x256.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
packages/gui/build/icons/32x32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
packages/gui/build/icons/48x48.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
packages/gui/build/icons/512x512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
packages/gui/build/icons/64x64.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
packages/gui/build/icons/icon.icns

Binary file not shown.

BIN
packages/gui/build/icons/icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

BIN
packages/gui/extra/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 17 KiB

BIN
packages/gui/extra/icons/1024x1024.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
packages/gui/extra/icons/128x128.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
packages/gui/extra/icons/16x16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

BIN
packages/gui/extra/icons/24x24.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 B

BIN
packages/gui/extra/icons/256x256.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
packages/gui/extra/icons/32x32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
packages/gui/extra/icons/48x48.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
packages/gui/extra/icons/512x512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
packages/gui/extra/icons/64x64.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
packages/gui/extra/icons/icon.icns

Binary file not shown.

BIN
packages/gui/extra/icons/icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

237
packages/gui/package-lock.json generated

@ -1958,6 +1958,11 @@
"integrity": "sha1-fuMwunyq+5gJC+zoal7kQRWQTCw=",
"dev": true
},
"@types/semver": {
"version": "7.3.4",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz",
"integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ=="
},
"@types/serve-static": {
"version": "1.13.5",
"resolved": "https://registry.npm.taobao.org/@types/serve-static/download/@types/serve-static-1.13.5.tgz?cache=0&sync_timestamp=1596840339942&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fserve-static%2Fdownload%2F%40types%2Fserve-static-1.13.5.tgz",
@ -2367,16 +2372,6 @@
"integrity": "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=",
"dev": true
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1601839122515&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"cacache": {
"version": "13.0.1",
"resolved": "https://registry.npm.taobao.org/cacache/download/cacache-13.0.1.tgz",
@ -2403,34 +2398,6 @@
"unique-filename": "^1.1.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"find-cache-dir": {
"version": "3.3.1",
"resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-3.3.1.tgz",
@ -2452,25 +2419,6 @@
"path-exists": "^4.0.0"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz",
"integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npm.taobao.org/locate-path/download/locate-path-5.0.0.tgz",
@ -2535,16 +2483,6 @@
"minipass": "^3.1.1"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1598611709087&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"terser-webpack-plugin": {
"version": "2.3.8",
"resolved": "https://registry.npm.taobao.org/terser-webpack-plugin/download/terser-webpack-plugin-2.3.8.tgz?cache=0&sync_timestamp=1602701885709&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fterser-webpack-plugin%2Fdownload%2Fterser-webpack-plugin-2.3.8.tgz",
@ -2561,18 +2499,6 @@
"terser": "^4.6.12",
"webpack-sources": "^1.4.3"
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8",
"resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.0.0-beta.8.tgz?cache=0&sync_timestamp=1600850354305&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.0.0-beta.8.tgz",
"integrity": "sha1-H1I9n+qOjG5PW7mf12gWWvWEWHk=",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
}
}
},
@ -3247,7 +3173,6 @@
"version": "1.0.10",
"resolved": "https://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz?cache=0&sync_timestamp=1598649734444&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fargparse%2Fdownload%2Fargparse-1.0.10.tgz",
"integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=",
"dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
@ -3511,8 +3436,7 @@
"at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/at-least-node/download/at-least-node-1.0.0.tgz",
"integrity": "sha1-YCzUtG6EStTv/JKoARo8RuAjjcI=",
"dev": true
"integrity": "sha1-YCzUtG6EStTv/JKoARo8RuAjjcI="
},
"atob": {
"version": "2.1.2",
@ -4934,7 +4858,6 @@
"version": "8.7.2",
"resolved": "https://registry.npm.taobao.org/builder-util-runtime/download/builder-util-runtime-8.7.2.tgz",
"integrity": "sha1-2Tr8cUKKEnibQ34ThQ4fp9qVbXI=",
"dev": true,
"requires": {
"debug": "^4.1.1",
"sax": "^1.2.4"
@ -7282,6 +7205,52 @@
"integrity": "sha1-Gt+sWv/OhNhbPXs9+8St4pOm/8Q=",
"dev": true
},
"electron-updater": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.3.5.tgz",
"integrity": "sha512-5jjN7ebvfj1cLI0VZMdCnJk6aC4bP+dy7ryBf21vArR0JzpRVk0OZHA2QBD+H5rm6ZSeDYHOY6+8PrMEqJ4wlQ==",
"requires": {
"@types/semver": "^7.3.1",
"builder-util-runtime": "8.7.2",
"fs-extra": "^9.0.1",
"js-yaml": "^3.14.0",
"lazy-val": "^1.0.4",
"lodash.isequal": "^4.5.0",
"semver": "^7.3.2"
},
"dependencies": {
"fs-extra": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
"requires": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^1.0.0"
}
},
"jsonfile": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
"integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^1.0.0"
}
},
"semver": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
},
"universalify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug=="
}
}
},
"elliptic": {
"version": "6.5.3",
"resolved": "https://registry.npm.taobao.org/elliptic/download/elliptic-6.5.3.tgz",
@ -7908,8 +7877,7 @@
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npm.taobao.org/esprima/download/esprima-4.0.1.tgz",
"integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=",
"dev": true
"integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE="
},
"esquery": {
"version": "1.3.1",
@ -8931,8 +8899,7 @@
"graceful-fs": {
"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",
"integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs=",
"dev": true
"integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs="
},
"gzip-size": {
"version": "5.1.1",
@ -10274,7 +10241,6 @@
"version": "3.14.0",
"resolved": "https://registry.npm.taobao.org/js-yaml/download/js-yaml-3.14.0.tgz",
"integrity": "sha1-p6NBcPJqIbsWJCTYray0ETpp5II=",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@ -10496,8 +10462,7 @@
"lazy-val": {
"version": "1.0.4",
"resolved": "https://registry.npm.taobao.org/lazy-val/download/lazy-val-1.0.4.tgz",
"integrity": "sha1-iCY2pyRcLP5uCk47psXWihN+XGU=",
"dev": true
"integrity": "sha1-iCY2pyRcLP5uCk47psXWihN+XGU="
},
"lcid": {
"version": "1.0.0",
@ -10692,6 +10657,11 @@
"integrity": "sha1-US6b1yHSctlOPTpjZT+hdRZ0HKY=",
"dev": true
},
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"lodash.kebabcase": {
"version": "4.1.1",
"resolved": "https://registry.npm.taobao.org/lodash.kebabcase/download/lodash.kebabcase-4.1.1.tgz",
@ -13715,8 +13685,7 @@
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npm.taobao.org/sax/download/sax-1.2.4.tgz",
"integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=",
"dev": true
"integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk="
},
"schema-utils": {
"version": "2.7.1",
@ -14402,8 +14371,7 @@
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsprintf-js%2Fdownload%2Fsprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sshpk": {
"version": "1.16.1",
@ -16367,6 +16335,87 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz",
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-ref": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/vue-ref/download/vue-ref-2.0.0.tgz",

4
packages/gui/package.json

@ -10,13 +10,15 @@
"electron:build": "vue-cli-service electron:build",
"electron": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps"
"postuninstall": "electron-builder install-app-deps",
"electron:generate-icons": "electron-icon-builder --input=./public/icon.png --output=build --flatten"
},
"main": "background.js",
"dependencies": {
"@docmirror/dev-sidecar": "1.0.0",
"ant-design-vue": "^1.6.5",
"core-js": "^3.6.5",
"electron-updater": "^4.3.5",
"es-abstract": "^1.17.7",
"json5": "^2.1.3",
"lodash": "^4.17.20",

BIN
packages/gui/public/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 17 KiB

BIN
packages/gui/public/icon-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
packages/gui/public/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

18
packages/gui/public/logo/logo-fff.svg

@ -0,0 +1,18 @@
<svg id="svg_canvas" viewBox="0 0 260 260" width="260" height="260" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="scale(2)">
<g class="logo-entity" transform="translate(139,-12.5) scale(-1.40,1.40)">
<circle fill="#FFF" cx="45.9" cy="55.2" r="17.5"
></circle>
<circle cx="23.5" cy="21.7" r="9.6" fill="#FFF"></circle>
<circle cx="52.3" cy="23.9" r="6.9" fill="#FFF"></circle>
<circle stroke="#FFF" stroke-miterlimit="10" cx="32.5" cy="89.7" r="9.6" fill="#FFF"></circle>
<circle fill="none" stroke="#FFF" stroke-width="5" stroke-miterlimit="10" cx="84" cy="61.3" r="10.4"></circle>
<line fill="none" stroke="#FFF" stroke-width="5" stroke-miterlimit="10" x1="37.9" y1="44.7" x2="26.2" y2="26.9"></line>
<line fill="none" stroke="#FFF" stroke-width="5" stroke-miterlimit="10" x1="48.1" y1="44.7" x2="51.3" y2="27.7"></line>
<line fill="none" stroke="#FFF" stroke-width="5" stroke-miterlimit="10" x1="40.1" y1="67.4" x2="34.9" y2="82.5"></line>
<line fill="none" stroke="#FFF" stroke-width="5" stroke-miterlimit="10" x1="57" y1="55.5" x2="71.8" y2="58.6"></line>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

6
packages/gui/public/logo/logo-simple.svg

@ -1,18 +1,18 @@
<svg id="svg_canvas" viewBox="0 0 260 260" width="260" height="260" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g transform="scale(2)">
<g class="logo-entity" transform="translate(144,-12.5) scale(-1.40,1.40)">
<g class="logo-entity" transform="translate(139,-12.5) scale(-1.40,1.40)">
<circle fill="#1B7FCD" cx="45.9" cy="55.2" r="17.5"
></circle>
<circle cx="23.5" cy="21.7" r="9.6" fill="#1B7FCD"></circle>
<circle cx="52.3" cy="23.9" r="6.9" fill="#1B7FCD"></circle>
<circle stroke="#1B7FCD" stroke-miterlimit="10" cx="32.5" cy="89.7" r="9.6" fill="#1B7FCD"></circle>
<circle fill="none" stroke="#1B7FCD" stroke-width="5" stroke-miterlimit="10" cx="87" cy="61.3" r="10.4"></circle>
<circle fill="none" stroke="#1B7FCD" stroke-width="5" stroke-miterlimit="10" cx="84" cy="61.3" r="10.4"></circle>
<line fill="none" stroke="#1B7FCD" stroke-width="5" stroke-miterlimit="10" x1="37.9" y1="44.7" x2="26.2" y2="26.9"></line>
<line fill="none" stroke="#1B7FCD" stroke-width="5" stroke-miterlimit="10" x1="48.1" y1="44.7" x2="51.3" y2="27.7"></line>
<line fill="none" stroke="#1B7FCD" stroke-width="5" stroke-miterlimit="10" x1="57" y1="55.5" x2="77.8" y2="59.2"></line>
<line fill="none" stroke="#1B7FCD" stroke-width="5" stroke-miterlimit="10" x1="40.1" y1="67.4" x2="34.9" y2="82.5"></line>
<line fill="none" stroke="#1B7FCD" stroke-width="5" stroke-miterlimit="10" x1="57" y1="55.5" x2="71.8" y2="58.6"></line>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
packages/gui/public/logo/logo-simple2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
packages/gui/public/setup.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

25
packages/gui/src/background.js

@ -1,4 +1,5 @@
'use strict'
/* global __static */
import path from 'path'
import { app, protocol, BrowserWindow, Menu, Tray } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
@ -9,7 +10,8 @@ const isDevelopment = process.env.NODE_ENV !== 'production'
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win
let tray
// eslint-disable-next-line no-unused-vars
let tray // 防止被内存清理
let forceClose = false
// Scheme must be registered before the app is ready
@ -28,7 +30,7 @@ function setTray (app) {
label: '退出',
click: () => {
forceClose = true
app.quit()
quit(app)
}
}
]
@ -57,14 +59,16 @@ function createWindow () {
// Create the browser window.
win = new BrowserWindow({
width: 800,
height: 600,
height: 700,
webPreferences: {
enableRemoteModule: true,
// preload: path.join(__dirname, 'preload.js'),
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
nodeIntegration: true// process.env.ELECTRON_NODE_INTEGRATION
}
},
// eslint-disable-next-line no-undef
icon: path.join(__static, 'icon.png')
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
@ -79,7 +83,7 @@ function createWindow () {
win.on('closed', async (e) => {
win = null
await bridge.devSidecar.api.shutdown()
tray = null
})
win.on('close', (e) => {
@ -95,7 +99,7 @@ app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
quit(app)
}
})
@ -129,17 +133,22 @@ app.on('ready', async () => {
}
})
function quit (app) {
bridge.devSidecar.api.shutdown().then(() => {
app.quit()
})
}
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit()
quit(app)
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
quit(app)
})
}
}

30
packages/gui/src/bridge/index.js

@ -5,6 +5,14 @@ import fs from 'fs'
import JSON5 from 'json5'
const localApi = {
getApiList () {
const core = lodash.cloneDeep(DevSidecar.api)
const local = lodash.cloneDeep(localApi)
lodash.merge(core, local)
const list = []
_deepFindFunction(list, core, '')
return list
},
config: {
/**
* 保存自定义的 config
@ -21,20 +29,30 @@ const localApi = {
_merge(defConfig, newConfig, saveConfig, 'setting.startup.server', true)
_merge(defConfig, newConfig, saveConfig, 'setting.startup.proxy')
// TODO 保存到文件
// console.log('save config ', saveConfig)
fs.writeFileSync(_getConfigPath(), JSON5.stringify(saveConfig, null, 2))
return saveConfig
},
reload () {
const path = _getConfigPath()
if (!fs.existsSync(path)) {
return
return DevSidecar.api.config.get()
}
const file = fs.readFileSync(path)
const userConfig = JSON5.parse(file.toString())
DevSidecar.api.config.set(userConfig)
return DevSidecar.api.config.get()
const config = DevSidecar.api.config.get()
return config
}
}
}
function _deepFindFunction (list, parent, parentKey) {
for (const key in parent) {
const item = parent[key]
if (item instanceof Function) {
list.push(parentKey + key)
} else if (item instanceof Object) {
_deepFindFunction(list, item, parentKey + key + '.')
}
}
}
@ -108,6 +126,10 @@ export default {
console.log('bridge on status', event)
win.webContents.send('status', { ...event })
})
DevSidecar.api.event.register('error', (event) => {
console.error('bridge on error', event)
win.webContents.send('error.core', event)
})
// 合并用户配置
localApi.config.reload()

13
packages/gui/src/main.js

@ -2,10 +2,15 @@ import Vue from 'vue'
import App from './view/components/App.vue'
import antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
import './view'
import view from './view'
import { apiInit } from './view/api'
Vue.config.productionTip = false
Vue.use(antd)
new Vue({
render: h => h(App)
}).$mount('#app')
apiInit().then(() => {
const app = new Vue({
render: h => h(App)
}).$mount('#app')
view.register(app)
})

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

@ -1,7 +1,9 @@
import lodash from 'lodash'
import { ipcRenderer } from 'electron'
const doInvoke = (api, args) => {
return ipcRenderer.invoke('apiInvoke', [api, args])
return ipcRenderer.invoke('apiInvoke', [api, args]).catch(err => {
console.error('api invoke error:', err)
})
}
const bindApi = (api, param1) => {
@ -15,23 +17,21 @@ const apiObj = {
},
doInvoke
}
let inited = false
bindApi('startup')
bindApi('shutdown')
bindApi('config.set')
bindApi('config.get')
bindApi('config.save')
bindApi('config.reload')
bindApi('server.start')
bindApi('server.close')
bindApi('proxy.system.open')
bindApi('proxy.system.close')
bindApi('proxy.npm.open')
bindApi('proxy.npm.close')
bindApi('proxy.yarn.open')
bindApi('proxy.yarn.close')
export function apiInit () {
if (!inited) {
return doInvoke('getApiList').then(list => {
console.log('apiList', list)
inited = true
for (const item of list) {
bindApi(item)
}
return apiObj
})
}
return new Promise(resolve => {
resolve(apiObj)
})
}
export default apiObj

31
packages/gui/src/view/components/App.vue

@ -3,11 +3,14 @@
<template>
<div style="margin:auto">
<div style="text-align: center"><img height="80px" src="/logo/logo-lang.svg"></div>
<a-card title="DevSidecar-开发者辅助工具 " style="width: 500px;margin:auto">
<a-card title="给开发者的辅助工具" style="width: 500px;margin:auto">
<div style="display: flex; align-items:center;justify-content:space-around;flex-direction: row">
<div style="text-align: center">
<div class="big_button" >
<a-button shape="circle" icon="poweroff" :type="startup.type()" :loading="startup.loading" @click="startup.doClick" ></a-button>
<a-button shape="circle" :type="startup.type()" :loading="startup.loading" @click="startup.doClick" >
<img v-if="!startup.loading && !status.server" width="50" src="/logo/logo-simple.svg">
<img v-if="!startup.loading && status.server" width="50" src="/logo/logo-fff.svg">
</a-button>
<div style="margin-top: 10px">{{status.server?'已开启':'已关闭'}}</div>
</div>
</div>
@ -32,11 +35,13 @@
</div>
</div>
<span slot="extra" >
<span slot="extra" >
<a-button style="margin-right:10px" @click="openSetupCa" >安装根证书</a-button>
<a-button v-if="config" @click="openSettings" icon="setting" ></a-button>
</span>
</span>
</a-card>
<setup-ca title="安装证书" :visible.sync="setupCa.visible"></setup-ca>
<settings v-if="config" title="设置" :config="config" :visible.sync="settings.visible" @change="onConfigChanged"></settings>
</div>
</template>
@ -48,10 +53,11 @@ import api from '../api'
import status from '../status'
import lodash from 'lodash'
import Settings from './settings'
import setupCa from './setup-ca'
export default {
name: 'App',
components: {
Settings
Settings, setupCa
},
data () {
return {
@ -87,6 +93,9 @@ export default {
config: undefined,
settings: {
visible: false
},
setupCa: {
visible: false
}
}
},
@ -105,6 +114,7 @@ export default {
methods: {
reloadConfig () {
return api.config.reload().then(ret => {
console.log('config', ret)
this.config = ret
return ret
})
@ -160,15 +170,14 @@ export default {
},
onConfigChanged (newConfig) {
console.log('config chagned', newConfig)
api.config.save(newConfig).then(() => {
return this.reloadConfig()
}).then(() => {
this.reloadConfig().then(() => {
if (this.status.server) {
return this.onServerClick(false).then(() => {
return this.onServerClick(true)
})
return api.server.restart()
}
})
},
openSetupCa () {
this.setupCa.visible = true
}
}
}

76
packages/gui/src/view/components/settings.vue

@ -42,26 +42,53 @@
<a-button type="primary" icon="plus" @click="addDnsMapping()" />
</a-col>
</a-row>
</div>
</a-tab-pane>
<a-tab-pane tab="镜像变量" key="3">
<div>
<div>某些库需要自己设置代理环境变量才能安装比如electron</div>
<div>
<a-form-item label="镜像环境变量" >
<a-switch v-model="targetConfig.setting.startup.mirrors.set" default-checked v-on:click="(checked)=>{targetConfig.setting.startup.mirrors.set = checked}">
<a-icon slot="checkedChildren" type="check" />
<a-icon slot="unCheckedChildren" type="close" />
</a-switch>
启动后自动检查设置
<a-button style="margin-left:10px" @click="doSetMirrorEnvNow">立即设置</a-button>
</a-form-item>
</div>
<a-row :gutter="10" style="margin-top: 10px" v-for="(item,index) of mirrorEnvs" :key = 'index'>
<a-col :span="10">
<a-input :disabled="item.key ===false" v-model="item.key"></a-input>
</a-col>
<a-col :span="10">
<a-input :disabled="item.value ===false" v-model="item.value"></a-input>
</a-col>
<a-col :span="4">
<a-icon v-if="item.exists" style="color:green" type="check" />
<a-icon v-if="!item.exists" title="还未设置" style="color:red" type="exclamation-circle" />
</a-col>
</a-row>
</div>
</a-tab-pane>
<a-tab-pane tab="启动设置" key="3" >
<a-tab-pane tab="启动设置" key="4" >
<div>启动应用程序后自动启动</div>
<a-form style="margin-top: 20px" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }" >
<a-form-item label="代理服务" style="margin-bottom: 10px">
<a-form-item label="代理服务" >
<a-switch v-model="targetConfig.setting.startup.server" default-checked v-on:click="(checked)=>{targetConfig.setting.startup.server = checked}">
<a-icon slot="checkedChildren" type="check" />
<a-icon slot="unCheckedChildren" type="close" />
</a-switch>
</a-form-item>
<a-form-item style="margin-bottom: 10px" v-for="(item,key) in targetConfig.setting.startup.proxy" :key="key" :label="key">
<a-form-item v-for="(item,key) in targetConfig.setting.startup.proxy" :key="key" :label="key">
<a-switch v-model="targetConfig.setting.startup.proxy[key]" default-checked v-on:click="(checked)=>{targetConfig.setting.startup.proxy[key] = checked}">
<a-icon slot="checkedChildren" type="check" />
<a-icon slot="unCheckedChildren" type="close" />
</a-switch>
</a-form-item>
</a-form>
</a-tab-pane>
</a-tabs>
@ -72,8 +99,9 @@
<script>
import vueJsonEditor from 'vue-json-editor'
import lodash from 'lodash'
import api from '../api'
export default {
name: 'App',
name: 'settings',
components: {
vueJsonEditor
},
@ -92,7 +120,8 @@ export default {
data () {
return {
targetConfig: {},
dnsMappings: []
dnsMappings: [],
mirrorEnvs: []
}
},
created () {
@ -109,6 +138,10 @@ export default {
key, value
})
}
api.config.getMirrorEnv().then(ret => {
this.mirrorEnvs = ret
})
},
onJsonChange (config) {
},
@ -141,13 +174,21 @@ export default {
mapping[item.key] = item.value
}
this.targetConfig.dns.mapping = mapping
const mirrors = {}
for (const item of this.mirrorEnvs) {
mirrors[item.key] = item.value
}
this.targetConfig.mirrors = mirrors
},
isChanged () {
this.syncTargetConfig()
return !lodash.isEqual(this.config, this.targetConfig)
},
doSave () {
this.$emit('change', this.targetConfig)
return api.config.save(this.targetConfig).then(ret => {
this.$emit('change', ret)
})
},
deleteDnsMapping (item, index) {
this.dnsMappings.splice(index, 1)
@ -157,6 +198,21 @@ export default {
},
addDnsMapping () {
this.dnsMappings.push({ key: '', value: 'usa' })
},
doSetMirrorEnvNow () {
this.syncTargetConfig()
this.doSave().then(() => {
return api.config.setupMirrors()
}).then(() => {
return api.config.getMirrorEnv().then(ret => {
this.mirrorEnvs = ret
})
}).then(() => {
this.$message.info('设置成功')
})
},
addMirrors () {
this.mirrorEnvs.push({ key: '', value: '', exists: false })
}
}
}
@ -182,5 +238,11 @@ export default {
}
.json-wrapper .ant-tabs-tabpane-active{
height: 100%;
overflow-y: auto;
overflow-x: hidden;
}
.a-form{
margin-bottom: 10px;
}
</style>

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

@ -0,0 +1,61 @@
<template>
<a-drawer
placement="right"
:closable="false"
:visible="visible"
:after-visible-change="afterVisibleChange"
@close="onClose"
width="650px"
height="100%"
:slots="{ title: 'title' }"
wrapClassName="json-wrapper"
>
<template slot="title">
{{title}}
<a-button type="primary" style="float:right" @click="doSetup()">点此去安装</a-button>
</template>
<img width="100%" src="/setup.png" />
</a-drawer>
</template>
<script>
import api from '../api'
export default {
name: 'setup-ca',
components: {
},
props: {
title: {
type: String,
default: '安装根证书'
},
visible: {
type: Boolean
}
},
data () {
return {
}
},
created () {
},
methods: {
afterVisibleChange (val) {
},
showDrawer () {
this.$emit('update:visible', true)
},
onClose () {
this.$emit('update:visible', false)
},
doSetup () {
api.shell.setupCa()
}
}
}
</script>
<style>
</style>

45
packages/gui/src/view/event.js

@ -0,0 +1,45 @@
import api from './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) => {
console.error('view on error', message)
const key = message.key
if (key === 'server.start') {
handleServerStartError(message.error, app)
}
})
api.on('error', (event, message) => {
console.error('error', event, message)
})
}
function handleServerStartError (err, app) {
if (err.message.indexOf('listen EADDRINUSE') >= 0) {
app.$confirm({
title: '端口被占用,代理服务启动失败',
content: '是否要杀掉占用进程?',
onOk () {
// TODO 杀掉进程
api.config.get().then(config => {
console.log('config', config)
api.shell.killByPort({ port: config.server.port }).then(ret => {
app.$message.info('杀掉进程成功,请重试开启代理服务')
})
})
},
onCancel () {
console.log('Cancel')
}
})
}
}
export default register

4
packages/gui/src/view/index.js

@ -1 +1,5 @@
import './status'
import register from './event'
export default {
register
}

9
packages/gui/src/view/status.js

@ -1,5 +1,3 @@
import api from './api'
import lodash from 'lodash'
const status = {
server: false,
proxy: {
@ -7,11 +5,4 @@ const status = {
npm: false
}
}
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)
})
export default status

Loading…
Cancel
Save