Browse Source

Merge remote-tracking branch 'upstream/master' into build-arm64

# Conflicts:
#	.github/workflows/build-and-release.yml
#	.github/workflows/test-and-upload.yml
build-arm64
王良 3 days ago
parent
commit
ebc05462be
  1. 12
      .github/ISSUE_TEMPLATE/1_BUG_REPORT.md
  2. 5
      .github/ISSUE_TEMPLATE/2_STYLE_ISSUE.md
  3. 6
      .github/ISSUE_TEMPLATE/3_CONFIG_ISSUES.md
  4. 6
      .github/ISSUE_TEMPLATE/4_FEATURE_REQUEST.md
  5. 1
      .github/ISSUE_TEMPLATE/5_OTHERS.md
  6. 4
      .github/PULL_REQUEST_TEMPLATE.md
  7. 184
      .github/workflows/build-and-release.yml
  8. 90
      .github/workflows/npm-run-electron.yml
  9. 207
      .github/workflows/test-and-upload.yml
  10. 16
      .gitignore
  11. 1
      .npmrc
  12. 240
      README.md
  13. 7
      _script/1、installEnv.bat
  14. 5
      _script/1、setupEnv.bat
  15. 2
      _script/2、installProject.bat
  16. 10
      doc/caroot.md
  17. 37
      doc/linux.md
  18. 14
      doc/other.md
  19. 11
      doc/recover.md
  20. 28
      eslint.config.js
  21. 18
      lerna.json
  22. 14
      package.json
  23. 373
      packages/cli/LICENSE
  24. 3
      packages/cli/cli.js
  25. 23
      packages/cli/package.json
  26. 0
      packages/cli/src/banner.txt
  27. 13
      packages/cli/src/index.js
  28. 7
      packages/cli/src/mitmproxy.js
  29. 42
      packages/cli/src/user_config.json5
  30. 3
      packages/core/.babelrc
  31. 1
      packages/core/.eslintignore
  32. 2
      packages/core/index.js
  33. 70
      packages/core/package.json
  34. 32
      packages/core/src/config.js
  35. 164
      packages/core/src/config/index.js
  36. 6
      packages/core/src/event.js
  37. 19
      packages/core/src/expose.js
  38. 2
      packages/core/src/index.js
  39. 6
      packages/core/src/merge.js
  40. 12
      packages/core/src/modules/index.js
  41. 6
      packages/core/src/modules/plugin/git/config.js
  42. 9
      packages/core/src/modules/plugin/git/index.js
  43. 10
      packages/core/src/modules/plugin/index.js
  44. 18
      packages/core/src/modules/plugin/node/config.js
  45. 21
      packages/core/src/modules/plugin/node/index.js
  46. 10
      packages/core/src/modules/plugin/overwall/config.js
  47. 12
      packages/core/src/modules/plugin/overwall/index.js
  48. 4
      packages/core/src/modules/plugin/pip/config.js
  49. 9
      packages/core/src/modules/plugin/pip/index.js
  50. 12
      packages/core/src/modules/proxy/index.js
  51. 18
      packages/core/src/modules/server/index.js
  52. 17
      packages/core/src/shell/index.js
  53. 8
      packages/core/src/shell/scripts/enable-loopback.js
  54. 4
      packages/core/src/shell/scripts/extra-path/index.js
  55. 10
      packages/core/src/shell/scripts/get-npm-env.js
  56. 8
      packages/core/src/shell/scripts/get-system-env.js
  57. 7
      packages/core/src/shell/scripts/kill-by-port.js
  58. 8
      packages/core/src/shell/scripts/set-npm-env.js
  59. 8
      packages/core/src/shell/scripts/set-system-env.js
  60. 33
      packages/core/src/shell/scripts/set-system-proxy/index.js
  61. 17
      packages/core/src/shell/scripts/setup-ca.js
  62. 27
      packages/core/src/shell/shell.js
  63. 6
      packages/core/src/shell/test.js
  64. 5
      packages/core/src/status.js
  65. 14
      packages/core/src/utils/util.log.js
  66. 30
      packages/core/src/utils/util.logger.js
  67. 42
      packages/core/start/user_config.json5
  68. 12
      packages/core/test/httpsVerifyTest.js
  69. 28
      packages/core/test/mergeTest.js
  70. 6
      packages/core/test/regex.test.js
  71. 14
      packages/core/test/requestTest.js
  72. 5
      packages/gui/README.md
  73. 4
      packages/gui/babel.config.js
  74. 81
      packages/gui/package.json
  75. 7
      packages/gui/pkg/after-all-artifact-build.js
  76. 4
      packages/gui/pkg/after-pack.js
  77. 16
      packages/gui/public/index.html
  78. 37
      packages/gui/src/background.js
  79. 6
      packages/gui/src/background/powerMonitor.js
  80. 40
      packages/gui/src/bridge/api/backend.js
  81. 8
      packages/gui/src/bridge/api/open-enable-loopback.js
  82. 9
      packages/gui/src/bridge/auto-start/backend.js
  83. 5
      packages/gui/src/bridge/auto-start/front.js
  84. 8
      packages/gui/src/bridge/backend.js
  85. 11
      packages/gui/src/bridge/error/front.js
  86. 10
      packages/gui/src/bridge/file-selector/backend.js
  87. 7
      packages/gui/src/bridge/file-selector/front.js
  88. 11
      packages/gui/src/bridge/front.js
  89. 8
      packages/gui/src/bridge/mitmproxy.js
  90. 14
      packages/gui/src/bridge/on-close/front.js
  91. 12
      packages/gui/src/bridge/tongji/backend.js
  92. 11
      packages/gui/src/bridge/tongji/front.js
  93. 75
      packages/gui/src/bridge/update/backend.js
  94. 65
      packages/gui/src/bridge/update/front.js
  95. 14
      packages/gui/src/main.js
  96. 7
      packages/gui/src/utils/util.apppath.js
  97. 15
      packages/gui/src/utils/util.log.js
  98. 107
      packages/gui/src/view/App.vue
  99. 15
      packages/gui/src/view/api.js
  100. 29
      packages/gui/src/view/components/container.vue
  101. Some files were not shown because too many files have changed in this diff Show More

12
.github/ISSUE_TEMPLATE/1_BUG_REPORT.md

@ -5,32 +5,34 @@ labels: Bug
---
<!-- 如果搜索过但未找到,请将 `[ ]` 替换为 `[x]` -->
- [ ] 你是否在现有 [Issue列表](/docmirror/dev-sidecar/issues) 中搜索过相同问题,但未找到?
### Ⅰ. 请说明操作系统及DS的版本号:
1. 操作系统:?
2. DS版本号:? <!-- 如:`1.8.6-node17` -->
### Ⅱ. 问题描述:
### Ⅲ. 期望的结果:
### Ⅳ. 如何复现问题?
1. xxx
2. xxx
3. xxx
### Ⅴ. 请提供相关的错误日志,尽可能的详细:(日志文件在 `${user.home}/.dev-sidecar/logs/` 目录下)
```log
```
```
### Ⅵ. 有必要时,请提供 `${user.home}/.dev-sidecar/running.json` 文件内容:
<!-- 请将 'running.json' 文件的内容粘贴在这里,方便我们排查问题是否由配置错误导致。 -->
```json
```

5
.github/ISSUE_TEMPLATE/2_STYLE_ISSUE.md

@ -5,15 +5,14 @@ labels: Style Issue
---
<!-- 如果搜索过但未找到,请将 `[ ]` 替换为 `[x]` -->
- [ ] 你是否在现有 [Issue列表](/docmirror/dev-sidecar/issues) 中搜索过相同问题,但未找到?
### Ⅰ. 请说明操作系统及DS的版本号:
1. 操作系统:?
2. DS版本号:? <!-- 如:`1.8.6-node17` -->
### Ⅱ. 样式问题描述:
### Ⅲ. 样式问题截图:

6
.github/ISSUE_TEMPLATE/3_CONFIG_ISSUES.md

@ -5,6 +5,7 @@ labels: Config Issue
---
### Ⅰ. 你对哪个功能的配置不了解?
<!-- 请选择一个或多个选项,将前面的 `[ ]` 修改为 `[x]` 即可。 -->
- [ ] 拦截设置:
@ -25,11 +26,12 @@ labels: Config Issue
- [ ] pip加速
- [ ] 梯子
### Ⅱ. 请详细描述你的问题:
### Ⅲ. 有必要时,请提供 `${user.home}/.dev-sidecar/running.json` 文件内容:
<!-- 请将 'running.json' 文件的内容粘贴在这里,方便我们排查问题是否由配置错误导致。 -->
```json
```

6
.github/ISSUE_TEMPLATE/4_FEATURE_REQUEST.md

@ -5,13 +5,13 @@ labels: Feature Request
---
### Ⅰ. 请描述你想要的新功能:
<!-- 请简单描述你希望的新功能,例如:"在某某页面,添加一个按钮,点击按钮时,弹出一个某某对话框,用于xxx。" -->
<!-- 请简单描述你希望的新功能,例如:"在某某页面,添加一个按钮,点击按钮时,弹出一个某某对话框,用于xxx。" -->
### Ⅱ. 请描述你心目中新功能的样子:
<!-- 可以讲讲你对新功能的看法,可以解释更多关于该功能的输入和输出的信息,或贴上你设想的界面设计。 -->
<!-- 可以讲讲你对新功能的看法,可以解释更多关于该功能的输入和输出的信息,或贴上你设想的界面设计。 -->
### Ⅲ. 你希望该新功能修复哪个issue?
<!-- 请将相关issue的编号填写在下面,格式如:#123 -->
<!-- 请将相关issue的编号填写在下面,格式如:#123 -->

1
.github/ISSUE_TEMPLATE/5_OTHERS.md

@ -4,4 +4,3 @@ about: 如果不是以上问题,请使用此模板。
---
### 请详细描述你的问题、需求或建议:

4
.github/PULL_REQUEST_TEMPLATE.md

@ -1,9 +1,9 @@
### Ⅰ. 描述此PR的作用:
### Ⅱ. 此PR修复了哪个issue吗?
<!-- 如果是的话, 请在下一行写上 "fixes #xxx",比如:fixes #97 -->
<!-- 如果是的话,请在下一行写上 "fixes #xxx",比如:fixes #97 -->
### Ⅲ. 界面变化截屏
<!-- 如果存在界面上的变化,请截屏展示出来 -->

184
.github/workflows/build-and-release.yml

@ -1,9 +1,11 @@
name: "Build And Release"
name: Build And Release
on:
push:
branches:
- release
tags:
- "*"
- '*'
jobs:
# job 1
@ -12,84 +14,72 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [
windows,
ubuntu,
macos
]
node: [
16,
17
]
os:
- windows
- ubuntu
- macos
node:
- 22
steps:
- name: "Checkout"
- name: Checkout
uses: actions/checkout@v4.1.7
- name: "Setup Node.js environment"
uses: actions/setup-node@v4.0.3
- name: 'Setup Node.js "${{ matrix.node }}.x" environment'
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
registry-url: https://npm.pkg.github.com/
- name: "Get package info"
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Python environment (Mac) Because of electron-builder install-app-deps requires Python setup tools
if: matrix.os == 'macos'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Get package info
id: package-info
uses: luizfelipelaviola/get-package-info@v1
with:
path: ./packages/mitmproxy
- name: "Print"
- name: Print
run: |
echo "version = ${{ steps.package-info.outputs.version }}";
echo "github.ref_type = ${{ github.ref_type }}";
echo "github.ref = ${{ github.ref }}";
echo "github.ref_name = ${{ github.ref_name }}";
- name: "npm install -g lerna@6/yarn"
run: |
echo "======================================================================";
echo "npm install -g lerna@6";
echo "--------------------";
npm install -g lerna@6;
echo "======================================================================";
echo "npm install -g yarn";
echo "--------------------";
npm install -g yarn;
- name: "lerna -v | npm -v | yarn -v | yarn config list"
- name: 'npm -v | pnpm -v | python --version'
run: |
echo "======================================================================";
echo "lerna -v";
echo "--------------------";
lerna -v;
echo "======================================================================";
echo "npm -v";
echo "--------------------";
npm -v;
echo "======================================================================";
echo "yarn -v";
echo "pnpm -v";
echo "--------------------";
yarn -v;
pnpm -v;
echo "======================================================================";
echo "yarn config list";
echo "python --version";
echo "--------------------";
yarn config list;
python --version;
- name: "lerna bootstrap"
- name: pnpm install
run: |
echo "======================================================================";
dir || ls -lah;
echo "======================================================================";
echo "lerna bootstrap --npm-client=yarn";
echo "pnpm install";
echo "--------------------";
lerna bootstrap --npm-client=yarn;
pnpm install;
- name: "npm run electron:build for node16"
if: ${{ matrix.node == '16' }}
- name: 'npm run electron:build'
run: |
echo "======================================================================";
echo "cd packages/gui";
@ -114,61 +104,33 @@ jobs:
cd ../../../;
dir || ls -lah;
- name: "npm run electron:build for node17"
if: ${{ matrix.node != '16' }}
env:
NODE_OPTIONS: --openssl-legacy-provider
run: |
echo "======================================================================";
echo "cd packages/gui";
echo "--------------------";
cd packages/gui;
dir || ls -lah;
echo "======================================================================";
echo "npm run electron:build";
echo "--------------------";
npm run electron:build;
echo "======================================================================";
echo "cd dist_electron";
echo "--------------------";
cd dist_electron;
dir || ls -lah;
echo "======================================================================";
echo "cd ../../../";
echo "--------------------";
cd ../../../;
dir || ls -lah;
- name: "Upload DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.exe - Windows"
- name: 'Upload DevSidecar-${{ steps.package-info.outputs.version }}.exe - Windows'
uses: actions/upload-artifact@v4.4.0
if: ${{ matrix.os == 'windows' }}
with:
path: packages/gui/dist_electron/DevSidecar-${{ steps.package-info.outputs.version }}.exe
name: "DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.exe"
name: 'DevSidecar-${{ steps.package-info.outputs.version }}.exe'
if-no-files-found: error
- name: "Upload DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.deb - Ubuntu"
- name: 'Upload DevSidecar-${{ steps.package-info.outputs.version }}.deb - Ubuntu'
uses: actions/upload-artifact@v4.4.0
if: ${{ matrix.os == 'ubuntu' }}
with:
path: packages/gui/dist_electron/DevSidecar-${{ steps.package-info.outputs.version }}.deb
name: "DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.deb"
name: 'DevSidecar-${{ steps.package-info.outputs.version }}.deb'
if-no-files-found: error
- name: "Upload DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.AppImage - Ubuntu"
- name: 'Upload DevSidecar-${{ steps.package-info.outputs.version }}.AppImage - Ubuntu'
uses: actions/upload-artifact@v4.4.0
if: ${{ matrix.os == 'ubuntu' }}
with:
path: packages/gui/dist_electron/DevSidecar-${{ steps.package-info.outputs.version }}.AppImage
name: "DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.AppImage"
name: 'DevSidecar-${{ steps.package-info.outputs.version }}.AppImage'
if-no-files-found: error
- name: "Upload DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.dmg - Mac"
- name: 'Upload DevSidecar-${{ steps.package-info.outputs.version }}.dmg - Mac'
uses: actions/upload-artifact@v4.4.0
if: ${{ matrix.os == 'macos' }}
with:
path: packages/gui/dist_electron/DevSidecar-${{ steps.package-info.outputs.version }}.dmg
name: "DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.dmg"
name: 'DevSidecar-${{ steps.package-info.outputs.version }}.dmg'
if-no-files-found: error
# job 2
@ -177,80 +139,46 @@ jobs:
needs:
- build-and-upload
steps:
- name: "Checkout"
- name: Checkout
uses: actions/checkout@v4.1.7
- name: "Get package info"
- name: Get package info
id: package-info
uses: luizfelipelaviola/get-package-info@v1
with:
path: ./packages/mitmproxy
- name: "Make 'release' dir"
- name: 'Make "release" dir'
run: mkdir release
# Download artifacts with node16
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node16.exe - Windows"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node16.exe
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node16.deb - Ubuntu"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node16.deb
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node16.AppImage - Ubuntu"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node16.AppImage
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node16.dmg - Mac"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node16.dmg
path: release
- name: "Rename 'release/DevSidecar-${{ steps.package-info.outputs.version }}.*' to 'release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.*'"
run: |
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.exe release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.exe;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.deb release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.deb;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.AppImage release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.AppImage;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.dmg release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.dmg;
# Download artifacts with node17
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node17.exe - Windows"
# Download artifacts
- name: 'Download DevSidecar-${{ steps.package-info.outputs.version }}.exe - Windows'
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node17.exe
name: DevSidecar-${{ steps.package-info.outputs.version }}.exe
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node17.deb - Ubuntu"
- name: 'Download DevSidecar-${{ steps.package-info.outputs.version }}.deb - Ubuntu'
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node17.deb
name: DevSidecar-${{ steps.package-info.outputs.version }}.deb
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node17.AppImage - Ubuntu"
- name: 'Download DevSidecar-${{ steps.package-info.outputs.version }}.AppImage - Ubuntu'
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node17.AppImage
name: DevSidecar-${{ steps.package-info.outputs.version }}.AppImage
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node17.dmg - Mac"
- name: 'Download DevSidecar-${{ steps.package-info.outputs.version }}.dmg - Mac'
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node17.dmg
name: DevSidecar-${{ steps.package-info.outputs.version }}.dmg
path: release
- name: "Rename 'release/DevSidecar-${{ steps.package-info.outputs.version }}.*' to 'release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.*'"
run: |
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.exe release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.exe;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.deb release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.deb;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.AppImage release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.AppImage;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.dmg release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.dmg;
- name: "Print files from 'release' dir"
- name: 'Print files from "release" dir'
run: |
ls -lah release;
- name: "Create a draft release"
uses: fnkr/github-action-ghr@ghr-options
- name: Create a draft release
uses: wangliang181230/github-action-ghr@master
env:
GITHUB_TOKEN: ${{ github.token }}
GHR_PATH: release/

90
.github/workflows/npm-run-electron.yml

@ -0,0 +1,90 @@
name: npm run electron
on:
push:
branches:
- run
jobs:
npm-run-electron:
runs-on: ${{ matrix.os }}-latest
strategy:
fail-fast: false
matrix:
os:
- windows
- ubuntu
- macos
node:
- 22
steps:
- name: Checkout
uses: actions/checkout@v4.1.7
- name: 'Setup Node.js "${{ matrix.node }}.x" environment'
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
registry-url: https://npm.pkg.github.com/
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Python environment (Mac) Because of electron-builder install-app-deps requires Python setup tools
if: matrix.os == 'macos'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Get package info
id: package-info
uses: luizfelipelaviola/get-package-info@v1
with:
path: ./packages/mitmproxy
- name: Print
run: |
echo "version = ${{ steps.package-info.outputs.version }}";
echo "github.ref_type = ${{ github.ref_type }}";
echo "github.ref = ${{ github.ref }}";
echo "github.ref_name = ${{ github.ref_name }}";
- name: 'npm -v | pnpm -v | python --version'
run: |
echo "======================================================================";
echo "npm -v";
echo "--------------------";
npm -v;
echo "======================================================================";
echo "pnpm -v";
echo "--------------------";
pnpm -v;
echo "======================================================================";
echo "python --version";
echo "--------------------";
python --version;
- name: pnpm install
run: |
echo "======================================================================";
dir || ls -lah;
echo "======================================================================";
echo "pnpm install";
echo "--------------------";
pnpm install;
- name: 'npm run electron'
run: |
echo "======================================================================";
echo "cd packages/gui";
echo "--------------------";
cd packages/gui;
dir || ls -lah;
echo "======================================================================";
echo "npm run electron";
echo "--------------------";
npm run electron;

207
.github/workflows/test-and-upload.yml

@ -1,13 +1,14 @@
name: "Test And Upload"
name: Test And Upload
on:
push:
branches:
- master
- release
- 1.x
pull_request:
branches:
- master
- 1.x
jobs:
test-and-upload:
@ -15,84 +16,72 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [
windows,
ubuntu,
macos
]
node: [
16,
17
]
os:
- windows
- ubuntu
- macos
node:
- 22
steps:
- name: "Checkout"
- name: Checkout
uses: actions/checkout@v4.1.7
- name: "Setup Node.js environment"
uses: actions/setup-node@v4.0.3
- name: 'Setup Node.js "${{ matrix.node }}.x" environment'
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
registry-url: https://npm.pkg.github.com/
- name: "Get package info"
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Python environment (Mac) Because of electron-builder install-app-deps requires Python setup tools
if: matrix.os == 'macos'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Get package info
id: package-info
uses: luizfelipelaviola/get-package-info@v1
with:
path: ./packages/mitmproxy
- name: "Print"
- name: Print
run: |
echo "version = ${{ steps.package-info.outputs.version }}";
echo "github.ref_type = ${{ github.ref_type }}";
echo "github.ref = ${{ github.ref }}";
echo "github.ref_name = ${{ github.ref_name }}";
- name: "npm install -g lerna@6/yarn"
run: |
echo "======================================================================";
echo "npm install -g lerna@6";
echo "--------------------";
npm install -g lerna@6;
echo "======================================================================";
echo "npm install -g yarn";
echo "--------------------";
npm install -g yarn;
- name: "lerna -v | npm -v | yarn -v | yarn config list"
- name: 'npm -v | pnpm -v | python --version'
run: |
echo "======================================================================";
echo "lerna -v";
echo "--------------------";
lerna -v;
echo "======================================================================";
echo "npm -v";
echo "--------------------";
npm -v;
echo "======================================================================";
echo "yarn -v";
echo "pnpm -v";
echo "--------------------";
yarn -v;
pnpm -v;
echo "======================================================================";
echo "yarn config list";
echo "python --version";
echo "--------------------";
yarn config list;
python --version;
- name: "lerna bootstrap"
- name: pnpm install
run: |
echo "======================================================================";
dir || ls -lah;
echo "======================================================================";
echo "lerna bootstrap --npm-client=yarn";
echo "pnpm install";
echo "--------------------";
lerna bootstrap --npm-client=yarn;
pnpm install;
- name: "npm run electron:build for node16"
if: ${{ matrix.node == '16' }}
- name: 'npm run electron:build'
run: |
echo "======================================================================";
echo "cd packages/gui";
@ -117,147 +106,31 @@ jobs:
cd ../../../;
dir || ls -lah;
- name: "npm run electron:build for node17"
if: ${{ matrix.node != '16' }}
env:
NODE_OPTIONS: --openssl-legacy-provider
run: |
echo "======================================================================";
echo "cd packages/gui";
echo "--------------------";
cd packages/gui;
dir || ls -lah;
echo "======================================================================";
echo "npm run electron:build";
echo "--------------------";
npm run electron:build;
echo "======================================================================";
echo "cd dist_electron";
echo "--------------------";
cd dist_electron;
dir || ls -lah;
echo "======================================================================";
echo "cd ../../../";
echo "--------------------";
cd ../../../;
dir || ls -lah;
- name: "Upload DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.exe - Windows"
- name: 'Upload DevSidecar-${{ steps.package-info.outputs.version }}.exe - Windows'
uses: actions/upload-artifact@v4.4.0
if: ${{ matrix.os == 'windows' }}
with:
path: packages/gui/dist_electron/DevSidecar-${{ steps.package-info.outputs.version }}.exe
name: "DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.exe"
name: 'DevSidecar-${{ steps.package-info.outputs.version }}.exe'
if-no-files-found: error
- name: "Upload DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.deb - Ubuntu"
- name: 'Upload DevSidecar-${{ steps.package-info.outputs.version }}.deb - Ubuntu'
uses: actions/upload-artifact@v4.4.0
if: ${{ matrix.os == 'ubuntu' }}
with:
path: packages/gui/dist_electron/DevSidecar-${{ steps.package-info.outputs.version }}.deb
name: "DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.deb"
name: 'DevSidecar-${{ steps.package-info.outputs.version }}.deb'
if-no-files-found: error
- name: "Upload DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.AppImage - Ubuntu"
- name: 'Upload DevSidecar-${{ steps.package-info.outputs.version }}.AppImage - Ubuntu'
uses: actions/upload-artifact@v4.4.0
if: ${{ matrix.os == 'ubuntu' }}
with:
path: packages/gui/dist_electron/DevSidecar-${{ steps.package-info.outputs.version }}.AppImage
name: "DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.AppImage"
name: 'DevSidecar-${{ steps.package-info.outputs.version }}.AppImage'
if-no-files-found: error
- name: "Upload DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.dmg - Mac"
- name: 'Upload DevSidecar-${{ steps.package-info.outputs.version }}.dmg - Mac'
uses: actions/upload-artifact@v4.4.0
if: ${{ matrix.os == 'macos' }}
with:
path: packages/gui/dist_electron/DevSidecar-${{ steps.package-info.outputs.version }}.dmg
name: "DevSidecar-${{ steps.package-info.outputs.version }}-node${{ matrix.node }}.dmg"
name: 'DevSidecar-${{ steps.package-info.outputs.version }}.dmg'
if-no-files-found: error
# job 2
download-and-release:
if: ${{ github.ref_name == 'release' }}
runs-on: ubuntu-latest
needs:
- test-and-upload
steps:
- name: "Checkout"
uses: actions/checkout@v4.1.7
- name: "Get package info"
id: package-info
uses: luizfelipelaviola/get-package-info@v1
with:
path: ./packages/mitmproxy
- name: "Make 'release' dir"
run: mkdir release
# Download artifacts with node16
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node16.exe - Windows"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node16.exe
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node16.deb - Ubuntu"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node16.deb
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node16.AppImage - Ubuntu"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node16.AppImage
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node16.dmg - Mac"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node16.dmg
path: release
- name: "Rename 'release/DevSidecar-${{ steps.package-info.outputs.version }}.*' to 'release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.*'"
run: |
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.exe release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.exe;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.deb release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.deb;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.AppImage release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.AppImage;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.dmg release/DevSidecar-${{ steps.package-info.outputs.version }}-node16.dmg;
# Download artifacts with node17
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node17.exe - Windows"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node17.exe
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node17.deb - Ubuntu"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node17.deb
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node17.AppImage - Ubuntu"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node17.AppImage
path: release
- name: "Download DevSidecar-${{ steps.package-info.outputs.version }}-node17.dmg - Mac"
uses: actions/download-artifact@v4.1.8
with:
name: DevSidecar-${{ steps.package-info.outputs.version }}-node17.dmg
path: release
- name: "Rename 'release/DevSidecar-${{ steps.package-info.outputs.version }}.*' to 'release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.*'"
run: |
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.exe release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.exe;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.deb release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.deb;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.AppImage release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.AppImage;
mv release/DevSidecar-${{ steps.package-info.outputs.version }}.dmg release/DevSidecar-${{ steps.package-info.outputs.version }}-node17.dmg;
- name: "Print files from 'release' dir"
run: |
ls -lah release;
- name: "Create a draft release"
uses: wangliang181230/github-action-ghr@master
env:
GITHUB_TOKEN: ${{ github.token }}
GHR_PATH: release/
GHR_TITLE: ${{ github.ref_name }}
GHR_REPLACE: true
GHR_DRAFT: true

16
.gitignore vendored

@ -1,11 +1,21 @@
# IntelliJ project files
.idea
*.iml
out
gen
# vscode settings files
.vscode
# Mac
.DS_Store
# Node files
node_modules/
*.lock
*.log
pnpm-lock.yaml
package-lock.json
# Other files
out
gen
*.log
*.lnk

1
.npmrc

@ -0,0 +1 @@
shamefully-hoist=true

240
README.md

@ -1,94 +1,91 @@
# dev-sidecar
开发者边车,命名取自service-mesh的service-sidecar,意为为开发者打辅助的边车工具(以下简称ds)
通过本地代理的方式将https请求代理到一些国内的加速通道上
<a href='https://github.com/docmirror/dev-sidecar'><img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github"></a>
`<a href='https://github.com/docmirror/dev-sidecar'><img alt="GitHub stars" src="https://img.shields.io/github/stars/docmirror/dev-sidecar?logo=github">``</a>`
>
> Gitee上的同步项目已被封禁,此项目将不再更新与维护 【狗头保命】
>
> 我将继续奋战在开源一线,为社区贡献更多更好的开源项目。
> 感兴趣的可以关注我的主页 [【github】](https://github.com/greper) [【gitee】](https://gitee.com/greper)
>
## 打个广告
>
> https://github.com/certd/certd
> 我的开源证书管理工具项目,全自动申请和部署证书,有需求的可以去试试,帮忙点个star
## 打个广告
> [https://github.com/certd/certd](https://github.com/certd/certd)
> 我的开源证书管理工具项目,全自动申请和部署证书,有需求的可以去试试,帮忙点个star
## 重要提醒
> ------------------------------重要提醒1---------------------------------
>
> 注意:由于electron无法监听windows的关机事件,开着ds情况下直接重启电脑,会导致无法上网,你可以手动启动ds即可恢复网络,你也可以将ds设置为开机自启。
>
> 关于此问题的更多讨论请前往:
> https://github.com/docmirror/dev-sidecar/issues/109
>
> [https://github.com/docmirror/dev-sidecar/issues/109](https://github.com/docmirror/dev-sidecar/issues/109)
> ------------------------------重要提醒2---------------------------------
>
> 注意:本应用启动会自动修改系统代理,所以会与其他代理软件有冲突,请务必不要一起使用。
> 本应用主要目的在于直连访问github,如果你已经有飞机了,那建议还是不要用这个自行车(ds)了
>
## 一、 特性
### 1.1、 dns优选(解决***污染问题)
* 根据网络状况智能解析最佳域名ip地址,获取最佳网络速度
* 解决一些网站和库无法访问或访问速度慢的问题
* 建议遇到打开比较慢的国外网站,可以优先尝试将该域名添加到dns设置中(注意:被***封杀的无效)
### 1.1、 dns优选(解决\*\*\*污染问题)
- 根据网络状况智能解析最佳域名ip地址,获取最佳网络速度
- 解决一些网站和库无法访问或访问速度慢的问题
- 建议遇到打开比较慢的国外网站,可以优先尝试将该域名添加到dns设置中(注意:被\*\*\*封杀的无效)
### 1.2、 请求拦截
* 拦截打不开的网站,代理到加速镜像站点上去。
* 可配置多个镜像站作为备份
* 具备测速机制,当访问失败或超时之后,自动切换到备用站点,使得目标服务高可用
- 拦截打不开的网站,代理到加速镜像站点上去。
- 可配置多个镜像站作为备份
- 具备测速机制,当访问失败或超时之后,自动切换到备用站点,使得目标服务高可用
### 1.3、 github加速
* github 直连加速 (通过修改sni实现,感谢 [fastGithub](https://github.com/dotnetcore/FastGithub) 提供的思路)
* release、source、zip下载加速
* clone 加速
* 头像加速
* 解决readme中图片引用无法加载的问题
* gist.github.com 加速
* 解决git push 偶尔失败需要输入账号密码的问题(fatal: TaskCanceledException encountered / fatal: HttpRequestException encountered)
* raw/blame加速
- github 直连加速 (通过修改sni实现,感谢 [fastGithub](https://github.com/dotnetcore/FastGithub) 提供的思路)
- release、source、zip下载加速
- clone 加速
- 头像加速
- 解决readme中图片引用无法加载的问题
- gist.github.com 加速
- 解决git push 偶尔失败需要输入账号密码的问题(fatal: TaskCanceledException encountered / fatal: HttpRequestException encountered)
- raw/blame加速
> 以上部分功能通过 `X.I.U` 的油猴脚本实现, 以下是仓库和脚本下载链接,大家可以去支持一下。
> * https://github.com/XIU2/UserScript
> * https://greasyfork.org/scripts/412245
>
> - [https://github.com/XIU2/UserScript](https://github.com/XIU2/UserScript)
> - [https://greasyfork.org/scripts/412245](https://greasyfork.org/scripts/412245)
>
> 由于此脚本在ds中是打包在本地的,更新会不及时,你可以直接通过浏览器安装油猴插件使用此脚本,从而获得最新更新(ds本地的可以通过 `加速服务->基本设置->启用脚本` 进行关闭)。
### 1.4、 Stack Overflow 加速
* 将ajax.google.com代理到加速CDN上
* recaptcha 图片验证码加速
### 1.5、 npm加速
* 支持开启npm代理
* 官方与淘宝npm registry一键切换,
* 某些npm install的时候,并且使用cnpm也无法安装时,可以尝试开启npm代理再试
- 将ajax.google.com代理到加速CDN上
- recaptcha 图片验证码加速
***安全警告***:
* 请勿使用来源不明的服务地址,有隐私和账号泄露风险
* 本应用及服务端承诺不收集任何信息。介意者请使用安全模式。
### 1.5、 npm加速
- 支持开启npm代理
- 官方与淘宝npm registry一键切换
- 某些npm install的时候,并且使用cnpm也无法安装时,可以尝试开启npm代理再试
**_安全警告_**:
- 请勿使用来源不明的服务地址,有隐私和账号泄露风险
- 本应用及服务端承诺不收集任何信息。介意者请使用安全模式。
## 二、快速开始
支持windows、Mac、Linux(Ubuntu)
### 2.1、DevSidecar桌面应用
#### 1)下载安装包
* release下载
- release下载
[Github Release](https://github.com/docmirror/dev-sidecar/releases)
> Windows: 请选择DevSidecar-x.x.x.exe
@ -100,7 +97,6 @@
> 注意:由于没有买应用证书,所以应用在下载安装时会有“未知发行者”等安全提示,选择保留即可。
#### 2)安装后打开
> 注意:mac版安装需要在“系统偏好设置->安全性与隐私->通用”中解锁并允许应用安装
@ -116,64 +112,67 @@
> 根证书是本地随机生成的,所以不用担心根证书的安全问题(本应用不收集任何用户信息)
> 你也可以在加速服务设置中自定义根证书(PEM格式的证书与私钥)
> 火狐浏览器需要[手动安装证书](#3浏览器打开提示证书不受信任)
#### 4)开始加速吧
去试试打开github
去试试打开github
### 2.2、开启前 vs 开启后
| | 开启前 | 开启后 |
| ---- | ---- | ---- |
| -------- | ------------------------------ | ------------------------------------------------- |
| 头像 | ![](./doc/avatar2.png) | ![](./doc/avatar1.png) |
| clone | ![](./doc/clone-before.png) | ![](./doc/clone.png) |
| zip 下载 | ![](./doc/download-before.png) | ![](./doc/download.png)秒下的,实在截不到速度的图 |
## 三、模式说明
### 3.1、安全模式
* 此模式:关闭拦截、关闭增强、开启dns优选、开启测速
* 最安全,无需安装证书,可以在浏览器地址栏左侧查看域名证书
* 功能也最弱,只有特性1,相当于查询github的国外ip,手动改hosts一个意思。
* github的可访问性不稳定,取决于IP测速,如果有绿色ip存在,就 `有可能` 可以直连访问。
- 此模式:关闭拦截、关闭增强、开启dns优选、开启测速
- 最安全,无需安装证书,可以在浏览器地址栏左侧查看域名证书
- 功能也最弱,只有特性1,相当于查询github的国外ip,手动改hosts一个意思。
- github的可访问性不稳定,取决于IP测速,如果有绿色ip存在,就 `有可能` 可以直连访问。
![](./doc/speed.png)
### 3.2、默认模式
* 此模式:开启拦截、关闭增强、开启dns优选、开启测速
* 需要安装证书,通过修改sni直连访问github
* 功能上包含特性1/2/3/4。
- 此模式:开启拦截、关闭增强、开启dns优选、开启测速
- 需要安装证书,通过修改sni直连访问github
- 功能上包含特性1/2/3/4。
## 四、 最佳实践
* 把dev-sidecar一直开着就行了(注意windows下开着ds重启电脑,会无法上网,重新打开ds即可。)
* 建议遇到打开比较慢的国外网站,可以尝试将该域名添加到dns设置中(注意:被***封杀的无效)
- 把dev-sidecar一直开着就行了(注意windows下开着ds重启电脑,会无法上网,重新打开ds即可。)
- 建议遇到打开比较慢的国外网站,可以尝试将该域名添加到dns设置中(注意:被\*\*\*封杀的无效)
### 其他加速
#### 1)git clone 加速
方式1:快捷复制:
- 方式1:快捷复制:
> 开启脚本支持,然后在复制clone链接下方,即可复制到加速链接
方式2:
> 使用方式用实际的名称替换{}的内容,即可加速clone
> https://hub.fastgit.org/{username}/{reponame}.git
> clone 出来的 remote "origin" 为fastgit的地址,需要手动改回来
> 你也可以直接使用他们的clone加速工具 [fgit-go](https://github.com/FastGitORG/fgit-go)
- 方式2:
#### 2)github.com的镜像网站(注意:不能登录)
> 1. [hub.fastgit.org](https://hub.fastgit.org/)
> 2. [github.com.cnpmjs.org](https://github.com.cnpmjs.org/) 这个很容易超限
> 1. 使用方式:用实际的名称替换 `{}` 的内容,即可加速clone [https://hub.fastgit.org/{username}/{reponame}.git](https://hub.fastgit.org/%7Busername%7D/%7Breponame%7D.git)
> 2. clone 出来的 remote "origin" 为fastgit的地址,需要手动改回来
> 3. 你也可以直接使用他们的clone加速工具 [fgit-go](https://github.com/FastGitORG/fgit-go)
#### 2)`github.com` 的镜像网站(注意:部分镜像网站不能登录)
> 1. [hub.fastgit.org](https://hub.fastgit.org/) (2024/11/18:这个好像失效了?)
> 2. [github.com.cnpmjs.org](https://github.com.cnpmjs.org/) 这个很容易超限(2024/11/18:这个好像失效了?)
> 3. [dgithub.xyz](https://dgithub.xyz/)
## 五、api
### 5.1、拦截配置
没有配置域名的不会拦截,其他根据配置进行拦截处理
```js
const intercepts = {
// 要拦截的域名
@ -212,6 +211,7 @@ const intercepts = {
```
### 5.2、DNS优选配置
某些域名解析出来的ip会无法访问,(比如api.github.com会被解析到新加坡的ip上,新加坡的服务器在上午挺好,到了晚上就卡死,基本不可用)
通过从dns上获取ip列表,切换不同的ip进行尝试,最终会挑选到一个最快的ip
@ -226,12 +226,13 @@ const intercepts = {
}
}
```
注意:暂时只支持IPv4的解析
注意:暂时只支持IPv4的解析
## 六、问题排查
### 6.1、dev-sidecar的前两个开关没有处于打开状态
1. 尝试将开关按钮手动打开
2. 请尝试右键dev-sidecar图标,点退出。再重新打开
3. 如果还不行,请将日志发送给作者
@ -239,38 +240,34 @@ const intercepts = {
如果是mac系统,可能是下面的原因
#### 1)Mac系统使用时,首页的系统代理开关无法打开
出现这个问题可能是没有开启系统代理命令的执行权限
```
networksetup -setwebproxy 'WiFi' 127.0.0.1 31181
#看是否有如下错误提示
** Error: Command requires admin privileges.
```
如果有上面的错误提示,请尝试如下方法:
> 取消访问偏好设置需要管理员密码
> 系统偏好设置—>安全性与隐私—> 通用—> 高级—> 访问系统范围的偏好设置需要输入管理员密码(取消勾选)
### 6.2、没有加速效果
> 本应用仅支持https加速,请务必确认你访问的网站地址是https开头的
1. 本应用仅支持https加速
请务必确认你访问的地址是https开头的
比如: https://github.com/
比如: [https://github.com/](https://github.com/)
2. 检查浏览器是否装了什么插件,与ds有冲突
3. 检查是否安装了其他代理软件,与ds有冲突
4. 请确认浏览器的代理设置为使用IE代理/或者使用系统代理状态
6. 可以尝试换个浏览器试试
7. 请确认网络代理设置处于勾选状态
5. 可以尝试换个浏览器试试
6. 请确认网络代理设置处于勾选状态
正常情况下ds在“系统代理”开关打开时,会自动设置系统代理。
### 6.3、浏览器打开提示证书不受信任
![](./doc/crt-error.png)
@ -278,65 +275,71 @@ networksetup -setwebproxy 'WiFi' 127.0.0.1 31181
#### 1)windows: 请确认证书已正确安装在“信任的根证书颁发机构”下
#### 2)mac: 请确认证书已经被安装并已经设置信任
#### 2)mac: 请确认证书已经被安装并已经设置信任
#### 3)火狐浏览器:火狐浏览器不走系统的根证书,需要在选项中添加根证书
> 1、火狐浏览器->选项->隐私与安全->证书->查看证书
> 2、证书颁发机构->导入
> 3、选择证书文件`C:\Users(用户)\Administrator(你的账号)\.dev-sidecar\dev-sidecar.ca.crt`(Mac或linux为`~/.dev-sidecar`目录)
> 4、勾选信任由此证书颁发机构来标识网站,确定即可
1. 火狐浏览器->选项->隐私与安全->证书->查看证书
2. 证书颁发机构->导入
3. 选择证书文件 `C:\Users(用户)\Administrator(你的账号)\.dev-sidecar\dev-sidecar.ca.crt`(Mac或linux为 `~/.dev-sidecar` 目录)
4. 勾选信任由此证书颁发机构来标识网站,确定即可
### 6.4、打开github显示连接超时
```html
DevSidecar Warning:
Error: www.github.com:443, 代理请求超时
DevSidecar Warning: Error: www.github.com:443, 代理请求超时
```
1、检查测速界面github.com是否有ip ,如果没有ip,则可能是由于你的网络提供商封锁了dns服务商的ip(试试能否ping通:1.1.1.1 / 9.9.9.9 )
2、如果是安全模式,则是因为不稳定导致的,等一会再刷新试试
3、如果是增强模式,则是由于访问人数过多,正常现象
1. 检查测速界面github.com是否有ip ,如果没有ip,则可能是由于你的网络提供商封锁了dns服务商的ip(试试能否ping通:1.1.1.1 / 9.9.9.9 )
2. 如果是安全模式,则是因为不稳定导致的,等一会再刷新试试
3. 如果是增强模式,则是由于访问人数过多,正常现象
### 6.5、查看日志是否有报错
如果还是不行,请在下方加作者好友,将服务日志发送给作者进行分析
日志打开方式:加速服务->右边日志按钮->打开日志文件夹
![](./doc/log.png)
### 6.6、某些原本可以打开的网站打不开了
1、可以尝试关闭pac
2、可以将域名加入白名单
1. 可以尝试关闭pac
2. 可以将域名加入白名单
### 6.7、应用意外关闭导致没有网络了
应用开启后会自动修改系统代理设置,正常退出会自动关闭系统代理
当应用意外关闭时,可能会因为没有将系统代理恢复,从而导致完全无法上网。
对于此问题有如下几种解决方案可供选择:
1、重新打开应用即可(右键应用托盘图标可完全退出,将会正常关闭系统代理设置)
2、如果应用被卸载了,此时需要[手动关闭系统代理设置](./doc/recover.md)
3、如果你是因为开着ds的情况下重启电脑导致无法上网,你可以设置ds为开机自启
1. 重新打开应用即可(右键应用托盘图标可完全退出,将会正常关闭系统代理设置)
2. 如果应用被卸载了,此时需要[手动关闭系统代理设置](./doc/recover.md)
3. 如果你是因为开着ds的情况下重启电脑导致无法上网,你可以设置ds为开机自启
### 6.8、卸载应用后上不了网,git请求不了
如果你在卸载应用前,没有正常退出app,就有可能无法上网。请按如下步骤操作恢复您的网络:
1、关闭系统代理设置,参见:[手动关闭系统代理设置](./doc/recover.md)
2、执行下面的命令关闭git的代理设置(如果你开启过 `Git.exe代理` 的开关)
```shell
git config --global --unset http.proxy
git config --global --unset https.proxy
git config --global --unset http.sslVerify
```
3、执行下面的命令关闭npm的代理设置(如果你开启过npm加速的开关)
```shell
npm config delete proxy
npm config delete https-proxy
```
## 七、在其他程序使用
* [java程序使用](./doc/other.md#Java程序使用)
- [java程序使用](./doc/other.md#Java程序使用)
## 八、贡献代码
@ -344,80 +347,85 @@ npm config delete https-proxy
#### 1)安装 `nodejs`
推荐安装 nodejs `16.x.x` 的版本,其他版本未做测试
推荐安装 nodejs `22.x.x` 的版本,其他版本未做测试
#### 2)安装 `lerna`
#### 2)安装 `pnpm`
运行如下命令即可安装所需依赖:
> 注:lerna指定为6.x版本,更高版本会导致打包失败(不兼容导致)
```shell
npm install -g cnpm --registry=https://registry.npmmirror.com
cnpm install -g lerna@6
```shell
npm install -g pnpm --registry=https://registry.npmmirror.com
```
### 8.2、开发调试模式启动
运行如下命令即可开发模式启动
```shell
# 拉取代码
git clone https://github.com/docmirror/dev-sidecar
cd dev-sidecar
# 注意不要使用 `npm install` 来安装依赖,因为 `lerna bootstrap` 会自动安装依赖
lerna bootstrap
# 如果 `lerna bootstrap` 有报错,可以尝试执行如下两行命令,用yarn替换掉npm:
#cnpm install -g yarn
#lerna bootstrap --npm-client=yarn
# 注意不要使用 `npm install` 来安装依赖,因为 `pnpm` 会自动安装依赖
pnpm install
# 运行DevSidecar
cd packages/gui
npm run electron
```
> 如果electron依赖包下载不动,可以开启ds的npm加速
### 8.3、打包成可执行文件
```shell
# 先执行上面的步骤,然后运行如下命令打包成可执行文件
npm run electron:build
```
### 8.4、提交pr
如果你想将你的修改贡献出来,请提交pr
如果你想将你的修改贡献出来,请提交pr
## 九、联系作者
欢迎bug反馈,需求建议,技术交流等
1、 加群(请备注dev-sidecar,或简称DS)
- QQ 1群:390691483,人数:500 / 500(满)
- QQ 2群:[667666069](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=n4nksr4sji93vZtD5e8YEHRT6qbh6VyQ&authKey=XKBZnzmoiJrAFyOT4V%2BCrgX5c13ds59b84g%2FVRhXAIQd%2FlAiilsuwDRGWJct%2B570&noverify=0&group_code=667666069),人数:447 / 500
- QQ 3群:419807815,人数:500 / 500(满)
- QQ 4群:[438148299](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=i_NCBB5f_Bkm2JsEV1tLs2TkQ79UlCID&authKey=nMsVJbJ6P%2FGNO7Q6vsVUadXRKnULUURwR8zvUZJnP3IgzhHYPhYdcBCHvoOh8vYr&noverify=0&group_code=438148299),人数:203 / 1000
- QQ 5群:[767622917](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=nAWi_Rxj7mM4Unp5LMiatmUWhGimtbcB&authKey=aswmlWGjbt3GIWXtvjB2GJqqAKuv7hWjk6UBs3MTb%2Biyvr%2Fsbb1kA9CjF6sK7Hgg&noverify=0&group_code=767622917),人数:016 / 200(new)
## 十、求star
我的其他项目求star
* [fast-crud](https://github.com/fast-crud/fast-crud) : 开发crud快如闪电
* [certd](https://github.com/certd/certd) : 让你的证书永不过期
* [trident-sync](https://github.com/handsfree-work/trident-sync) : 二次开发项目同步升级工具
- [fast-crud](https://github.com/fast-crud/fast-crud) : 开发crud快如闪电
- [certd](https://github.com/certd/certd) : 让你的证书永不过期
- [trident-sync](https://github.com/handsfree-work/trident-sync) : 二次开发项目同步升级工具
## 十一、感谢
本项目使用lerna包管理工具
[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org/)
本项目参考如下开源项目
* [node-mitmproxy](https://github.com/wuchangming/node-mitmproxy)
* [ReplaceGoogleCDN](https://github.com/justjavac/ReplaceGoogleCDN)
- [node-mitmproxy](https://github.com/wuchangming/node-mitmproxy)
- [ReplaceGoogleCDN](https://github.com/justjavac/ReplaceGoogleCDN)
特别感谢
* [github增强油猴脚本](https://greasyfork.org/zh-CN/scripts/412245-github-%E5%A2%9E%E5%BC%BA-%E9%AB%98%E9%80%9F%E4%B8%8B%E8%BD%BD) 本项目部分加速功能完全复制该脚本。
* [中国域名白名单](https://github.com/pluwen/china-domain-allowlist),本项目的系统代理排除域名功能中,使用了该白名单。
- [github增强油猴脚本](https://greasyfork.org/zh-CN/scripts/412245-github-%E5%A2%9E%E5%BC%BA-%E9%AB%98%E9%80%9F%E4%B8%8B%E8%BD%BD) 本项目部分加速功能完全复制该脚本。
- [中国域名白名单](https://github.com/pluwen/china-domain-allowlist),本项目的系统代理排除域名功能中,使用了该白名单。
本项目部分加速资源由如下组织提供
* [FastGit UK](https://fastgit.org/)
- [FastGit UK](https://fastgit.org/)

7
_script/1、installEnv.bat

@ -1,7 +0,0 @@
cd ../
node -v
npm install -g cnpm --registry=https://registry.npmmirror.com
cnpm install -g lerna@6

5
_script/1、setupEnv.bat

@ -0,0 +1,5 @@
cd ../
node -v
npm install -g pnpm --registry=https://registry.npmmirror.com

2
_script/2、installBootstrap.bat → _script/2、installProject.bat

@ -2,4 +2,4 @@ cd ../
node -v
lerna bootstrap
pnpm install

10
doc/caroot.md

@ -3,7 +3,9 @@
## 一、为什么要信任根证书。
要回答这个问题需要先掌握下面两个知识点
### 知识点1:什么是根证书
[百度百科-什么是根证书](https://baike.baidu.com/item/%E6%A0%B9%E8%AF%81%E4%B9%A6/9874620?fr=aladdin)
当访问目标网站是https协议时,服务器会发送一个由根证书签发的网站ssl证书给浏览器,让浏览器用这个ssl证书给数据加密。
@ -14,11 +16,11 @@
windows、mac、linux或者浏览器他们都内置了市面上可信的大型证书颁发机构的根证书。
### 知识点2:中间人攻击
本应用的实现原理如下图:
![](./flow.jpg)
> 简单来说就是DevSidecar在本地启动了一个代理服务器帮你访问目标网站。
> 实际上就是 [中间人攻击](https://baike.baidu.com/item/%E4%B8%AD%E9%97%B4%E4%BA%BA%E6%94%BB%E5%87%BB/1739730?fr=aladdin) 的原理,只是本应用没有用它来干坏事,而是帮助开发者加速目标网站的访问。
@ -30,7 +32,7 @@ windows、mac、linux或者浏览器他们都内置了市面上可信的大型
例如加速github就需要修改如下几处
1. 直连访问github需要修改tls握手时的sni域名,规避***的sni阻断问题。
1. 直连访问github需要修改tls握手时的sni域名,规避\*\*\*的sni阻断问题。
2. asserts.github.com等静态资源拦截替换成fastgit.org的镜像地址
DevSidecar在第一次启动时会在本地随机生成一份根证书,当有用户访问github时,就用这份根证书来签发一份假的叫github.com的证书。
@ -51,9 +53,7 @@ DevSidecar在第一次启动时会在本地随机生成一份根证书,当有
> 或者从源码自行编译安装
> 对于拦截配置里的替代网站风险:
>
> 1. 尽量缩小替代配置的范围
> 2. 不使用来源不明的镜像地址,尽量使用知名度较高的镜像地址
> 3. 你甚至可以将其他拦截配置全部删除,只保留github相关配置
>

37
doc/linux.md

@ -1,4 +1,5 @@
# linux 支持
`linux`使用说明,目前仅支持`ubuntu_x64(GNOME)`,其他`linux`未测试
> 注意:需要开启[sudo免密支持](https://www.jianshu.com/p/5d02428f313d)
@ -6,32 +7,38 @@
## 安装
### 1. ubuntu
* 下载`DevSidecar-x.x.x.deb`
* 执行命令安装 `dpkg -i DevSidecar-x.x.x.deb`
* 去应用列表里面找到dev-sidecar应用,打开即可
- 下载`DevSidecar-x.x.x.deb`
- 执行命令安装 `dpkg -i DevSidecar-x.x.x.deb`
- 去应用列表里面找到dev-sidecar应用,打开即可
### 2. 其他linux系统(未测试)
* 下载 `DevSidecar-x.x.x.AppImage`
* 设置可执行权限 `sudo chmod +X DevSidecar-x.x.x.AppImage`
* 双击运行
- 下载 `DevSidecar-x.x.x.AppImage`
- 设置可执行权限 `sudo chmod +X DevSidecar-x.x.x.AppImage`
- 双击运行
## 证书安装
默认模式和增强模式需要系统信任CA证书。
由于linux上火狐和chrome都不走系统证书,所以除了安装系统证书之外,还需要给浏览器安装证书
### 1. 系统证书安装
根据弹出的提示:
* 点击首页右上角“安装根证书”按钮
* 点击“点此去安装”
* 提示安装成功即可
- 点击首页右上角“安装根证书”按钮
- 点击“点此去安装”
- 提示安装成功即可
### 2. 火狐浏览器安装证书
* 火狐浏览器->选项->隐私与安全->证书->查看证书
* 证书颁发机构->导入
* 选择证书文件在`~/.dev-sidecar`目录下
* 勾选信任由此证书颁发机构来标识网站,确定即可
- 火狐浏览器->选项->隐私与安全->证书->查看证书
- 证书颁发机构->导入
- 选择证书文件在`~/.dev-sidecar`目录下
- 勾选信任由此证书颁发机构来标识网站,确定即可
### 3. chrome浏览器安装证书
证书文件目录为`~/.dev-sidecar`
![](../packages/gui/public/setup-linux.png)

14
doc/other.md

@ -1,13 +1,15 @@
# 其他程序使用
## Java程序使用
> 由[Enaium](https://github.com/Enaium) 提供,未做验证,可供参考
> 由 [Enaium](https://github.com/Enaium) 提供,未做验证,可供参考
>
需要先通过keytool安装证书
`keytool -import -alias dev-sidecar -keystore "jdk路径\security\cacerts" -file 用户目录\.dev-sidecar\dev-sidecar.ca.crt`默认密码为`changeit`
启动时还需要设置参数
`-Dhttp.proxyHost=localhost -Dhttp.proxyPort=31181 -Dhttps.proxyHost=localhost -Dhttps.proxyPort=31181`
Gradle还需在`用户目录/.gradle/gradle.properties`创建配置文件
> 需要先通过keytool安装证书
> `keytool -import -alias dev-sidecar -keystore "jdk路径\security\cacerts" -file 用户目录\.dev-sidecar\dev-sidecar.ca.crt`默认密码为`changeit`
> 启动时还需要设置参数
> `-Dhttp.proxyHost=localhost -Dhttp.proxyPort=31181 -Dhttps.proxyHost=localhost -Dhttps.proxyPort=31181`
> Gradle还需在`用户目录/.gradle/gradle.properties`创建配置文件
```properties
systemProp.http.proxyHost=localhost
systemProp.http.proxyPort=31181

11
doc/recover.md

@ -1,21 +1,26 @@
# 卸载与恢复网络
由于应用启动后会自动设置系统代理,正常退出时会关闭系统代理。
当应用意外关闭,或者未正常退出后被卸载,此时会因为系统代理没有恢复从而导致完全上不了网。
目前electron在windows系统上无法监听系统重启事件。更多相关资料 [electron issues](https://github.com/electron/electron/pull/24261)
## 恢复代理设置
### 1、windows 代理关闭
如何打开查看windows代理设置:
* win10: 开始->设置->网络和Internet->最下方代理
* win7: 开始->控制面板->网络和Internet->网络和共享中心->左下角Internet选项->连接选项卡->局域网设置
- win10: 开始->设置->网络和Internet->最下方代理
- win7: 开始->控制面板->网络和Internet->网络和共享中心->左下角Internet选项->连接选项卡->局域网设置
![windows](./proxy.png)
### 2、mac 代理关闭
网络->网卡->代理->去掉http和https的两个勾
![](./mac-proxy.png)
### 3、Linux(Ubuntu)
网络->代理->选择禁用

28
eslint.config.js

@ -0,0 +1,28 @@
import antfu from '@antfu/eslint-config'
export default antfu(
{
vue: {
vueVersion: 2,
},
rules: {
'style/brace-style': ['error', '1tbs'],
'style/space-before-function-paren': ['error', 'always'],
'import/newline-after-import': 'off',
'import/first': 'off',
'perfectionist/sort-imports': 'off',
'node/prefer-global/buffer': 'off',
'node/prefer-global/process': 'off',
'no-console': 'off',
},
ignores: [
'**/build/*',
'**/dist_electron',
],
formatters: {
css: true,
html: true,
markdown: 'prettier',
},
},
)

18
lerna.json

@ -1,18 +0,0 @@
{
"packages": [
"packages/*"
],
"command": {
"publish": {
"ignoreChanges": [
"*.md",
"config",
"doc"
]
},
"bootstrap": {
"ignore": []
}
},
"version": "1.8.9"
}

14
package.json

@ -1,11 +1,17 @@
{
"name": "dev-sidecar-parent",
"type": "module",
"private": false,
"license": "MPL-2.0",
"packageManager": "pnpm@9.13.2",
"author": "Greper",
"devDependencies": {
"lerna": "^6.6.2"
"license": "MPL-2.0",
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"dependencies": {
"devDependencies": {
"@antfu/eslint-config": "^3.9.1",
"eslint": "^9.15.0",
"eslint-plugin-format": "^0.1.2"
}
}

373
packages/cli/LICENSE

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

3
packages/cli/cli.js

@ -0,0 +1,3 @@
#!/usr/bin/env node
require('./src')

23
packages/cli/package.json

@ -0,0 +1,23 @@
{
"name": "@docmirror/dev-sidecar-cli",
"version": "1.8.9",
"private": false,
"description": "给开发者的加速代理工具",
"author": "docmirror.cn",
"license": "MPL-2.0",
"keywords": [
"dev-sidecar",
"github加速",
"google加速",
"代理"
],
"main": "src/index.js",
"bin": "./cli.js",
"scripts": {
"start": "node ./src"
},
"dependencies": {
"@docmirror/dev-sidecar": "workspace:*",
"@docmirror/mitmproxy": "workspace:*"
}
}

0
packages/core/start/banner.txt → packages/cli/src/banner.txt

13
packages/core/start/index.js → packages/cli/src/index.js

@ -1,19 +1,18 @@
const fs = require('node:fs')
const DevSidecar = require('@docmirror/dev-sidecar')
const jsonApi = require('@docmirror/mitmproxy/src/json')
const DevSidecar = require('../index')
const fs = require('fs')
const log = require('../src/utils/util.log')
// 启动服务
const mitmproxyPath = './start/mitmproxy'
const mitmproxyPath = './mitmproxy'
async function startup () {
const banner = fs.readFileSync('./start/banner.txt')
const banner = fs.readFileSync('./banner.txt')
console.log(banner.toString())
const configPath = './start/user_config.json5'
const configPath = './user_config.json5'
if (fs.existsSync(configPath)) {
const file = fs.readFileSync(configPath)
const userConfig = jsonApi.parse(file.toString())
log.info('读取 user_config.json5 成功:', configPath)
console.info('读取 user_config.json5 成功:', configPath)
DevSidecar.api.config.set(userConfig)
}

7
packages/core/start/mitmproxy.js → packages/cli/src/mitmproxy.js

@ -1,8 +1,10 @@
const fs = require('node:fs')
const path = require('node:path')
const server = require('@docmirror/mitmproxy')
const jsonApi = require('@docmirror/mitmproxy/src/json')
const path = require('path')
const log = require('@docmirror/mitmproxy/src/utils/util.log')
const home = process.env.USER_HOME || process.env.HOME || 'C:/Users/Administrator/'
const log = require('../src/utils/util.log')
let configPath
if (process.argv && process.argv.length > 3) {
@ -11,7 +13,6 @@ if (process.argv && process.argv.length > 3) {
configPath = path.join(home, '.dev-sidecar/running.json')
}
const fs = require('fs')
const configJson = fs.readFileSync(configPath)
log.info('读取 running.json by core 成功:', configPath)
const config = jsonApi.parse(configJson.toString())

42
packages/cli/src/user_config.json5

@ -0,0 +1,42 @@
{
"app": {
"autoStart": {
"enabled": true
},
"mode": "default"
},
"plugin": {
"node": {
"setting": {
"yarnRegistry": "null"
}
},
"git": {
"enabled": true
},
"overwall": {
"enabled": false,
"targets": {
"*gagedigital.com": true,
"*yonsz.net": true,
"*bootstrapcdn.com": true,
"*cloudflare.com": true,
"help.yonsz.net": true
}
}
},
"server": {
"intercepts": {
"dev-sidecar.docmirror.cn": {
".*": {
"proxy": "dev-sidecar-preview.docmirror.cn"
}
},
"test1111.gagedigital.com": {
".*": {
"proxy": "test1.gagedigital.com"
}
}
}
}
}

3
packages/core/.babelrc

@ -1,3 +0,0 @@
{
"presets": ["es2020","stage-4"]
}

1
packages/core/.eslintignore

@ -1 +0,0 @@
./src/test

2
packages/core/index.js

@ -3,5 +3,5 @@ module.exports = require('./src')
// TODO 这是一个解谜游戏 ↓ ↓ ↓ ↓ ↓ ↓ ,如果你破解了它,请不要公开,好好用它来学习和查资料吧(特别注意:为了你的人身安全,请不要用它来查看和发表不当言论,你懂得)。
/**
\u0061\u0048\u0052\u0030\u0063\u0044\u006f\u0076\u004c\u0032\u0052\u006c\u0064\u0069\u0031\u007a\u0061\u0057\u0052\u006c\u0059\u0032\u0046\u0079\u004c\u006d\u0052\u0076\u0059\u0032\u0031\u0070\u0063\u006e\u004a\u0076\u0063\u0069\u0035\u006a\u0062\u0069\u0039\u0035\u0062\u0033\u0056\u006d\u0061\u0057\u0035\u006b\u0061\u0058\u0051\u0076\u0061\u0057\u0035\u006b\u005a\u0058\u0067\u0075\u0061\u0048\u0052\u0074\u0062\u0041\u003d\u003d
**/
*/
// 这个项目里有一点点解谜提示: https://github.com/fast-crud/fast-crud (打开拉到最下面)

70
packages/core/package.json

@ -1,83 +1,31 @@
{
"name": "@docmirror/dev-sidecar",
"version": "1.8.9",
"private": false,
"description": "给开发者的加速代理工具",
"main": "src/index.js",
"author": "docmirror.cn",
"license": "MPL-2.0",
"keywords": [
"dev-sidecar",
"github加速",
"google加速",
"代理"
],
"author": "docmirror.cn",
"license": "MPL-2.0",
"private": false,
"main": "src/index.js",
"scripts": {
"start": "node ./start",
"test": "mocha"
},
"dependencies": {
"@docmirror/mitmproxy": "^1.8.9",
"agentkeepalive": "^2.1.1",
"babel-preset-es2020": "^1.0.2",
"charset": "^1.0.0",
"child_process": "^1.0.2",
"colors": "^1.1.2",
"commander": "^2.9.0",
"debug": "^4.1.1",
"dns-over-http": "^0.2.0",
"dns-over-tls": "^0.0.8",
"fix-path": "^3.0.0",
"iconv-lite": "^0.4.13",
"is-browser": "^2.1.0",
"jschardet": "^1.4.1",
"json5": "^2.1.3",
"lodash": "^4.7.0",
"log4js": "^6.3.0",
"lru-cache": "^6.0.0",
"mkdirp": "^0.5.1",
"node-cmd": "^3.0.0",
"node-forge": "^0.8.2",
"iconv-lite": "^0.6.3",
"lodash": "^4.17.20",
"log4js": "^6.9.1",
"node-powershell": "^4.0.0",
"require-context": "^1.1.0",
"spawn-sync": "^2.0.0",
"through2": "^2.0.1",
"tunnel-agent": "^0.4.3",
"util": "^0.12.3",
"validator": "^13.1.17",
"winreg": "^1.2.4"
"winreg": "^1.2.5"
},
"devDependencies": {
"@vue/cli-plugin-eslint": "^4.5.0",
"@vue/eslint-config-standard": "^5.1.2",
"babel-eslint": "^10.1.0",
"chai": "^4.3.4",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^6.2.2",
"mocha": "^8.2.1"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"@vue/standard"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
],
"gitHead": "30ecf21cb001fb8d821e575d476ca0ffb4ff086e"
}
}

32
packages/core/src/config.js

@ -1,12 +1,12 @@
const fs = require('fs')
const Shell = require('./shell')
const lodash = require('lodash')
const defConfig = require('./config/index.js')
const fs = require('node:fs')
const path = require('node:path')
const jsonApi = require('@docmirror/mitmproxy/src/json')
const lodash = require('lodash')
const request = require('request')
const path = require('path')
const log = require('./utils/util.log')
const defConfig = require('./config/index.js')
const mergeApi = require('./merge.js')
const Shell = require('./shell')
const log = require('./utils/util.log')
let configTarget = lodash.cloneDeep(defConfig)
@ -80,13 +80,13 @@ const configApi = {
configApi.deleteRemoteConfigFile(suffix)
return
}
// eslint-disable-next-line handle-callback-err
return new Promise((resolve, reject) => {
log.info('开始下载远程配置:', remoteConfigUrl)
const headers = {
'Cache-Control': 'no-cache', // 禁止使用缓存
Pragma: 'no-cache' // 禁止使用缓存
'Pragma': 'no-cache', // 禁止使用缓存
}
if (remoteConfigUrl.startsWith('https://raw.githubusercontent.com/')) {
headers['Server-Name'] = 'baidu.com'
@ -131,7 +131,7 @@ const configApi = {
if (response) {
message = `下载远程配置失败: ${remoteConfigUrl}, message: ${response.message}, code: ${response.statusCode}`
} else {
message = '下载远程配置失败: response: ' + response
message = `下载远程配置失败: response: ${response}`
}
reject(new Error(message))
}
@ -205,7 +205,7 @@ const configApi = {
return {
diffConfig,
allConfig
allConfig,
}
},
doMerge: mergeApi.doMerge,
@ -330,31 +330,31 @@ const configApi = {
list.push({
key,
value: map[key],
exists
exists,
})
}
return list
},
async setVariables (type) {
const list = await configApi.getVariables(type)
const noSetList = list.filter(item => {
const noSetList = list.filter((item) => {
return !item.exists
})
if (list.length > 0) {
const context = {
root_ca_cert_path: configApi.get().server.setting.rootCaFile.certPath
root_ca_cert_path: configApi.get().server.setting.rootCaFile.certPath,
}
for (const item of noSetList) {
if (item.value.indexOf('${') >= 0) {
if (item.value.includes('${')) {
for (const key in context) {
item.value = item.value.replcace(new RegExp('${' + key + '}', 'g'), context[key])
item.value = item.value.replcace(new RegExp(`\${${key}}`, 'g'), context[key])
}
}
}
const method = type === 'npm' ? Shell.setNpmEnv : Shell.setSystemEnv
return method({ list: noSetList })
}
}
},
}
module.exports = configApi

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

@ -1,4 +1,4 @@
const path = require('path')
const path = require('node:path')
function getUserBasePath () {
const userHome = process.env.USERPROFILE || process.env.HOME || '/'
@ -17,14 +17,14 @@ module.exports = {
app: {
mode: 'default',
autoStart: {
enabled: false
enabled: false,
},
remoteConfig: {
enabled: true,
// 共享远程配置地址
url: 'https://gitee.com/wangliang181230/dev-sidecar/raw/docmirror/packages/core/src/config/remote_config.json5',
// 个人远程配置地址
personalUrl: ''
personalUrl: '',
},
startShowWindow: true, // 启动时是否打开窗口:true=打开窗口, false=隐藏窗口
showHideShortcut: 'Alt + S', // 显示/隐藏窗口快捷键
@ -33,10 +33,10 @@ module.exports = {
autoChecked: true, // 是否自动检查更新
skipPreRelease: true, // 是否忽略预发布版本
dock: {
hideWhenWinClose: false
hideWhenWinClose: false,
},
closeStrategy: 0,
showShutdownTip: true
showShutdownTip: true,
},
server: {
enabled: true,
@ -47,12 +47,12 @@ module.exports = {
verifySsl: true,
script: {
enabled: true,
defaultDir: './extra/scripts/'
defaultDir: './extra/scripts/',
},
userBasePath: getUserBasePath(),
rootCaFile: {
certPath: getRootCaCertPath(),
keyPath: getRootCaKeyPath()
keyPath: getRootCaKeyPath(),
},
// 默认超时时间配置
@ -63,12 +63,12 @@ module.exports = {
timeoutMapping: {
'github.com': {
timeout: 20000,
keepAliveTimeout: 30000
}
keepAliveTimeout: 30000,
},
},
// 慢速IP延迟时间:测速超过该值时,则视为延迟高,显示为橙色
lowSpeedDelay: 150
lowSpeedDelay: 150,
},
compatible: {
// **** 自定义兼容配置 **** //
@ -87,15 +87,15 @@ module.exports = {
// rejectUnauthorized: false
// }
// }
}
},
},
intercept: {
enabled: true
enabled: true,
},
intercepts: {
'github.com': {
'.*': {
sni: 'baidu.com'
sni: 'baidu.com',
},
'^(/[\\w-.]+){2,}/?(\\?.*)?$': {
// 篡改猴插件地址,以下是高速镜像地址
@ -103,7 +103,7 @@ module.exports = {
// Github油猴脚本地址,以下是高速镜像地址
script: 'https://gitee.com/wangliang181230/dev-sidecar/raw/scripts/GithubEnhanced-High-Speed-Download.user.js',
remark: '注:上面所使用的脚本地址,为高速镜像地址。',
desc: '油猴脚本:高速下载 Git Clone/SSH、Release、Raw、Code(ZIP) 等文件 (公益加速)、项目列表单文件快捷下载、添加 git clone 命令'
desc: '油猴脚本:高速下载 Git Clone/SSH、Release、Raw、Code(ZIP) 等文件 (公益加速)、项目列表单文件快捷下载、添加 git clone 命令',
},
// 以下三项暂时先注释掉,因为已经有油猴脚本提供高速下载地址了。
// '/.*/.*/releases/download/': {
@ -119,140 +119,140 @@ module.exports = {
// },
'/fluidicon.png': {
cacheDays: 365,
desc: 'Github那只猫的图片,缓存1年'
desc: 'Github那只猫的图片,缓存1年',
},
'^(/[^/]+){2}/pull/\\d+/open_with_menu.*$': {
cacheDays: 7,
desc: 'PR详情页:标题右边那个Code按钮的HTML代码请求地址,感觉上应该可以缓存。暂时先设置为缓存7天'
desc: 'PR详情页:标题右边那个Code按钮的HTML代码请求地址,感觉上应该可以缓存。暂时先设置为缓存7天',
},
'^((/[^/]+){2,})/raw((/[^/]+)+\\.(jpg|jpeg|png|gif))(\\?.*)?$': {
// eslint-disable-next-line no-template-curly-in-string
proxy: 'https://raw.githubusercontent.com${m[1]}${m[3]}',
sni: 'baidu.com',
cacheDays: 7,
desc: '仓库内图片,重定向改为代理,并缓存7天。'
desc: '仓库内图片,重定向改为代理,并缓存7天。',
},
'^((/[^/]+){2,})/raw((/[^/]+)+\\.js)(\\?.*)?$': {
// eslint-disable-next-line no-template-curly-in-string
proxy: 'https://raw.githubusercontent.com${m[1]}${m[3]}',
sni: 'baidu.com',
responseReplace: { headers: { 'content-type': 'application/javascript; charset=utf-8' } },
desc: '仓库内脚本,重定向改为代理,并设置响应头Content-Type。作用:方便script拦截器直接使用,避免引起跨域问题和脚本内容限制问题。'
}
desc: '仓库内脚本,重定向改为代理,并设置响应头Content-Type。作用:方便script拦截器直接使用,避免引起跨域问题和脚本内容限制问题。',
},
},
'github-releases.githubusercontent.com': {
'.*': {
sni: 'baidu.com'
}
sni: 'baidu.com',
},
},
'github.githubassets.com': {
'.*': {
sni: 'baidu.com'
}
sni: 'baidu.com',
},
},
'camo.githubusercontent.com': {
'^[a-zA-Z0-9/]+(\\?.*)?$': {
cacheDays: 365,
desc: '图片,缓存1年'
desc: '图片,缓存1年',
},
'.*': {
sni: 'baidu.com'
}
sni: 'baidu.com',
},
},
'collector.github.com': {
'.*': {
sni: 'baidu.com'
}
sni: 'baidu.com',
},
},
'customer-stories-feed.github.com': {
'.*': { proxy: 'customer-stories-feed.fastgit.org' }
'.*': { proxy: 'customer-stories-feed.fastgit.org' },
},
'raw.githubusercontent.com': {
'.*': {
sni: 'baidu.com'
}
sni: 'baidu.com',
},
},
'user-images.githubusercontent.com': {
'.*': {
sni: 'baidu.com'
sni: 'baidu.com',
},
'^/.*\\.png(\\?.*)?$': {
cacheDays: 365,
desc: '用户在PR或issue等内容中上传的图片,缓存1年。注:每张图片都有唯一的ID,不会重复,可以安心缓存'
}
desc: '用户在PR或issue等内容中上传的图片,缓存1年。注:每张图片都有唯一的ID,不会重复,可以安心缓存',
},
},
'private-user-images.githubusercontent.com': {
'.*': {
sni: 'baidu.com'
sni: 'baidu.com',
},
'^/.*\\.png(\\?.*)?$': {
cacheHours: 1,
desc: '用户在PR或issue等内容中上传的图片,缓存1小时就够了,因为每次刷新页面都是不一样的链接。'
}
desc: '用户在PR或issue等内容中上传的图片,缓存1小时就够了,因为每次刷新页面都是不一样的链接。',
},
},
'avatars.githubusercontent.com': {
'.*': {
sni: 'baidu.com'
sni: 'baidu.com',
},
'^/u/\\d+(\\?.*)?$': {
cacheDays: 365,
desc: '用户头像,缓存1年'
}
desc: '用户头像,缓存1年',
},
},
'api.github.com': {
'^/_private/browser/stats$': {
success: true,
desc: 'github的访问速度分析上传,没有必要,直接返回成功'
desc: 'github的访问速度分析上传,没有必要,直接返回成功',
},
'.*': {
sni: 'baidu.com'
}
sni: 'baidu.com',
},
},
'*.docker.com': {
'.*': {
sni: 'baidu.com'
}
sni: 'baidu.com',
},
},
'login.docker.com': {
'/favicon.ico': {
proxy: 'hub.docker.com',
sni: 'baidu.com',
desc: '登录页面的ico,采用hub.docker.com的'
}
desc: '登录页面的ico,采用hub.docker.com的',
},
},
// google cdn
'www.google.com': {
'/recaptcha/.*': { proxy: 'www.recaptcha.net' }
'/recaptcha/.*': { proxy: 'www.recaptcha.net' },
// '.*': {
// proxy: 'gg.docmirror.top/_yxorp',
// desc: '呀,被你发现了,偷偷的用,别声张'
// }
},
'www.gstatic.com': {
'/recaptcha/.*': { proxy: 'www.recaptcha.net' }
'/recaptcha/.*': { proxy: 'www.recaptcha.net' },
},
'ajax.googleapis.com': {
'.*': {
proxy: 'ajax.lug.ustc.edu.cn',
backup: ['gapis.geekzu.org'],
test: 'ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js'
}
test: 'ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js',
},
},
'fonts.googleapis.com': {
'.*': {
proxy: 'fonts.geekzu.org',
backup: ['fonts.loli.net'],
test: 'https://fonts.googleapis.com/css?family=Oswald'
}
test: 'https://fonts.googleapis.com/css?family=Oswald',
},
},
'themes.googleapis.com': {
'.*': {
proxy: 'themes.loli.net',
backup: ['themes.proxy.ustclug.org']
}
backup: ['themes.proxy.ustclug.org'],
},
},
'themes.googleusercontent.com': {
'.*': { proxy: 'google-themes.proxy.ustclug.org' }
'.*': { proxy: 'google-themes.proxy.ustclug.org' },
},
// 'fonts.gstatic.com': {
// '.*': {
@ -266,8 +266,8 @@ module.exports = {
// mapbox-node-binary.s3.amazonaws.com/sqlite3/v5.0.0/napi-v3-win32-x64.tar.gz
'*.s3.1amazonaws1.com': {
'/sqlite3/.*': {
redirect: 'npm.taobao.org/mirrors'
}
redirect: 'npm.taobao.org/mirrors',
},
},
// 'packages.elastic.co': { '.*': { proxy: 'elastic.proxy.ustclug.org' } },
// 'ppa.launchpad.net': { '.*': { proxy: 'launchpad.proxy.ustclug.org' } },
@ -278,15 +278,15 @@ module.exports = {
'*.carbonads.com': {
'/carbon.*': {
abort: true,
desc: '广告拦截'
}
desc: '广告拦截',
},
},
'*.buysellads.com': {
'/ads/.*': {
abort: true,
desc: '广告拦截'
}
}
desc: '广告拦截',
},
},
},
// 预设置IP列表
preSetIpList: {
@ -303,7 +303,7 @@ module.exports = {
'140.82.116.3',
'140.82.116.4',
'140.82.121.3',
'140.82.121.4'
'140.82.121.4',
],
'api.github.com': [
'20.26.156.210',
@ -316,7 +316,7 @@ module.exports = {
'140.82.112.5',
'140.82.113.6',
'140.82.116.6',
'140.82.121.6'
'140.82.121.6',
],
'codeload.github.com': [
'20.26.156.216',
@ -329,26 +329,26 @@ module.exports = {
'140.82.113.9',
'140.82.114.10',
'140.82.116.10',
'140.82.121.9'
'140.82.121.9',
],
'*.githubusercontent.com': [
'185.199.108.133',
'185.199.109.133',
'185.199.110.133',
'185.199.111.133'
'185.199.111.133',
],
'github.githubassets.com': [
'185.199.108.154',
'185.199.109.154',
'185.199.110.154',
'185.199.111.154'
'185.199.111.154',
],
'github.io': [
'185.199.108.153',
'185.199.109.153',
'185.199.110.153',
'185.199.111.153'
]
'185.199.111.153',
],
},
whiteList: {
'*.cn': true,
@ -360,35 +360,35 @@ module.exports = {
'*.alipay.com': true,
'*.qq.com': true,
'*.baidu.com': true,
'192.168.*': true
'192.168.*': true,
},
dns: {
providers: {
aliyun: {
type: 'https',
server: 'https://dns.alidns.com/dns-query',
cacheSize: 1000
cacheSize: 1000,
},
cloudflare: {
type: 'https',
server: 'https://1.1.1.1/dns-query',
cacheSize: 1000
cacheSize: 1000,
},
quad9: {
type: 'https',
server: 'https://9.9.9.9/dns-query',
cacheSize: 1000
cacheSize: 1000,
},
safe360: {
type: 'https',
server: 'https://doh.360.cn/dns-query',
cacheSize: 1000
cacheSize: 1000,
},
rubyfish: {
type: 'https',
server: 'https://rubyfish.cn/dns-query',
cacheSize: 1000
}
cacheSize: 1000,
},
},
mapping: {
'*.github.com': 'quad9',
@ -407,16 +407,16 @@ module.exports = {
'*.v2ex.com': 'quad9',
'*.pypi.org': 'quad9',
'*.jetbrains.com': 'quad9',
'*.azureedge.net': 'quad9'
'*.azureedge.net': 'quad9',
},
speedTest: {
enabled: true,
interval: 300000,
hostnameList: ['github.com'],
dnsProviders: ['cloudflare', 'safe360', 'rubyfish']
}
}
dnsProviders: ['cloudflare', 'safe360', 'rubyfish'],
},
},
},
proxy: {},
plugin: {}
plugin: {},
}

6
packages/core/src/event.js

@ -6,7 +6,9 @@ function register (channel, handle, order = 10) {
handles = listener[channel] = []
}
handles.push({ id: index, handle, order })
handles.sort((a, b) => { return a.order - b.order })
handles.sort((a, b) => {
return a.order - b.order
})
return index++
}
function fire (channel, event) {
@ -34,6 +36,6 @@ function unregister (id) {
const EventHub = {
register,
fire,
unregister
unregister,
}
module.exports = EventHub

19
packages/core/src/expose.js

@ -1,16 +1,17 @@
const status = require('./status')
const lodash = require('lodash')
const config = require('./config')
const event = require('./event')
const shell = require('./shell')
const modules = require('./modules')
const lodash = require('lodash')
const shell = require('./shell')
const status = require('./status')
const log = require('./utils/util.log')
const context = {
config,
shell,
status,
event,
log
log,
}
function setupPlugin (key, plugin, context, config) {
@ -29,14 +30,14 @@ const proxy = setupPlugin('proxy', modules.proxy, context, config)
const plugin = {}
for (const key in modules.plugin) {
const target = modules.plugin[key]
const api = setupPlugin('plugin.' + key, target, context, config)
const api = setupPlugin(`plugin.${key}`, target, context, config)
plugin[key] = api
}
config.resetDefault()
const server = modules.server
const serverStart = server.start
const newServerStart = ({ mitmproxyPath }) => {
function newServerStart ({ mitmproxyPath }) {
return serverStart({ mitmproxyPath, plugins: plugin })
}
server.start = newServerStart
@ -126,7 +127,7 @@ const api = {
status: {
get () {
return status
}
},
},
config,
event,
@ -134,9 +135,9 @@ const api = {
server,
proxy,
plugin,
log
log,
}
module.exports = {
status,
api
api,
}

2
packages/core/src/index.js

@ -3,7 +3,7 @@ const log = require('./utils/util.log')
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
// 避免异常崩溃
process.on('uncaughtException', function (err) {
process.on('uncaughtException', (err) => {
if (err.code === 'ECONNABORTED') {
// console.error(err.errno)
return

6
packages/core/src/merge.js

@ -75,13 +75,13 @@ function deleteNullItems (target) {
}
module.exports = {
doMerge: function (oldObj, newObj) {
return lodash.mergeWith(oldObj, newObj, function (objValue, srcValue) {
doMerge (oldObj, newObj) {
return lodash.mergeWith(oldObj, newObj, (objValue, srcValue) => {
if (lodash.isArray(objValue)) {
return srcValue
}
})
},
doDiff,
deleteNullItems
deleteNullItems,
}

12
packages/core/src/modules/index.js

@ -1,11 +1,5 @@
const server = require('./server')
const proxy = require('./proxy')
const plugin = require('./plugin')
module.exports = {
server,
proxy,
plugin
server: require('./server'),
proxy: require('./proxy'),
plugin: require('./plugin'),
}

6
packages/core/src/modules/plugin/git/config.js

@ -7,7 +7,7 @@ module.exports = {
noProxyUrls: {
'https://gitee.com': true, // 码云
'https://e.coding.net': true, // Coding(腾讯云)
'https://codeup.aliyun.com': true // 云效 Codeup (阿里云)
}
}
'https://codeup.aliyun.com': true, // 云效 Codeup (阿里云)
},
},
}

9
packages/core/src/modules/plugin/git/index.js

@ -1,4 +1,5 @@
const pluginConfig = require('./config')
const Plugin = function (context) {
const { config, shell, event, log } = context
const pluginApi = {
@ -29,7 +30,7 @@ const Plugin = function (context) {
async setProxy (ip, port) {
const cmds = [
`git config --global http.proxy http://${ip}:${port} `,
`git config --global https.proxy http://${ip}:${port} `
`git config --global https.proxy http://${ip}:${port} `,
]
if (config.get().plugin.git.setting.sslVerify === true) {
@ -76,7 +77,7 @@ const Plugin = function (context) {
event.fire('status', { key: 'plugin.git.enabled', value: false })
log.info('关闭【Git】代理成功')
return ret
}
},
}
return pluginApi
}
@ -85,7 +86,7 @@ module.exports = {
key: 'git',
config: pluginConfig,
status: {
enabled: false
enabled: false,
},
plugin: Plugin
plugin: Plugin,
}

10
packages/core/src/modules/plugin/index.js

@ -1,8 +1,6 @@
const node = require('./node')
const git = require('./git')
const overwall = require('./overwall')
const pip = require('./pip')
module.exports = {
node, git, pip, overwall
node: require('./node'),
git: require('./git'),
pip: require('./pip'),
overwall: require('./overwall'),
}

18
packages/core/src/modules/plugin/node/config.js

@ -3,16 +3,16 @@ module.exports = {
enabled: false,
tip: '如果你没有安装nodejs则不需要启动它',
startup: {
variables: true
variables: true,
},
setting: {
command: 'npm',
'command': 'npm',
'strict-ssl': true,
cafile: false,
NODE_EXTRA_CA_CERTS: false,
NODE_TLS_REJECT_UNAUTHORIZED: false,
yarnRegistry: 'null',
registry: 'https://registry.npmjs.org'// 可以选择切换官方或者淘宝镜像
'cafile': false,
'NODE_EXTRA_CA_CERTS': false,
'NODE_TLS_REJECT_UNAUTHORIZED': false,
'yarnRegistry': 'default',
'registry': 'https://registry.npmjs.org', // 可以选择切换官方或者淘宝镜像
},
// intercepts: {
// 'cdn.cypress.io': [{ regexp: '/desktop/.*', proxy: 'http://npmmirror.com/mirrors/cypress/' }]
@ -27,6 +27,6 @@ module.exports = {
CHROMEDRIVER_CDNURL: 'https://npmmirror.com/mirrors/chromedriver',
OPERADRIVER: 'https://npmmirror.com/mirrors/operadriver',
ELECTRON_BUILDER_BINARIES_MIRROR: 'https://npmmirror.com/mirrors/electron-builder-binaries/',
PYTHON_MIRROR: 'https://npmmirror.com/mirrors/python'
}
PYTHON_MIRROR: 'https://npmmirror.com/mirrors/python',
},
}

21
packages/core/src/modules/plugin/node/index.js

@ -1,5 +1,6 @@
const nodeConfig = require('./config')
const jsonApi = require('@docmirror/mitmproxy/src/json')
const nodeConfig = require('./config')
const NodePlugin = function (context) {
const { config, shell, event, log } = context
const nodeApi = {
@ -44,7 +45,7 @@ const NodePlugin = function (context) {
const cmds = []
for (const item of list) {
if (item.value != null && item.value.length > 0 && item.value !== 'null') {
if (item.value != null && item.value.length > 0 && item.value !== 'default' && item.value !== 'null') {
cmds.push(`${command} config set ${item.key} ${item.value}`)
} else {
cmds.push(`${command} config delete ${item.key}`)
@ -67,7 +68,7 @@ const NodePlugin = function (context) {
const cmds = []
log.debug('yarn set:', JSON.stringify(list))
for (const item of list) {
if (item.value != null && item.value.length > 0 && item.value !== 'null') {
if (item.value != null && item.value.length > 0 && item.value !== 'default' && item.value !== 'null') {
cmds.push(`yarn config set ${item.key} ${item.value}`)
} else {
cmds.push(`yarn config delete ${item.key}`)
@ -95,7 +96,7 @@ const NodePlugin = function (context) {
value: map[key],
oldValue: currentMap[key],
exists,
hadSet: currentMap[key] === map[key]
hadSet: currentMap[key] === map[key],
})
}
return list
@ -103,7 +104,7 @@ const NodePlugin = function (context) {
async setVariables () {
const list = await nodeApi.getVariables()
const noSetList = list.filter(item => {
const noSetList = list.filter((item) => {
return !item.exists
})
if (noSetList.length > 0) {
@ -125,7 +126,7 @@ const NodePlugin = function (context) {
const cmds = [
`${command} config set proxy=http://${ip}:${port}`,
`${command} config set https-proxy=http://${ip}:${port}`
`${command} config set https-proxy=http://${ip}:${port}`,
]
const env = []
@ -172,13 +173,13 @@ const NodePlugin = function (context) {
`${command} config delete proxy`,
`${command} config delete https-proxy`,
`${command} config delete NODE_EXTRA_CA_CERTS`,
`${command} config delete strict-ssl`
`${command} config delete strict-ssl`,
]
const ret = await shell.exec(cmds, { type: 'cmd' })
event.fire('status', { key: 'plugin.node.enabled', value: false })
log.info('关闭【NPM】代理成功')
return ret
}
},
}
return nodeApi
}
@ -187,7 +188,7 @@ module.exports = {
key: 'node',
config: nodeConfig,
status: {
enabled: false
enabled: false,
},
plugin: NodePlugin
plugin: NodePlugin,
}

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

@ -6,8 +6,8 @@ module.exports = {
'ow-prod.docmirror.top': {
port: 443,
path: 'X2dvX292ZXJfd2FsbF8',
password: 'dev_sidecar_is_666'
}
password: 'dev_sidecar_is_666',
},
},
targets: {
'*.github.com': true,
@ -36,13 +36,13 @@ module.exports = {
'*.intlify.dev': true,
'*.segment.io': true,
'*.shields.io': true,
'*.jsdelivr.net': true
'*.jsdelivr.net': true,
},
pac: {
enabled: true,
autoUpdate: true,
pacFileUpdateUrl: 'https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt',
pacFileAbsolutePath: null, // 自定义 pac.txt 文件位置,可以是本地文件路径
pacFilePath: './extra/pac/pac.txt' // 内置 pac.txt 文件路径
}
pacFilePath: './extra/pac/pac.txt', // 内置 pac.txt 文件路径
},
}

12
packages/core/src/modules/plugin/overwall/index.js

@ -36,14 +36,12 @@ const Plugin = function (context) {
for (const key in conf.targets) {
serverConfig.intercepts[key] = {
'.*': {
// eslint-disable-next-line no-template-curly-in-string
proxy: main + '/${host}',
backup
}
}
proxy: `${main}/\${host}`,
backup,
},
}
}
},
}
return api
}
@ -51,5 +49,5 @@ const Plugin = function (context) {
module.exports = {
key: 'overwall',
config: pluginConfig,
plugin: Plugin
plugin: Plugin,
}

4
packages/core/src/modules/plugin/pip/config.js

@ -8,6 +8,6 @@ module.exports = {
setting: {
command: 'pip',
trustedHost: 'pypi.org',
registry: 'https://pypi.org/simple/'// 可以选择切换官方或者淘宝镜像
}
registry: 'https://pypi.org/simple/', // 可以选择切换官方或者淘宝镜像
},
}

9
packages/core/src/modules/plugin/pip/index.js

@ -1,4 +1,5 @@
const pipConfig = require('./config')
const PipPlugin = function (context) {
const { config, shell, event, log } = context
const api = {
@ -20,7 +21,7 @@ const PipPlugin = function (context) {
},
async getPipEnv () {
const command = config.get().plugin.pip.setting.command
let ret = await shell.exec([command + ' config list'], { type: 'cmd' })
let ret = await shell.exec([`${command} config list`], { type: 'cmd' })
if (ret != null) {
ret = ret.trim()
const lines = ret.split('\n')
@ -81,7 +82,7 @@ const PipPlugin = function (context) {
async unsetProxy () {
}
},
}
return api
}
@ -90,7 +91,7 @@ module.exports = {
key: 'pip',
config: pipConfig,
status: {
enabled: false
enabled: false,
},
plugin: PipPlugin
plugin: PipPlugin,
}

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

@ -43,7 +43,7 @@ const ProxyPlugin = function (context) {
await shell.enableLoopback()
log.info('打开EnableLoopback成功')
return true
}
},
}
return api
}
@ -207,7 +207,7 @@ module.exports = {
// endregion
// 本地地址,无需代理
localhost: true,
'localhost': true,
'localhost.*': true, // 部分VPN会在host中添加这种格式的域名指向127.0.0.1,所以也排除掉
'127.*.*.*': true,
'test.*': true, // 本地开发时,测试用的虚拟域名格式,无需代理
@ -232,12 +232,12 @@ module.exports = {
'172.31.*.*': true,
// 局域网地址,无需代理
'192.168.*.*': true
}
'192.168.*.*': true,
},
},
status: {
enabled: false,
proxyTarget: ''
proxyTarget: '',
},
plugin: ProxyPlugin
plugin: ProxyPlugin,
}

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

@ -1,19 +1,19 @@
const fork = require('node:child_process').fork
const fs = require('node:fs')
const path = require('node:path')
const lodash = require('lodash')
const config = require('../../config')
const event = require('../../event')
const status = require('../../status')
const lodash = require('lodash')
const fork = require('child_process').fork
const log = require('../../utils/util.log')
const fs = require('fs')
const path = require('path')
const jsonApi = require('@docmirror/mitmproxy/src/json')
const log = require('../../utils/util.log')
let server = null
function fireStatus (status) {
event.fire('status', { key: 'server.enabled', value: status })
}
function sleep (time) {
return new Promise(resolve => {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, time)
@ -84,7 +84,7 @@ const serverApi = {
process: serverProcess,
close () {
serverProcess.send({ type: 'action', event: { key: 'close' } })
}
},
}
serverProcess.on('beforeExit', (code) => {
log.warn('server process beforeExit, code:', code)
@ -98,7 +98,7 @@ const serverApi = {
serverProcess.on('uncaughtException', (err, origin) => {
log.error('server process uncaughtException:', err)
})
serverProcess.on('message', function (msg) {
serverProcess.on('message', (msg) => {
log.info('收到子进程消息:', JSON.stringify(msg))
if (msg.type === 'status') {
fireStatus(msg.event)
@ -166,6 +166,6 @@ const serverApi = {
if (server) {
server.process.send({ type: 'speed', event: { key: 'reTest' } })
}
}
},
}
module.exports = serverApi

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

@ -1,13 +1,14 @@
const shell = require('./shell')
const killByPort = require('./scripts/kill-by-port')
const setupCa = require('./scripts/setup-ca')
const getSystemEnv = require('./scripts/get-system-env')
const setSystemEnv = require('./scripts/set-system-env')
const enableLoopback = require('./scripts/enable-loopback')
const extraPath = require('./scripts/extra-path')
const getNpmEnv = require('./scripts/get-npm-env')
const getSystemEnv = require('./scripts/get-system-env')
const killByPort = require('./scripts/kill-by-port')
const setNpmEnv = require('./scripts/set-npm-env')
const setSystemEnv = require('./scripts/set-system-env')
const setSystemProxy = require('./scripts/set-system-proxy/index')
const enableLoopback = require('./scripts/enable-loopback')
const extraPath = require('./scripts/extra-path')
const setupCa = require('./scripts/setup-ca')
const shell = require('./shell')
module.exports = {
killByPort,
setupCa,
@ -21,5 +22,5 @@ module.exports = {
async exec (cmds, args) {
return shell.getSystemShell().exec(cmds, args)
},
getSystemPlatform: shell.getSystemPlatform
getSystemPlatform: shell.getSystemPlatform,
}

8
packages/core/src/shell/scripts/enable-loopback.js

@ -2,7 +2,9 @@
*/
const Shell = require('../shell')
const extraPath = require('./extra-path')
const execute = Shell.execute
const executor = {
async windows (exec) {
const loopbackPath = extraPath.getEnableLoopbackPath()
@ -10,11 +12,11 @@ const executor = {
await execFile(loopbackPath)
},
async linux (exec, { port }) {
throw Error('不支持此操作')
throw new Error('不支持此操作')
},
async mac (exec, { port }) {
throw Error('不支持此操作')
}
throw new Error('不支持此操作')
},
}
module.exports = async function (args) {

4
packages/core/src/shell/scripts/extra-path/index.js

@ -1,5 +1,5 @@
const path = require('node:path')
const log = require('../../../utils/util.log')
const path = require('path')
function getExtraPath () {
let extraPath = process.env.DS_EXTRA_PATH
@ -27,5 +27,5 @@ function getEnableLoopbackPath () {
module.exports = {
getProxyExePath,
getEnableLoopbackPath,
getClearBatPath
getClearBatPath,
}

10
packages/core/src/shell/scripts/get-npm-env.js

@ -1,9 +1,11 @@
/**
* 获取环境变量
*/
const Shell = require('../shell')
const jsonApi = require('@docmirror/mitmproxy/src/json')
const Shell = require('../shell')
const execute = Shell.execute
const executor = {
async windows (exec) {
const ret = await exec(['npm config list --json'], { type: 'cmd' })
@ -14,11 +16,11 @@ const executor = {
return {}
},
async linux (exec, { port }) {
throw Error('暂未实现此功能')
throw new Error('暂未实现此功能')
},
async mac (exec, { port }) {
throw Error('暂未实现此功能')
}
throw new Error('暂未实现此功能')
},
}
module.exports = async function (args) {

8
packages/core/src/shell/scripts/get-system-env.js

@ -2,7 +2,9 @@
* 获取环境变量
*/
const Shell = require('../shell')
const execute = Shell.execute
const executor = {
async windows (exec) {
const ret = await exec(['set'], { type: 'cmd' })
@ -19,11 +21,11 @@ const executor = {
return map
},
async linux (exec, { port }) {
throw Error('暂未实现此功能')
throw new Error('暂未实现此功能')
},
async mac (exec, { port }) {
throw Error('暂未实现此功能')
}
throw new Error('暂未实现此功能')
},
}
module.exports = async function (args) {

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

@ -1,4 +1,5 @@
const Shell = require('../shell')
const execute = Shell.execute
const executor = {
@ -9,13 +10,13 @@ const executor = {
return true
},
async linux (exec, { port }) {
await exec('kill `lsof -i:' + port + " |grep 'dev-sidecar\\|electron\\|@docmirro' |awk '{print $2}'`")
await exec(`kill \`lsof -i:${port} |grep 'dev-sidecar\\|electron\\|@docmirro' |awk '{print $2}'\``)
return true
},
async mac (exec, { port }) {
await exec('kill `lsof -i:' + port + " |grep 'dev-side\\|Elect' |awk '{print $2}'`")
await exec(`kill \`lsof -i:${port} |grep 'dev-side\\|Elect' |awk '{print $2}'\``)
return true
}
},
}
module.exports = async function (args) {

8
packages/core/src/shell/scripts/set-npm-env.js

@ -2,7 +2,9 @@
* 设置环境变量
*/
const Shell = require('../shell')
const execute = Shell.execute
const executor = {
async windows (exec, { list }) {
const cmds = []
@ -13,11 +15,11 @@ const executor = {
return ret
},
async linux (exec, { port }) {
throw Error('暂未实现此功能')
throw new Error('暂未实现此功能')
},
async mac (exec, { port }) {
throw Error('暂未实现此功能')
}
throw new Error('暂未实现此功能')
},
}
module.exports = async function (args) {

8
packages/core/src/shell/scripts/set-system-env.js

@ -2,7 +2,9 @@
* 设置环境变量
*/
const Shell = require('../shell')
const execute = Shell.execute
const executor = {
async windows (exec, { list }) {
const cmds = []
@ -22,11 +24,11 @@ const executor = {
return ret
},
async linux (exec, { port }) {
throw Error('暂未实现此功能')
throw new Error('暂未实现此功能')
},
async mac (exec, { port }) {
throw Error('暂未实现此功能')
}
throw new Error('暂未实现此功能')
},
}
module.exports = async function (args) {

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

@ -1,16 +1,16 @@
/**
* 获取环境变量
*/
const Shell = require('../../shell')
const fs = require('node:fs')
const path = require('node:path')
const request = require('request')
const Registry = require('winreg')
const log = require('../../../utils/util.log')
const Shell = require('../../shell')
const extraPath = require('../extra-path/index')
const execute = Shell.execute
const execFile = Shell.execFile
const log = require('../../../utils/util.log')
const extraPath = require('../extra-path/index')
const fs = require('fs')
const path = require('path')
const request = require('request')
let config = null
function loadConfig () {
@ -20,7 +20,6 @@ function loadConfig () {
}
async function _winUnsetProxy (exec, setEnv) {
// eslint-disable-next-line no-constant-condition
const proxyPath = extraPath.getProxyExePath()
await execFile(proxyPath, ['set', '1'])
@ -28,7 +27,7 @@ async function _winUnsetProxy (exec, setEnv) {
await exec('echo \'删除环境变量 HTTPS_PROXY、HTTP_PROXY\'')
const regKey = new Registry({ // new operator is optional
hive: Registry.HKCU, // open registry hive HKEY_CURRENT_USER
key: '\\Environment' // key containing autostart programs
key: '\\Environment', // key containing autostart programs
})
regKey.get('HTTPS_PROXY', (err) => {
if (!err) {
@ -74,12 +73,12 @@ async function downloadDomesticDomainAllowListAsync () {
let fileTxt = body
try {
if (fileTxt.indexOf('*.') < 0) {
if (!fileTxt.includes('*.')) {
fileTxt = Buffer.from(fileTxt, 'base64').toString('utf8')
// log.debug('解析 base64 后的 domestic-domain-allowlist:', fileTxt)
}
} catch (e) {
if (fileTxt.indexOf('*.') < 0) {
if (!fileTxt.includes('*.')) {
log.error(`远程 domestic-domain-allowlist.txt 文件内容即不是base64格式,也不是要求的格式,url: ${remoteFileUrl},body: ${body}`)
return
}
@ -195,7 +194,7 @@ function getProxyExcludeIpStr (split) {
try {
let domesticDomainAllowList = getDomesticDomainAllowList()
if (domesticDomainAllowList) {
domesticDomainAllowList = (domesticDomainAllowList + '\n').replaceAll(/[\r\n]+/g, '\n').replaceAll(/[^\n]*[^*.a-zA-Z\d-\n]+[^\n]*\r?\n/g, '').trim().replaceAll(/\s*\n+\s*/g, split)
domesticDomainAllowList = (`${domesticDomainAllowList}\n`).replaceAll(/[\r\n]+/g, '\n').replaceAll(/[\d*\-.A-Z]*[^\d\n*\-.A-Z][^\n]*\n/gi, '').trim().replaceAll(/\s*\n\s*/g, split)
if (domesticDomainAllowList) {
excludeIpStr += domesticDomainAllowList
log.info('系统代理排除列表拼接国内域名')
@ -224,7 +223,7 @@ async function _winSetProxy (exec, ip, port, setEnv) {
let proxyAddr = `https=http://${ip}:${port}`
// http
if (config.get().proxy.proxyHttp) {
proxyAddr = `http=http://${ip}:${port - 1};` + proxyAddr
proxyAddr = `http=http://${ip}:${port - 1};${proxyAddr}`
}
// 读取排除域名
@ -276,25 +275,25 @@ const executor = {
const setProxyCmd = [
'gsettings set org.gnome.system.proxy mode manual',
`gsettings set org.gnome.system.proxy.https host ${ip}`,
`gsettings set org.gnome.system.proxy.https port ${port}`
`gsettings set org.gnome.system.proxy.https port ${port}`,
]
// http
if (config.get().proxy.proxyHttp) {
setProxyCmd.push(`gsettings set org.gnome.system.proxy.http host ${ip}`)
setProxyCmd.push(`gsettings set org.gnome.system.proxy.http port ${port - 1}`)
} else {
setProxyCmd.push("gsettings set org.gnome.system.proxy.http host ''")
setProxyCmd.push('gsettings set org.gnome.system.proxy.http host \'\'')
setProxyCmd.push('gsettings set org.gnome.system.proxy.http port 0')
}
// 设置排除域名(ignore-hosts)
const excludeIpStr = getProxyExcludeIpStr("', '")
const excludeIpStr = getProxyExcludeIpStr('\', \'')
setProxyCmd.push(`gsettings set org.gnome.system.proxy ignore-hosts "['${excludeIpStr}']"`)
await exec(setProxyCmd)
} else { // 关闭代理
const setProxyCmd = [
'gsettings set org.gnome.system.proxy mode none'
'gsettings set org.gnome.system.proxy mode none',
]
await exec(setProxyCmd)
}
@ -342,7 +341,7 @@ const executor = {
// `
// await exec(removeEnv)
}
}
},
}
module.exports = async function (args) {

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

@ -1,24 +1,23 @@
const Shell = require('../shell')
const execute = Shell.execute
const executor = {
async windows (exec, { certPath }) {
const cmds = ['start "" "' + certPath + '"']
// eslint-disable-next-line no-unused-vars
const ret = await exec(cmds, { type: 'cmd' })
const cmds = [`start "" "${certPath}"`]
await exec(cmds, { type: 'cmd' })
return true
},
async linux (exec, { certPath }) {
const cmds = [`sudo cp ${certPath} /usr/local/share/ca-certificates`, 'sudo update-ca-certificates ']
// eslint-disable-next-line no-unused-vars
const ret = await exec(cmds)
await exec(cmds)
return true
},
async mac (exec, { certPath }) {
const cmds = ['open "' + certPath + '"']
// eslint-disable-next-line no-unused-vars
const ret = await exec(cmds, { type: 'cmd' })
const cmds = [`open "${certPath}"`]
await exec(cmds, { type: 'cmd' })
return true
}
},
}
module.exports = async function (args) {

27
packages/core/src/shell/shell.js

@ -1,11 +1,14 @@
const os = require('os')
const childProcess = require('child_process')
const _execFile = childProcess.execFile
const PowerShell = require('node-powershell')
const log = require('../utils/util.log')
const childProcess = require('node:child_process')
const os = require('node:os')
const fixPath = require('fix-path')
const iconv = require('iconv-lite')
const PowerShell = require('node-powershell')
const log = require('../utils/util.log')
const _execFile = childProcess.execFile
fixPath()
class SystemShell {
static async exec (cmds, args) {
throw new Error('You have to implement the method exec!')
@ -46,7 +49,7 @@ class WindowsSystemShell extends SystemShell {
if (type === 'ps') {
const ps = new PowerShell({
executionPolicy: 'Bypass',
noProfile: true
noProfile: true,
})
for (const cmd of cmds) {
@ -63,7 +66,7 @@ class WindowsSystemShell extends SystemShell {
} else {
let compose = 'echo "test" ' // 'chcp 65001 '
for (const cmd of cmds) {
compose += ' && ' + cmd
compose += ` && ${cmd}`
}
// compose += '&& exit'
const ret = await childExec(compose, args)
@ -75,9 +78,9 @@ class WindowsSystemShell extends SystemShell {
function _childExec (composeCmds, options = {}) {
return new Promise((resolve, reject) => {
const childProcess = require('child_process')
const childProcess = require('node:child_process')
log.info('shell:', composeCmds)
childProcess.exec(composeCmds, options, function (error, stdout, stderr) {
childProcess.exec(composeCmds, options, (error, stdout, stderr) => {
if (error) {
if (options.printErrorLog !== false) {
log.error('cmd 命令执行错误:\n===>\ncommands:', composeCmds, '\n error:', error, '\n<===')
@ -98,9 +101,9 @@ function childExec (composeCmds, options = {}) {
const encoding = 'cp936'
const binaryEncoding = 'binary'
const childProcess = require('child_process')
const childProcess = require('node:child_process')
log.info('shell:', composeCmds)
childProcess.exec(composeCmds, { encoding: binaryEncoding }, function (error, stdout, stderr) {
childProcess.exec(composeCmds, { encoding: binaryEncoding }, (error, stdout, stderr) => {
if (error) {
// console.log('------', decoder.decode(stderr))
const message = iconv.decode(Buffer.from(stderr, binaryEncoding), encoding)
@ -175,5 +178,5 @@ module.exports = {
getSystemShell,
getSystemPlatform,
execute,
execFile
execFile,
}

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

@ -34,13 +34,13 @@
// console.error(e)
// })
const fs = require('node:fs')
const request = require('request')
const fs = require('fs')
request({
url: 'https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js',
proxy: 'http://127.0.0.1:31181',
ca: fs.readFileSync('C:/Users/Administrator/.dev-sidecar/dev-sidecar.ca.crt')
// eslint-disable-next-line handle-callback-err
ca: fs.readFileSync('C:/Users/Administrator/.dev-sidecar/dev-sidecar.ca.crt'),
}, (err, res, body) => {
console.log(body)
})

5
packages/core/src/status.js

@ -1,10 +1,11 @@
const event = require('./event')
const lodash = require('lodash')
const event = require('./event')
const log = require('./utils/util.log')
const status = {
server: { enabled: false },
proxy: {},
plugin: {}
plugin: {},
}
event.register('status', (event) => {

14
packages/core/src/utils/util.log.js

@ -1,14 +1,4 @@
const log4js = require('log4js')
const config = require('../config/index')
function getDefaultConfigBasePath () {
return config.server.setting.userBasePath
}
const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info'
const path = require('path')
const filename = path.join(getDefaultConfigBasePath(), '/logs/core.log')
log4js.configure({
appenders: { std: { type: 'stdout' }, file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename } },
categories: { default: { appenders: ['file', 'std'], level: level } }
})
const log4js = require('./util.logger')
const logger = log4js.getLogger('core')
module.exports = logger

30
packages/core/src/utils/util.logger.js

@ -0,0 +1,30 @@
const path = require('node:path')
const log4js = require('log4js')
const config = require('../config/index')
const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info'
function getDefaultConfigBasePath () {
return config.server.setting.userBasePath
}
const coreLogFilename = path.join(getDefaultConfigBasePath(), '/logs/core.log')
const guiLogFilename = path.join(getDefaultConfigBasePath(), '/logs/gui.log')
const serverLogFilename = path.join(getDefaultConfigBasePath(), '/logs/server.log')
log4js.configure({
appenders: {
std: { type: 'stdout' },
core: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename: coreLogFilename },
gui: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename: guiLogFilename },
server: { level: 'debug', type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename: serverLogFilename },
},
categories: {
default: { appenders: ['std'], level },
core: { appenders: ['core', 'std'], level },
gui: { appenders: ['gui', 'std'], level },
server: { appenders: ['server', 'std'], level },
},
})
module.exports = log4js

42
packages/core/start/user_config.json5

@ -1,42 +0,0 @@
{
app: {
autoStart: {
enabled: true,
},
mode: 'default',
},
plugin: {
node: {
setting: {
yarnRegistry: 'null',
},
},
git: {
enabled: true,
},
overwall: {
enabled: false,
targets: {
'*gagedigital.com': true,
'*yonsz.net': true,
'*bootstrapcdn.com': true,
'*cloudflare.com': true,
'help.yonsz.net': true,
},
},
},
server: {
intercepts: {
'dev-sidecar.docmirror.cn': {
'.*': {
proxy: 'dev-sidecar-preview.docmirror.cn',
},
},
'test1111.gagedigital.com': {
'.*': {
proxy: 'test1.gagedigital.com',
},
}
},
}
}

12
packages/core/test/httpsVerifyTest.js

@ -1,5 +1,7 @@
const https = require('https')
const https = require('node:https')
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'
function request () {
return new Promise((resolve, reject) => {
const options = {
@ -7,7 +9,7 @@ function request () {
port: 443,
path: '/ssltest.php',
method: 'GET',
rejectUnauthorized: true
rejectUnauthorized: true,
}
console.log('ssl test: gagedigital')
const req = https.request(options, (res) => {
@ -28,12 +30,12 @@ function request () {
})
}
// eslint-disable-next-line no-undef
describe('ssl.verify', function () {
describe('ssl.verify', () => {
// eslint-disable-next-line no-undef
it('regex.test.js', async function () {
it('regex.test.js', async () => {
// https.request('https://test1.gagedigital.com/ssltest.php')
await request()
// eslint-disable-next-line no-unused-expressions
// expect(ret).be.ok
})
})

28
packages/core/test/mergeTest.js

@ -5,28 +5,28 @@ const mergeApi = require('../src/merge.js')
const defConfig = {
a: {
aa: { value: 1 },
bb: { value: 2 }
bb: { value: 2 },
},
b: { c: 2 },
c: 1,
d: [1, 2, 3],
e: {
aa: 2,
ee: 5
ee: 5,
},
f: {
x: 1
x: 1,
},
g: [1, 2],
h: null,
i: null
i: null,
}
// 自定义配置
const customConfig = {
a: {
bb: { value: 2 },
cc: { value: 3 }
cc: { value: 3 },
},
b: { c: 2 },
c: null,
@ -34,11 +34,11 @@ const customConfig = {
e: {
aa: 2,
ee: 5,
ff: 6
ff: 6,
},
f: {},
g: [1, 2],
h: null
h: null,
}
// doDiff
@ -49,16 +49,16 @@ console.log('\r')
const doDiffExpect = {
a: {
aa: null,
cc: { value: 3 }
cc: { value: 3 },
},
c: null,
d: [1, 2, 3, 4],
e: {
ff: 6
ff: 6,
},
f: {
x: null
}
x: null,
},
}
console.log('check diff result:', lodash.isEqual(doDiffResult, doDiffExpect))
console.log('\r')
@ -72,17 +72,17 @@ console.log('running:', JSON.stringify(doMergeResult, null, 2))
const doMergeExpect = {
a: {
bb: { value: 2 },
cc: { value: 3 }
cc: { value: 3 },
},
b: { c: 2 },
d: [1, 2, 3, 4],
e: {
aa: 2,
ee: 5,
ff: 6
ff: 6,
},
f: {},
g: [1, 2]
g: [1, 2],
}
console.log('check merge result:', lodash.isEqual(doMergeResult, doMergeExpect))
console.log('\r')

6
packages/core/test/regex.test.js

@ -1,13 +1,13 @@
const expect = require('chai').expect
// eslint-disable-next-line no-undef
describe('test', function () {
describe('test', () => {
// eslint-disable-next-line no-undef
it('regexp', function () {
it('regexp', () => {
const test = '^/[^/]+/[^/]+(/releases(/.*)?)?$'
const reg = new RegExp(test)
const ret = reg.test('/docmirror/dev-sidecar/releases/tag')
// eslint-disable-next-line no-unused-expressions
expect(ret).be.ok
})
})

14
packages/core/test/requestTest.js

@ -1,5 +1,5 @@
const request = require('request')
const HttpsAgent = require('@docmirror/mitmproxy/src/lib/proxy/common/ProxyHttpsAgent')
const request = require('request')
const options = {
url: 'https://raw.githubusercontent.com/docmirror/dev-sidecar/refs/heads/master/packages/core/src/config/remote_config.json5',
@ -9,8 +9,8 @@ const options = {
keepAlive: true,
timeout: 20000,
keepAliveTimeout: 30000,
rejectUnauthorized: false
})
rejectUnauthorized: false,
}),
}
if (options.agent.options) {
options.agent.options.rejectUnauthorized = false
@ -18,9 +18,7 @@ if (options.agent.options) {
}
request(options, (error, response, body) => {
console.info('error:', error,
'\n---------------------------------------------------------------------------\n' +
'response:', response,
'\n---------------------------------------------------------------------------\n' +
'body:', body)
console.info('error:', error, '\n---------------------------------------------------------------------------\n'
+ 'response:', response, '\n---------------------------------------------------------------------------\n'
+ 'body:', body)
})

5
packages/gui/README.md

@ -1,24 +1,29 @@
# dev-sidecar-gui
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

4
packages/gui/babel.config.js

@ -1,5 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
'@vue/babel-preset-jsx',
],
}

81
packages/gui/package.json

@ -2,8 +2,12 @@
"name": "@docmirror/dev-sidecar-gui",
"version": "1.8.9",
"private": false,
"author": {
"email": "xiaojunnuo@qq.com",
"name": "Greper"
},
"license": "MPL-2.0",
"main": "background.js",
"homepage": "https://github.com/docmirror/dev-sidecar",
"scripts": {
"serve": "vue-cli-service serve",
"lint": "vue-cli-service lint",
@ -15,77 +19,40 @@
"electron:icons-mac": "electron-icon-builder --input=./public/logo/mac.png --output=build --flatten",
"electron:icons-black": "electron-icon-builder --input=./public/logo/win-black.png --output=build/black --flatten"
},
"homepage": "https://github.com/docmirror/dev-sidecar",
"author": {
"email": "xiaojunnuo@qq.com",
"name": "Greper"
},
"dependencies": {
"@docmirror/dev-sidecar": "^1.8.9",
"@docmirror/mitmproxy": "^1.8.9",
"@docmirror/dev-sidecar": "workspace:*",
"@docmirror/mitmproxy": "workspace:*",
"@mihomo-party/sysproxy": "^2.0.4",
"@natmri/platform-napi": "^0.0.7",
"adm-zip": "^0.5.5",
"ant-design-vue": "^1.6.5",
"compressing": "^1.5.1",
"core-js": "^3.6.5",
"adm-zip": "^0.5.16",
"ant-design-vue": "^1.7.8",
"electron-baidu-tongji": "^1.0.5",
"electron-reload": "^1.5.0",
"electron-store": "^6.0.1",
"electron-sudo": "^4.0.12",
"electron-updater": "^4.3.5",
"es-abstract": "^1.17.7",
"extract-zip": "^2.0.1",
"iconv-lite": "^0.6.2",
"json5": "^2.1.3",
"electron-updater": "^6.3.9",
"json5": "^2.2.3",
"lodash": "^4.17.20",
"log4js": "^6.3.0",
"request-progress": "^3.0.0",
"sass": "^1.27.1",
"sass-loader": "^10.0.4",
"vue": "^2.6.11",
"sass": "^1.81.0",
"sass-loader": "^16.0.3",
"vue": "^2.7.16",
"vue-json-editor-fix-cn": "^1.4.3",
"vue-router": "^3.4.8"
"vue-router": "^3.6.5"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.0",
"@vue/cli-plugin-eslint": "^4.5.0",
"@vue/cli-service": "^4.5.0",
"@vue/eslint-config-standard": "^5.1.2",
"babel-eslint": "^10.1.0",
"electron": "^17.4.11",
"electron-builder": "^23.0.3",
"electron-devtools-installer": "^3.1.0",
"@babel/plugin-syntax-jsx": "^7.25.9",
"@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
"@vue/babel-preset-jsx": "^1.4.0",
"@vue/cli-plugin-babel": "^5.0.8",
"@vue/cli-service": "^5.0.8",
"electron": "^19.1.9",
"electron-builder": "^25.0.6",
"electron-icon-builder": "^2.0.1",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^6.2.2",
"json5-loader": "^4.0.1",
"vue-cli-plugin-electron-builder": "^2.1.1",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"@vue/standard"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
"vue-cli-plugin-electron-builder": "^3.0.0-alpha.4"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
],
"__npminstall_done": false,
"gitHead": "30ecf21cb001fb8d821e575d476ca0ffb4ff086e"
]
}

7
packages/gui/pkg/after-all-artifact-build.js

@ -1,6 +1,6 @@
const path = require('path')
const fs = require('node:fs')
const path = require('node:path')
const pkg = require('../package.json')
const fs = require('fs')
function appendIntro (context, systemType, latest) {
const version = pkg.version
@ -14,8 +14,7 @@ partMiniVersion: 1.7.0
releaseNotes:
- 升级日志
- https://download.fastgit.org/docmirror/dev-sidecar/releases/download/v${version}/DevSidecar-${version}.exe
`,
(err) => {
`, (err) => {
if (err) {
console.log('修改latest 失败')
}

4
packages/gui/pkg/after-pack.js

@ -1,7 +1,7 @@
const path = require('path')
const fs = require('node:fs')
const path = require('node:path')
const AdmZip = require('adm-zip')
const pkg = require('../package.json')
const fs = require('fs')
function writeAppUpdateYmlForLinux () {
const publishUrl = process.env.VUE_APP_PUBLISH_URL

16
packages/gui/public/index.html

@ -1,16 +1,20 @@
<!DOCTYPE html>
<html lang="en" style="height: 100%">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title><%= htmlWebpackPlugin.options.title %></title>
<script type="application/javascript">window.config = {}</script>
<script type="application/javascript">
window.config = {}
</script>
</head>
<body 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>
<!-- built files will be auto injected -->
</body>

37
packages/gui/src/background.js

@ -1,17 +1,16 @@
'use strict'
/* global __static */
import path from 'path'
import { app, protocol, BrowserWindow, Menu, Tray, ipcMain, dialog, powerMonitor, nativeImage, nativeTheme, globalShortcut } from 'electron'
import path from 'node:path'
import DevSidecar from '@docmirror/dev-sidecar'
import { app, BrowserWindow, dialog, globalShortcut, ipcMain, Menu, nativeImage, nativeTheme, powerMonitor, protocol, Tray } from 'electron'
import minimist from 'minimist'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import backend from './bridge/backend'
import DevSidecar from '@docmirror/dev-sidecar'
import jsonApi from '@docmirror/mitmproxy/src/json'
import log from './utils/util.log'
import minimist from 'minimist'
const isWindows = process.platform === 'win32'
// eslint-disable-next-line no-unused-vars
const isMac = process.platform === 'darwin'
// import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
const isDevelopment = process.env.NODE_ENV !== 'production'
// 避免其他系统出现异常,只有 Windows 使用 './background/powerMonitor'
@ -21,14 +20,14 @@ const _powerMonitor = isWindows ? require('./background/powerMonitor').powerMoni
// be closed automatically when the JavaScript object is garbage collected.
let win
let winIsHidden = false
// eslint-disable-next-line no-unused-vars
let tray // 防止被内存清理
let forceClose = false
DevSidecar.api.config.reload()
let hideDockWhenWinClose = DevSidecar.api.config.get().app.dock.hideWhenWinClose || false
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
{ scheme: 'app', privileges: { secure: true, standard: true } },
])
function openDevTools () {
@ -73,7 +72,7 @@ function setTray () {
{
// 系统托盘图标目录
label: 'DevTools (F12)',
click: switchDevTools
click: switchDevTools,
},
{
// 系统托盘图标目录
@ -82,8 +81,8 @@ function setTray () {
log.info('force quit')
forceClose = true
quit()
}
}
},
},
]
// 设置系统托盘图标
const iconRootPath = path.join(__dirname, '../extra/icons/tray')
@ -123,8 +122,8 @@ function setTray () {
showWin()
})
appTray.on('right-click', function () {
setTimeout(function () {
appTray.on('right-click', () => {
setTimeout(() => {
appTray.popUpContextMenu(contextMenu)
}, 200)
})
@ -181,11 +180,10 @@ function createWindow (startHideWindow) {
// 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
nodeIntegration: true, // process.env.ELECTRON_NODE_INTEGRATION
},
show: !startHideWindow,
// eslint-disable-next-line no-undef
icon: path.join(__static, 'icon.png')
icon: path.join(__static, 'icon.png'),
})
winIsHidden = !!startHideWindow
@ -263,7 +261,6 @@ function createWindow (startHideWindow) {
event.preventDefault()
// 切换开发者工具显示状态
switchDevTools()
// eslint-disable-next-line brace-style
}
// 按 F5,刷新页面
else if (input.key === 'F5') {
@ -368,14 +365,14 @@ if (app.getLoginItemSettings().wasOpenedAsHidden) {
log.info('start args:', args)
// 通过启动参数,判断是否隐藏窗口
const hideWindowArg = args.hideWindow + ''
const hideWindowArg = `${args.hideWindow}`
if (hideWindowArg === 'true' || hideWindowArg === '1') {
startHideWindow = true
} else if (hideWindowArg === 'false' || hideWindowArg === '0') {
startHideWindow = false
}
}
log.info('start hide window:', startHideWindow, app.getLoginItemSettings())
log.info('startHideWindow = ', startHideWindow, ', app.getLoginItemSettings() = ', jsonApi.stringify2(app.getLoginItemSettings()))
// 禁止双开
const isFirstInstance = app.requestSingleInstanceLock()
@ -483,7 +480,7 @@ if (isDevelopment) {
}
}
// 系统关机和重启时的操作
process.on('exit', function () {
process.on('exit', () => {
log.info('进程结束,退出app')
quit()
})

6
packages/gui/src/background/powerMonitor.js

@ -1,5 +1,5 @@
import { acquireShutdownBlock, insertWndProcHook, releaseShutdownBlock, removeWndProcHook, setMainWindowHandle } from '@natmri/platform-napi'
import { powerMonitor as _powerMonitor } from 'electron'
import { setMainWindowHandle, insertWndProcHook, removeWndProcHook, releaseShutdownBlock, acquireShutdownBlock } from '@natmri/platform-napi'
class PowerMonitor {
constructor () {
@ -43,7 +43,7 @@ class PowerMonitor {
if (event === 'shutdown' && process.platform === 'win32') {
if (!this._shutdownCallback) {
this._shutdownCallback = async () => {
await Promise.all(this._listeners.map((fn) => fn()))
await Promise.all(this._listeners.map(fn => fn()))
releaseShutdownBlock()
}
insertWndProcHook(this._shutdownCallback)
@ -57,7 +57,7 @@ class PowerMonitor {
off (event, listener) {
if (event === 'shutdown' && process.platform === 'win32') {
this._listeners = this._listeners.filter((fn) => fn !== listener)
this._listeners = this._listeners.filter(fn => fn !== listener)
} else {
return _powerMonitor.off(event, listener)
}

40
packages/gui/src/bridge/api/backend.js

@ -1,13 +1,15 @@
import lodash from 'lodash'
import fs from 'node:fs'
import path from 'node:path'
import DevSidecar from '@docmirror/dev-sidecar'
import { ipcMain } from 'electron'
import fs from 'fs'
import path from 'path'
import lodash from 'lodash'
const jsonApi = require('@docmirror/mitmproxy/src/json')
const pk = require('../../../package.json')
const log = require('../../utils/util.log')
const mitmproxyPath = path.join(__dirname, 'mitmproxy.js')
process.env.DS_EXTRA_PATH = path.join(__dirname, '../extra/')
const jsonApi = require('@docmirror/mitmproxy/src/json')
const log = require('../../utils/util.log')
const getDefaultConfigBasePath = function () {
return DevSidecar.api.config.get().server.setting.userBasePath
@ -42,7 +44,7 @@ const localApi = {
info: {
get () {
return {
version: pk.version
version: pk.version,
}
},
getConfigDir () {
@ -50,7 +52,7 @@ const localApi = {
},
getSystemPlatform () {
return DevSidecar.api.shell.getSystemPlatform()
}
},
},
/**
* 软件设置
@ -83,7 +85,7 @@ const localApi = {
if (setting.rootCa == null) {
setting.rootCa = {
setuped: false,
desc: '根证书未安装'
desc: '根证书未安装',
}
}
@ -96,7 +98,7 @@ const localApi = {
const settingPath = _getSettingsPath()
fs.writeFileSync(settingPath, jsonApi.stringify(setting))
log.info('保存 setting.json 配置文件成功:', settingPath)
}
},
},
/**
* 启动所有
@ -119,8 +121,8 @@ const localApi = {
*/
restart () {
return DevSidecar.api.server.restart({ mitmproxyPath })
}
}
},
},
}
function _deepFindFunction (list, parent, parentKey) {
@ -129,7 +131,7 @@ function _deepFindFunction (list, parent, parentKey) {
if (item instanceof Function) {
list.push(parentKey + key)
} else if (item instanceof Object) {
_deepFindFunction(list, item, parentKey + key + '.')
_deepFindFunction(list, item, `${parentKey + key}.`)
}
}
}
@ -184,14 +186,20 @@ export default {
// 注册从core里来的事件,并转发给view
DevSidecar.api.event.register('status', (event) => {
log.info('bridge on status, event:', event)
if (win) win.webContents.send('status', { ...event })
if (win) {
win.webContents.send('status', { ...event })
}
})
DevSidecar.api.event.register('error', (event) => {
log.error('bridge on error, event:', event)
if (win) win.webContents.send('error.core', event)
if (win) {
win.webContents.send('error.core', event)
}
})
DevSidecar.api.event.register('speed', (event) => {
if (win) win.webContents.send('speed', event)
if (win) {
win.webContents.send('speed', event)
}
})
// 合并用户配置
@ -200,5 +208,5 @@ export default {
},
devSidecar: DevSidecar,
invoke,
getDateTimeStr
getDateTimeStr,
}

8
packages/gui/src/bridge/api/open-enable-loopback.js

@ -1,12 +1,14 @@
import Sudoer from 'electron-sudo'
import DevSidecar from '@docmirror/dev-sidecar'
import Sudoer from 'electron-sudo'
export default {
async open () {
const options = { name: '设置loopback' }
const sudoer = new Sudoer(options)
const exeFile = DevSidecar.api.shell.extraPath.getEnableLoopbackPath()
await sudoer.exec(
exeFile, { env: { PARAM: 'VALUE' } }
exeFile,
{ env: { PARAM: 'VALUE' } },
)
}
},
}

9
packages/gui/src/bridge/auto-start/backend.js

@ -44,8 +44,9 @@ export default {
openAtLogin: true,
openAsHidden: true,
args: [
'--hideWindow', '"true"'
]
'--hideWindow',
'"true"',
],
})
}
@ -57,12 +58,12 @@ export default {
app.setLoginItemSettings({
openAtLogin: false,
openAsHidden: false,
args: []
args: [],
})
}
event.sender.send('auto-start', { key: 'enabled', value: false })
}
})
}
},
}

5
packages/gui/src/bridge/auto-start/front.js

@ -1,4 +1,3 @@
function install (app, api) {
api.ipc.on('auto-start', (event, message) => {
if (message.value === true) {
@ -10,10 +9,10 @@ function install (app, api) {
api.autoStart = {
async enabled (value) {
api.ipc.send('auto-start', { key: 'enabled', value })
}
},
}
}
export default {
install
install,
}

8
packages/gui/src/bridge/backend.js

@ -1,15 +1,15 @@
import api from './api/backend'
import autoStart from './auto-start/backend'
import fileSelector from './file-selector/backend'
import tongji from './tongji/backend'
import update from './update/backend'
import fileSelector from './file-selector/backend'
import autoStart from './auto-start/backend'
const modules = {
api, // 核心接口模块
fileSelector, // 文件选择模块
tongji, // 统计模块
update, // 自动更新
autoStart
autoStart,
}
export default {
install (context) {
@ -18,5 +18,5 @@ export default {
modules[module].install(context)
}
},
...modules
...modules,
}

11
packages/gui/src/bridge/error/front.js

@ -13,28 +13,27 @@ function install (app, api) {
function handleServerStartError (message, err, app, api) {
if (message.value === 'EADDRINUSE') {
// eslint-disable-next-line no-debugger
app.$confirm({
title: '端口被占用,代理服务启动失败',
content: '是否要杀掉占用进程?您也可以点击取消,然后前往加速服务->基本设置中修改代理端口',
onOk () {
// TODO 杀掉进程
api.config.get().then(config => {
api.config.get().then((config) => {
console.log('config', config)
api.shell.killByPort({ port: config.server.port }).then(ret => {
api.shell.killByPort({ port: config.server.port }).then((ret) => {
app.$message.info('杀掉进程成功,请重试开启代理服务')
})
})
},
onCancel () {
console.log('Cancel')
}
},
})
} else {
app.$message.error('加速服务启动失败:' + message.message)
app.$message.error(`加速服务启动失败:${message.message}`)
}
}
export default {
install
install,
}

10
packages/gui/src/bridge/file-selector/backend.js

@ -1,21 +1,21 @@
export default {
install (context) {
const { ipcMain, dialog, log } = context
ipcMain.on('file-selector', function (event, message) {
ipcMain.on('file-selector', (event, message) => {
if (message.key === 'open') {
dialog.showOpenDialog({
properties: ['openFile'],
...message
}).then(result => {
...message,
}).then((result) => {
if (result.canceled) {
event.sender.send('file-selector', { key: 'canceled' })
} else {
event.sender.send('file-selector', { key: 'selected', value: result.filePaths })
}
}).catch(err => {
}).catch((err) => {
log.error('选择文件失败:', err)
})
}
})
}
},
}

7
packages/gui/src/bridge/file-selector/front.js

@ -1,9 +1,8 @@
function install (app, api) {
api.fileSelector = {
open (value, options) {
return new Promise((resolve, reject) => {
api.ipc.send('file-selector', { key: 'open', value: value, ...options })
api.ipc.send('file-selector', { key: 'open', value, ...options })
api.ipc.on('file-selector', (event, message) => {
console.log('selector', message)
if (message.key === 'selected') {
@ -14,10 +13,10 @@ function install (app, api) {
api.ipc.on('file-selector', () => {})
})
})
}
},
}
}
export default {
install
install,
}

11
packages/gui/src/bridge/front.js

@ -1,10 +1,11 @@
// import api from './api/front'
import autoStart from './auto-start/front'
import error from './error/front'
import tongji from './tongji/front'
import update from './update/front'
import fileSelector from './file-selector/front'
import autoStart from './auto-start/front'
import onClose from './on-close/front'
import tongji from './tongji/front'
import update from './update/front'
const modules = {
// api, // 核心接口模块
error,
@ -12,7 +13,7 @@ const modules = {
tongji, // 统计模块
update, // 自动更新
autoStart,
onClose
onClose,
}
export default {
install (app, api, router) {
@ -20,5 +21,5 @@ export default {
modules[module].install(app, api, router)
}
},
...modules
...modules,
}

8
packages/gui/src/bridge/mitmproxy.js

@ -1,10 +1,10 @@
// eslint-disable-next-line no-unused-vars
const log = require('../utils/util.log')
const fs = require('node:fs')
const path = require('node:path')
const server = require('@docmirror/mitmproxy')
const jsonApi = require('@docmirror/mitmproxy/src/json')
const log = require('../utils/util.log')
const configPath = process.argv[2]
const fs = require('fs')
const path = require('path')
const configJson = fs.readFileSync(configPath)
log.info('读取 running.json by gui bridge 成功:', configPath)
const config = jsonApi.parse(configJson.toString())

14
packages/gui/src/bridge/on-close/front.js

@ -12,19 +12,21 @@ function install (app, api) {
}
app.$confirm({
title: '关闭策略',
content: h => <div>
<div style={'margin-top:10px'}>
content: (h) => (
<div>
<div style="margin-top:10px">
<a-radio-group vOn:change={onRadioChange} defaultValue={closeType}>
<a-radio value={1}>直接关闭</a-radio>
<a-radio value={2}>最小化到系统托盘</a-radio>
</a-radio-group>
</div>
<div style={'margin-top:10px'}>
<div style="margin-top:10px">
<a-checkbox vOn:change={onCheckChange} defaultChecked={doSave}>
记住本次选择不再提示
</a-checkbox>
</div>
</div>,
</div>
),
async onOk () {
console.log('OK. closeType=', closeType)
if (doSave) {
@ -34,11 +36,11 @@ function install (app, api) {
},
onCancel () {
console.log('Cancel. closeType=', closeType)
}
},
})
})
}
export default {
install
install,
}

12
packages/gui/src/bridge/tongji/backend.js

@ -1,9 +1,8 @@
/**
* first step
* @param {*} ipcMain
*/
const ebtMain = (ipcMain) => {
function ebtMain (ipcMain) {
const isDevelopment = process.env.NODE_ENV !== 'production'
const request = require('request')
/* istanbul ignore else */
@ -19,17 +18,16 @@ const ebtMain = (ipcMain) => {
url: `https://hm.baidu.com/hm.js?${arg}`,
method: 'GET',
headers: {
Referer: 'https://hm.baidu.com/'
}
Referer: 'https://hm.baidu.com/',
},
(err, response, body) => {
}, (err, response, body) => {
if (err) {
console.error('百度统计请求出错', err)
return
}
const rource = '(h.c.b.su=h.c.b.u||document.location.href),h.c.b.u=f.protocol+"//"+document.location.host+'
/* istanbul ignore else */
if (body && body.indexOf(rource) >= 0) {
if (body && body.includes(rource)) {
// step 3
let text = body
@ -51,5 +49,5 @@ const ebtMain = (ipcMain) => {
export default {
install (context) {
ebtMain(context.ipcMain)
}
},
}

11
packages/gui/src/bridge/tongji/front.js

@ -1,11 +1,10 @@
/**
* second step
* @param {*} ipcRenderer
* @param {*} siteId
* @param {*} router
*/
const ebtRenderer = (ipcRenderer, siteId, router) => {
function ebtRenderer (ipcRenderer, siteId, router) {
/* istanbul ignore else */
if (!(ipcRenderer && ipcRenderer.on && ipcRenderer.send)) {
throw new TypeError('require ipcRenderer')
@ -20,7 +19,9 @@ const ebtRenderer = (ipcRenderer, siteId, router) => {
ipcRenderer.on('electron-baidu-tongji-reply', (_, { text, isDevelopment }) => {
console.log('electron-baidu-tongji-reply')
/* istanbul ignore else */
if (isDevelopment) { document.body.classList.add('electron-baidu-tongji_dev') }
if (isDevelopment) {
document.body.classList.add('electron-baidu-tongji_dev')
}
window._hmt = window._hmt || []
@ -38,7 +39,7 @@ const ebtRenderer = (ipcRenderer, siteId, router) => {
router.beforeEach((to, _, next) => {
/* istanbul ignore else */
if (to.path) {
window._hmt.push(['_trackPageview', '/#' + to.fullPath])
window._hmt.push(['_trackPageview', `/#${to.fullPath}`])
console.log('baidu trace', to.fullPath)
}
@ -58,5 +59,5 @@ export default {
const { ipcRenderer } = require('electron')
ebtRenderer(ipcRenderer, BAIDU_SITE_ID, router)
},
ebtRenderer
ebtRenderer,
}

75
packages/gui/src/bridge/update/backend.js

@ -1,16 +1,15 @@
import fs from 'node:fs'
import path from 'node:path'
import DevSidecar from '@docmirror/dev-sidecar'
import AdmZip from 'adm-zip'
import { ipcMain } from 'electron'
import { autoUpdater } from 'electron-updater'
import path from 'path'
import request from 'request'
import progress from 'request-progress'
import fs from 'fs'
import AdmZip from 'adm-zip'
import log from '../../utils/util.log'
import appPathUtil from '../../utils/util.apppath'
import pkg from '../../../package.json'
import DevSidecar from '@docmirror/dev-sidecar'
import appPathUtil from '../../utils/util.apppath'
import log from '../../utils/util.log'
// eslint-disable-next-line no-unused-vars
const isMac = process.platform === 'darwin'
const isLinux = process.platform === 'linux'
@ -24,16 +23,16 @@ function downloadFile (uri, filePath, onProgress, onSuccess, onError) {
// delay: 1000, // Only start to emit after 1000ms delay, defaults to 0ms
// lengthHeader: 'x-transfer-length' // Length header to use, defaults to content-length
})
.on('progress', function (state) {
.on('progress', (state) => {
onProgress(state.percent * 100)
log.log('progress', state.percent)
})
.on('error', function (err) {
.on('error', (err) => {
// Do something with err
log.error('下载升级包失败:', err)
onError(err)
})
.on('end', function () {
.on('end', () => {
// Do something after request finishes
onSuccess()
})
@ -44,10 +43,10 @@ function parseVersion (version) {
const matched = version.match(/^v?(\d+\.\d+\.\d+)(.*)$/)
const versionArr = matched[1].split('.')
return {
major: parseInt(versionArr[0]),
minor: parseInt(versionArr[1]),
patch: parseInt(versionArr[2]),
suffix: matched[2]
major: Number.parseInt(versionArr[0]),
minor: Number.parseInt(versionArr[1]),
patch: Number.parseInt(versionArr[2]),
suffix: matched[2],
}
}
@ -115,7 +114,7 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
error: '更新失败',
checking: '检查更新中',
updateAva: '发现新版本',
updateNotAva: '当前为最新版本,无需更新'
updateNotAva: '当前为最新版本,无需更新',
}
// 本地开发环境,改变app-update.yml地址
if (process.env.NODE_ENV === 'development') {
@ -141,11 +140,11 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
// 检查更新
const releasesApiUrl = 'https://api.github.com/repos/docmirror/dev-sidecar/releases'
async function checkForUpdatesFromGitHub () {
request(releasesApiUrl, { headers: { 'User-Agent': 'DS/' + curVersion, 'Server-Name': 'baidu.com' } }, (error, response, body) => {
request(releasesApiUrl, { headers: { 'User-Agent': `DS/${curVersion}`, 'Server-Name': 'baidu.com' } }, (error, response, body) => {
try {
if (error) {
log.error('检查更新失败:', error)
const errorMsg = '检查更新失败:' + error
const errorMsg = `检查更新失败:${error}`
win.webContents.send('update', { key: 'error', action: 'checkForUpdate', error: errorMsg })
return
}
@ -203,9 +202,9 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
value: {
version,
releaseNotes: versionData.body
? (versionData.body.replace(/\r\n/g, '\n').replace(/https:\/\/github.com\/docmirror\/dev-sidecar/g, '').replace(/(?<=(^|\n))[ \t]*[ #]*#\s*/g, '') || '无')
: '无'
}
? (versionData.body.replace(/\r\n/g, '\n').replace(/https:\/\/github.com\/docmirror\/dev-sidecar/g, '').replace(/(?<=(^|\n))[ \t]*(?:#[ #]*)?#\s*/g, '') || '无')
: '无',
},
})
} else {
log.info(`检查更新:没有新版本,最近发布的版本号为 '${version}',而当前版本号为 '${curVersion}'`)
@ -229,15 +228,15 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
let message
if (response) {
message = '检查更新失败: ' + (bodyObj && bodyObj.message ? bodyObj.message : response.message) + ', code: ' + response.statusCode
message = `检查更新失败: ${bodyObj && bodyObj.message ? bodyObj.message : response.message}, code: ${response.statusCode}`
} else {
message = '检查更新失败: ' + (bodyObj && bodyObj.message ? bodyObj.message : body)
message = `检查更新失败: ${bodyObj && bodyObj.message ? bodyObj.message : body}`
}
win.webContents.send('update', { key: 'error', action: 'checkForUpdate', error: message })
}
} catch (e) {
log.error('检查更新失败:', e)
win.webContents.send('update', { key: 'error', action: 'checkForUpdate', error: '检查更新失败:' + e.message })
win.webContents.send('update', { key: 'error', action: 'checkForUpdate', error: `检查更新失败:${e.message}` })
}
})
}
@ -252,10 +251,10 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
} catch (e) {
fs.mkdirSync(fileDir)
}
const filePath = path.join(fileDir, value.version + '.zip')
const filePath = path.join(fileDir, `${value.version}.zip`)
downloadFile(value.partPackage, filePath, (data) => {
win.webContents.send('update', { key: 'progress', value: parseInt(data) })
win.webContents.send('update', { key: 'progress', value: Number.parseInt(data) })
}, () => {
// 文件下载完成
win.webContents.send('update', { key: 'progress', value: 100 })
@ -263,10 +262,10 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
partPackagePath = filePath
win.webContents.send('update', {
key: 'downloaded',
value: value
value,
})
}, (error) => {
sendUpdateMessage({ key: 'error', value: error, error: error })
sendUpdateMessage({ key: 'error', value: error, error })
})
}
@ -294,34 +293,34 @@ function updateHandle (app, api, win, beforeQuit, quit, log) {
}
}
autoUpdater.on('error', function (error) {
autoUpdater.on('error', (error) => {
log.warn('autoUpdater error:', error)
sendUpdateMessage({ key: 'error', value: error, error: error })
sendUpdateMessage({ key: 'error', value: error, error })
// dialog.showErrorBox('Error: ', error == null ? 'unknown' : (error.stack || error).toString())
})
autoUpdater.on('checking-for-update', function () {
autoUpdater.on('checking-for-update', () => {
log.info('autoUpdater checking-for-update')
sendUpdateMessage({ key: 'checking', value: message.checking })
})
autoUpdater.on('update-available', function (info) {
autoUpdater.on('update-available', (info) => {
log.info('autoUpdater update-available')
sendUpdateMessage({ key: 'available', value: info })
})
autoUpdater.on('update-not-available', function () {
autoUpdater.on('update-not-available', () => {
log.info('autoUpdater update-not-available')
sendUpdateMessage({ key: 'notAvailable', value: message.updateNotAva })
})
// 更新下载进度
autoUpdater.on('download-progress', function (progressObj) {
autoUpdater.on('download-progress', (progressObj) => {
log.info('autoUpdater download-progress')
win.webContents.send('update', { key: 'progress', value: parseInt(progressObj.percent) })
win.webContents.send('update', { key: 'progress', value: Number.parseInt(progressObj.percent) })
})
// 更新完成,重启应用
autoUpdater.on('update-downloaded', function (info) {
autoUpdater.on('update-downloaded', (info) => {
log.info('download complete, version:', info.version)
win.webContents.send('update', {
key: 'downloaded',
value: info
value: info,
})
})
@ -374,9 +373,9 @@ export default {
Object.defineProperty(app, 'isPackaged', {
get () {
return true
}
},
})
}
updateHandle(app, api, win, beforeQuit, quit, log)
}
},
}

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

@ -22,7 +22,7 @@ function install (app, api) {
},
doUpdateNow () {
api.ipc.send('update', { key: 'doUpdateNow' })
}
},
}
function handleUpdateMessage (message) {
@ -75,20 +75,25 @@ function install (app, api) {
function goManualUpdate (value) {
updateParams.newVersion = false
app.$confirm({
// title: '暂不支持自动升级',
title: '暂不提供自动升级',
cancelText: '取消',
okText: '打开链接',
width: 420,
content: h => {
return <div>
<div>请前往 <a onClick={openGithubUrl}>github项目release页面</a> </div>
content: (h) => {
return (
<div>
<div>
请前往
<a onClick={openGithubUrl}>github项目release页面</a>
下载新版本手动安装
</div>
<div><a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a></div>
</div>
)
},
onOk () {
openGithubUrl()
}
},
})
}
@ -141,31 +146,41 @@ function install (app, api) {
}
console.log(value)
app.$confirm({
title: '发现新版本:v' + value.version,
title: `发现新版本:v${value.version}`,
cancelText: '暂不升级',
okText: '升级',
width: 700,
content: h => {
content: (h) => {
if (value.releaseNotes) {
const notes = []
if (typeof value.releaseNotes === 'string') {
const releaseNotes = value.releaseNotes.replace(/\r\n/g, '\n')
return <div>
<div>发布公告<a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a></div>
return (
<div>
<div>
发布公告
<a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a>
</div>
<hr />
<pre style="max-height:350px;font-family:auto">
{releaseNotes}
</pre>
</div>
)
} else {
for (const note of value.releaseNotes) {
notes.push(<li>{note}</li>)
}
return <div>
<div>发布公告<a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a></div>
return (
<div>
<div>
发布公告
<a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a>
</div>
<div>更新内容</div>
<ol>{notes}</ol>
</div>
)
}
}
},
@ -175,7 +190,7 @@ function install (app, api) {
},
onCancel () {
console.log('Cancel')
}
},
})
}
@ -187,37 +202,47 @@ function install (app, api) {
cancelText: '暂不升级',
okText: '立即升级',
width: 700,
content: h => {
content: (h) => {
if (value.releaseNotes) {
const notes = []
if (typeof value.releaseNotes === 'string') {
const releaseNotes = value.releaseNotes.replace(/\r\n/g, '\n')
return <div>
<div>发布公告<a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a></div>
return (
<div>
<div>
发布公告
<a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a>
</div>
<hr />
<pre style="max-height:350px;font-family:auto">
{releaseNotes}
</pre>
</div>
)
} else {
for (const note of value.releaseNotes) {
notes.push(<li>{note}</li>)
}
return <div>
<div>发布公告<a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a></div>
return (
<div>
<div>
发布公告
<a onClick={openGithubUrl}>https://github.com/docmirror/dev-sidecar/releases</a>
</div>
<div>更新内容</div>
<ol>{notes}</ol>
</div>
)
}
}
},
onOk () {
api.update.doUpdateNow()
}
},
})
}
}
export default {
install
install,
}

14
packages/gui/src/main.js

@ -1,11 +1,11 @@
import Vue from 'vue'
import App from './view/App.vue'
import antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
import view from './view'
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './view/router'
import view from './view'
import App from './view/App.vue'
import DsContainer from './view/components/container'
import routes from './view/router'
import 'ant-design-vue/dist/antd.css'
import './view/style/index.scss'
import './view/style/theme/dark.scss' // 暗色主题
@ -16,11 +16,11 @@ Vue.component(DsContainer)
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
routes, // (缩写) 相当于 routes: routes
})
const app = new Vue({
router,
render: h => h(App)
render: h => h(App),
})
view.initApi(app).then(async (api) => {
// 初始化status

7
packages/gui/src/utils/util.apppath.js

@ -1,5 +1,6 @@
import path from 'path'
import os from 'os'
import os from 'node:os'
import path from 'node:path'
function getSystemPlatform () {
switch (os.platform()) {
case 'darwin':
@ -22,5 +23,5 @@ export default {
return path.join(exePath, '../../')
}
return path.join(exePath, '../')
}
},
}

15
packages/gui/src/utils/util.log.js

@ -1,14 +1,5 @@
const log4js = require('log4js')
const DevSidecar = require('@docmirror/dev-sidecar')
const getDefaultConfigBasePath = function () {
return DevSidecar.api.config.get().server.setting.userBasePath
}
const level = process.env.NODE_ENV === 'development' ? 'debug' : 'info'
const path = require('path')
const filename = path.join(getDefaultConfigBasePath(), '/logs/gui.log')
log4js.configure({
appenders: { std: { type: 'stdout' }, file: { type: 'file', pattern: 'yyyy-MM-dd', daysToKeep: 3, filename } },
categories: { default: { appenders: ['file', 'std'], level } }
})
const log4js = require('@docmirror/dev-sidecar/src/utils/util.logger')
const logger = log4js.getLogger('gui')
module.exports = logger

107
packages/gui/src/view/App.vue

@ -1,49 +1,7 @@
<template>
<a-config-provider :locale="locale">
<div class="ds_layout" :class="themeClass">
<a-layout>
<a-layout-sider :theme="theme">
<div class="logo"></div>
<div class="aside">
<a-menu
mode="inline"
:defaultSelectedKeys="[$route.fullPath]"
:defaultOpenKeys="['/plugin']"
>
<template v-for="(item) of menus">
<a-sub-menu v-if="item.children && item.children.length>0" :key="item.path" @titleClick="titleClick(item)">
<span slot="title"><a-icon :type="item.icon?item.icon:'file'"/><span>{{item.title}}</span></span>
<a-menu-item v-for="(sub) of item.children" :key="sub.path" @click="menuClick(sub)">
<a-icon :type="sub.icon?sub.icon:'file'"/> {{ sub.title }}
</a-menu-item>
</a-sub-menu>
<a-menu-item v-else :key="item.path" @click="menuClick(item)">
<a-icon :type="item.icon?item.icon:'file'"/>
<span class="nav-text">{{ item.title }}</span>
</a-menu-item>
</template>
</a-menu>
</div>
</a-layout-sider>
<a-layout>
<!-- <a-layout-header>Header</a-layout-header>-->
<a-layout-content>
<router-view></router-view>
</a-layout-content>
<a-layout-footer>
<div class="footer">
©2020-2024 docmirror.cn by Greper, WangLiang <span>{{info.version}}</span>
</div>
</a-layout-footer>
</a-layout>
</a-layout>
</div>
</a-config-provider>
</template>
<script>
import createMenus from '@/view/router/menu'
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
export default {
name: 'App',
components: {
@ -53,21 +11,21 @@ export default {
locale: zhCN,
info: {},
menus: undefined,
config: undefined
config: undefined,
}
},
computed: {
themeClass () {
return 'theme-' + this.config.app.theme
return `theme-${this.config.app.theme}`
},
theme () {
return this.config.app.theme
}
},
},
created () {
this.menus = createMenus(this)
this.config = this.$global.config
this.$api.info.get().then(ret => {
this.$api.info.get().then((ret) => {
this.info = ret
})
},
@ -81,11 +39,54 @@ export default {
menuClick (item) {
console.log('menu click', item)
this.$router.replace(item.path)
}
}
},
},
}
</script>
<template>
<a-config-provider :locale="locale">
<div class="ds_layout" :class="themeClass">
<a-layout>
<a-layout-sider :theme="theme">
<div class="logo" />
<div class="aside">
<a-menu
mode="inline"
:default-selected-keys="[$route.fullPath]"
:default-open-keys="['/plugin']"
>
<template v-for="(item) of menus">
<a-sub-menu v-if="item.children && item.children.length > 0" :key="item.path" @titleClick="titleClick(item)">
<span slot="title"><a-icon :type="item.icon ? item.icon : 'file'" /><span>{{ item.title }}</span></span>
<a-menu-item v-for="(sub) of item.children" :key="sub.path" @click="menuClick(sub)">
<a-icon :type="sub.icon ? sub.icon : 'file'" /> {{ sub.title }}
</a-menu-item>
</a-sub-menu>
<a-menu-item v-else :key="item.path" @click="menuClick(item)">
<a-icon :type="item.icon ? item.icon : 'file'" />
<span class="nav-text">{{ item.title }}</span>
</a-menu-item>
</template>
</a-menu>
</div>
</a-layout-sider>
<a-layout>
<!-- <a-layout-header>Header</a-layout-header> -->
<a-layout-content>
<router-view />
</a-layout-content>
<a-layout-footer>
<div class="footer">
©2020-2024 docmirror.cn by Greper, WangLiang <span>{{ info.version }}</span>
</div>
</a-layout-footer>
</a-layout>
</a-layout>
</div>
</a-config-provider>
</template>
<style lang="scss">
body {
height: 100%;
@ -112,13 +113,13 @@ body{
border-right: 1px solid #eee;
}
.ant-layout {
height:100%
height: 100%;
}
.logo {
padding: 5px;
border-bottom: #eee solid 1px;
height: 60px;
background-image: url("/logo/logo-lang.svg");
background-image: url('../../public/logo/logo-lang.svg');
background-size: auto 50px;
background-repeat: no-repeat;
background-position: 5px center;
@ -128,7 +129,9 @@ body{
text-align: center;
border-top: #d6d4d4 solid 1px;
}
.ant-menu-inline, .ant-menu-vertical, .ant-menu-vertical-left{
.ant-menu-inline,
.ant-menu-vertical,
.ant-menu-vertical-left {
border: 0;
}
}

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

@ -1,13 +1,14 @@
import lodash from 'lodash'
import { ipcRenderer, shell } from 'electron'
import lodash from 'lodash'
let inited = false
let apiObj = null
export function apiInit (app) {
const invoke = (api, args) => {
return ipcRenderer.invoke('apiInvoke', [api, args]).catch(e => {
return ipcRenderer.invoke('apiInvoke', [api, args]).catch((e) => {
app.$notification.error({
message: 'Api invoke error',
description: e.message
description: e.message,
})
})
}
@ -31,8 +32,8 @@ export function apiInit (app) {
},
openPath (file) {
shell.openPath(file)
}
}
},
},
}
const bindApi = (api, param1) => {
@ -42,7 +43,7 @@ export function apiInit (app) {
}
if (!inited) {
return invoke('getApiList').then(list => {
return invoke('getApiList').then((list) => {
inited = true
for (const item of list) {
bindApi(item)
@ -52,7 +53,7 @@ export function apiInit (app) {
})
}
return new Promise(resolve => {
return new Promise((resolve) => {
resolve(apiObj)
})
}

29
packages/gui/src/view/components/container.vue

@ -1,19 +1,25 @@
<script>
export default {
name: 'DsContainer',
}
</script>
<template>
<div class="ds-container">
<div class="body-wrapper">
<div v-if="$slots.header" class="container-header"><slot name="header"></slot></div>
<div class="container-body"> <slot></slot></div>
<div class="container-footer"> <slot name="footer"></slot></div>
<div v-if="$slots.header" class="container-header">
<slot name="header" />
</div>
<div class="container-body">
<slot />
</div>
<div class="container-footer">
<slot name="footer" />
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ds-container'
}
</script>
<style lang="scss">
.ds-container {
height: 100%;
@ -34,8 +40,8 @@ export default {
.container-header {
padding: 15px;
border-bottom: 1px solid #EEE;
background: #FFF;
border-bottom: 1px solid #eee;
background: #fff;
height: 60px;
display: flex;
align-items: center;
@ -49,5 +55,4 @@ export default {
padding: 15px;
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save