mirror of
https://github.com/lecepin/WeChatVideoDownloader.git
synced 2025-04-05 20:11:10 +08:00
feat: 代理/证书/拦截
This commit is contained in:
parent
7a4e26c8ce
commit
08253c9e49
42
electron/cert.js
Normal file
42
electron/cert.js
Normal file
@ -0,0 +1,42 @@
|
||||
import CONFIG from './const';
|
||||
import mkdirp from 'mkdirp';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import sudo from 'sudo-prompt';
|
||||
import { clipboard, dialog } from 'electron';
|
||||
|
||||
function checkCertInstalled() {
|
||||
return fs.existsSync(CONFIG.INSTALL_CERT_FLAG);
|
||||
}
|
||||
|
||||
export async function installCert(checkInstalled = true) {
|
||||
if (checkInstalled && checkCertInstalled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
return new Promise((resolve, reject) => {
|
||||
mkdirp.sync(path.dirname(CONFIG.INSTALL_CERT_FLAG));
|
||||
clipboard.writeText(
|
||||
`echo "输入本地登录密码" && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${CONFIG.CERT_PUBLIC_PATH}" && touch ${CONFIG.INSTALL_CERT_FLAG} && echo "安装完成"`,
|
||||
);
|
||||
dialog.showMessageBoxSync({
|
||||
type: 'info',
|
||||
message: `命令已复制到剪贴板,粘贴命令到终端并运行以安装并信任证书`,
|
||||
});
|
||||
|
||||
reject();
|
||||
});
|
||||
} else {
|
||||
return sudo.exec(
|
||||
`${CONFIG.WIN_CERT_INSTALL_HELPER} -c -add ${CONFIG.CERT_PUBLIC_PATH} -s root`,
|
||||
{ name: CONFIG.APP_EN_NAME },
|
||||
(error, stdout) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
resolve(stdout);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,24 +1,34 @@
|
||||
import path from "path";
|
||||
import isDev from "electron-is-dev";
|
||||
import url from "url";
|
||||
import { app } from "electron";
|
||||
import path from 'path';
|
||||
import isDev from 'electron-is-dev';
|
||||
import url from 'url';
|
||||
import os from 'os';
|
||||
import { app } from 'electron';
|
||||
|
||||
const APP_PATH = app.getAppPath();
|
||||
// 对于一些 shell 去执行的文件,asar 目录下无法使用。配合 extraResources
|
||||
const EXECUTABLE_PATH = path.join(
|
||||
APP_PATH.indexOf("app.asar") > -1
|
||||
? APP_PATH.substring(0, APP_PATH.indexOf("app.asar"))
|
||||
APP_PATH.indexOf('app.asar') > -1
|
||||
? APP_PATH.substring(0, APP_PATH.indexOf('app.asar'))
|
||||
: APP_PATH,
|
||||
"public"
|
||||
'public',
|
||||
);
|
||||
const HOME_PATH = path.join(os.homedir(), '.wechat-video-downloader');
|
||||
|
||||
export default {
|
||||
APP_START_URL: isDev
|
||||
? "http://localhost:3000"
|
||||
? 'http://localhost:3000'
|
||||
: url.format({
|
||||
pathname: path.join(APP_PATH, "./build/index.html"),
|
||||
protocol: "file:",
|
||||
pathname: path.join(APP_PATH, './build/index.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true,
|
||||
}),
|
||||
IS_DEV: isDev,
|
||||
EXECUTABLE_PATH,
|
||||
HOME_PATH,
|
||||
CERT_PRIVATE_PATH: path.join(EXECUTABLE_PATH, './keys/private.pem'),
|
||||
CERT_PUBLIC_PATH: path.join(EXECUTABLE_PATH, './keys/public.pem'),
|
||||
INSTALL_CERT_FLAG: path.join(HOME_PATH, './installed.lock'),
|
||||
WIN_CERT_INSTALL_HELPER: path.join(EXECUTABLE_PATH, './w_c.exe'),
|
||||
APP_CN_NAME: '微信视频号下载器',
|
||||
APP_EN_NAME: 'WeChat Video Downloader',
|
||||
};
|
||||
|
@ -1,18 +1,20 @@
|
||||
import { app, BrowserWindow } from "electron";
|
||||
import log from "electron-log";
|
||||
import CONFIG from "./const";
|
||||
import { checkUpdate } from "./utils";
|
||||
import { app, BrowserWindow } from 'electron';
|
||||
import log from 'electron-log';
|
||||
import CONFIG from './const';
|
||||
import { checkUpdate } from './utils';
|
||||
import { startServer } from './proxyServer';
|
||||
import { installCert } from './cert';
|
||||
|
||||
app.commandLine.appendSwitch("--no-proxy-server");
|
||||
app.commandLine.appendSwitch('--no-proxy-server');
|
||||
|
||||
function createWindow() {
|
||||
// electron.Menu.setApplicationMenu(null);
|
||||
checkUpdate(
|
||||
"https://cdn.jsdelivr.net/gh/lecepin/electron-react-tpl/package.json",
|
||||
"https://github.com/lecepin/electron-react-tpl/releases"
|
||||
'https://cdn.jsdelivr.net/gh/lecepin/electron-react-tpl/package.json',
|
||||
'https://github.com/lecepin/electron-react-tpl/releases',
|
||||
);
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
// resizable: false,
|
||||
@ -24,26 +26,29 @@ function createWindow() {
|
||||
},
|
||||
});
|
||||
|
||||
mainWindow.loadURL(CONFIG.APP_START_URL);
|
||||
mainWindow.loadURL('https://baidu.com' ?? CONFIG.APP_START_URL);
|
||||
CONFIG.IS_DEV && mainWindow.webContents.openDevTools();
|
||||
|
||||
mainWindow.on("closed", function () {
|
||||
mainWindow = null;
|
||||
});
|
||||
installCert()
|
||||
.then(data => {
|
||||
console.log('install cert success', data);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error('err', err);
|
||||
});
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow();
|
||||
|
||||
app.on("activate", () => {
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
37
electron/proxyServer.js
Normal file
37
electron/proxyServer.js
Normal file
@ -0,0 +1,37 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import hoxy from 'hoxy';
|
||||
import getPort from 'get-port';
|
||||
import { app } from 'electron';
|
||||
import CONFIG from './const';
|
||||
import { setProxy, closeProxy } from './setProxy';
|
||||
|
||||
export async function startServer({ interceptCallback = f => f, errorCallback = f => f }) {
|
||||
const port = await getPort();
|
||||
const proxy = hoxy
|
||||
.createServer({
|
||||
certAuthority: {
|
||||
key: fs.readFileSync(CONFIG.CERT_PRIVATE_PATH),
|
||||
cert: fs.readFileSync(CONFIG.CERT_PUBLIC_PATH),
|
||||
},
|
||||
})
|
||||
.listen(port, () => {
|
||||
setProxy('127.0.0.1', port).catch(errorCallback);
|
||||
});
|
||||
|
||||
proxy.intercept(
|
||||
{
|
||||
phase: 'request',
|
||||
},
|
||||
interceptCallback,
|
||||
);
|
||||
}
|
||||
|
||||
app.on('before-quit', async e => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await closeProxy();
|
||||
} catch (error) {}
|
||||
|
||||
app.exit();
|
||||
});
|
118
electron/setProxy.js
Normal file
118
electron/setProxy.js
Normal file
@ -0,0 +1,118 @@
|
||||
import { exec } from 'child_process';
|
||||
import regedit from 'regedit';
|
||||
|
||||
export async function setProxy(host, port) {
|
||||
if (process.platform === 'darwin') {
|
||||
const networks = await getMacAvailableNetworks();
|
||||
|
||||
if (networks.length === 0) {
|
||||
throw 'no network';
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
networks.map(network => {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(`networksetup -setsecurewebproxy "${network}" ${host} ${port}`, error => {
|
||||
if (error) {
|
||||
reject(null);
|
||||
} else {
|
||||
resolve(network);
|
||||
}
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
const valuesToPut = {
|
||||
'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings': {
|
||||
ProxyServer: {
|
||||
value: `${host}:${port}`,
|
||||
type: 'REG_SZ',
|
||||
},
|
||||
ProxyEnable: {
|
||||
value: 1,
|
||||
type: 'REG_DWORD',
|
||||
},
|
||||
},
|
||||
};
|
||||
return editWinRegPromise(valuesToPut);
|
||||
}
|
||||
}
|
||||
|
||||
export async function closeProxy() {
|
||||
if (process.platform === 'darwin') {
|
||||
const networks = await getMacAvailableNetworks();
|
||||
|
||||
if (networks.length === 0) {
|
||||
throw 'no network';
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
networks.map(network => {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(`networksetup -setsecurewebproxystate "${network}" off`, error => {
|
||||
if (error) {
|
||||
reject(null);
|
||||
} else {
|
||||
resolve(network);
|
||||
}
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
const valuesToPut = {
|
||||
'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings': {
|
||||
ProxyEnable: {
|
||||
value: 0,
|
||||
type: 'REG_DWORD',
|
||||
},
|
||||
},
|
||||
};
|
||||
return editWinRegPromise(valuesToPut);
|
||||
}
|
||||
}
|
||||
|
||||
function getMacAvailableNetworks() {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec('networksetup -listallnetworkservices', (error, stdout) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
Promise.all(
|
||||
stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map(network => {
|
||||
return new Promise(resolve => {
|
||||
exec(
|
||||
`networksetup getinfo "${network}" | grep "^IP address:\\s\\d"`,
|
||||
(error, stdout) => {
|
||||
if (error) {
|
||||
resolve(null);
|
||||
} else {
|
||||
resolve(stdout ? network : null);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}),
|
||||
).then(networks => {
|
||||
resolve(networks.filter(Boolean));
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function editWinRegPromise(valuesToPut) {
|
||||
return new Promise((resolve, reject) => {
|
||||
regedit.putValue(valuesToPut, function (err) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
@ -1,22 +1,22 @@
|
||||
import { get } from "axios";
|
||||
const { app, dialog, shell } = require("electron");
|
||||
import semver from "semver";
|
||||
import { get } from 'axios';
|
||||
const { app, dialog, shell } = require('electron');
|
||||
import semver from 'semver';
|
||||
|
||||
// packageUrl 需要包含 { "version": "1.0.0" } 结构
|
||||
function checkUpdate(
|
||||
// 可以使用加速地址 https://cdn.jsdelivr.net/gh/lecepin/electron-react-tpl/package.json
|
||||
packageUrl = "https://raw.githubusercontent.com/lecepin/electron-react-tpl/master/package.json",
|
||||
downloadUrl = "https://github.com/lecepin/electron-react-tpl/releases"
|
||||
packageUrl = 'https://raw.githubusercontent.com/lecepin/electron-react-tpl/master/package.json',
|
||||
downloadUrl = 'https://github.com/lecepin/electron-react-tpl/releases',
|
||||
) {
|
||||
get(packageUrl)
|
||||
.then(({ data }) => {
|
||||
if (semver.gt(data?.version, app.getVersion())) {
|
||||
const result = dialog.showMessageBoxSync({
|
||||
message: "发现新版本,是否更新?",
|
||||
type: "question",
|
||||
message: '发现新版本,是否更新?',
|
||||
type: 'question',
|
||||
cancelId: 1,
|
||||
defaultId: 0,
|
||||
buttons: ["进入新版本下载页面", "取消"],
|
||||
buttons: ['进入新版本下载页面', '取消'],
|
||||
});
|
||||
|
||||
if (result === 0 && downloadUrl) {
|
||||
@ -24,7 +24,7 @@ function checkUpdate(
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {});
|
||||
.catch(err => {});
|
||||
}
|
||||
|
||||
export { checkUpdate };
|
||||
|
18
package.json
18
package.json
@ -8,12 +8,13 @@
|
||||
"postinstall": "husky install",
|
||||
"start": "concurrently \"cross-env BROWSER=none npm run start-web\" \"wait-on http://localhost:3000 && npm run start-electron\" ",
|
||||
"start-web": "react-app-rewired start",
|
||||
"start-electron": "parcel build --target electron --no-cache && electron .",
|
||||
"start-electron": "webpack --config webpack.electron.js && electron .",
|
||||
"build-web": "react-app-rewired build",
|
||||
"build-electron": "parcel build --target electron --no-cache",
|
||||
"build-electron": "webpack --config webpack.electron.js",
|
||||
"build-all": "rm -rf ./build && rm -rf ./build-electron && npm run build-electron && npm run build-web",
|
||||
"pack": "npm run build-all && electron-builder",
|
||||
"gen-icon": "electron-icon-builder --input=./public/icon/icon.png --output=./public/icon"
|
||||
"gen-icon": "electron-icon-builder --input=./public/icon/icon.png --output=./public/icon",
|
||||
"pretty": "prettier -c --write \"(src/**/*|electron/**/*)\""
|
||||
},
|
||||
"targets": {
|
||||
"electron": {
|
||||
@ -41,15 +42,22 @@
|
||||
"prettier": "^2.6.2",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"wait-on": "^6.0.1"
|
||||
"wait-on": "^6.0.1",
|
||||
"webpack": "^5.72.1",
|
||||
"webpack-cli": "^4.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"electron-is-dev": "^2.0.0",
|
||||
"electron-log": "^4.4.7",
|
||||
"get-port": "^6.1.2",
|
||||
"hoxy": "^3.3.1",
|
||||
"mkdirp": "^1.0.4",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"semver": "^7.3.7"
|
||||
"regedit": "^5.1.1",
|
||||
"semver": "^7.3.7",
|
||||
"sudo-prompt": "^9.2.1"
|
||||
},
|
||||
"author": "lecepin",
|
||||
"license": "ISC",
|
||||
|
BIN
public/w_c.exe
Normal file
BIN
public/w_c.exe
Normal file
Binary file not shown.
10
src/App.jsx
10
src/App.jsx
@ -1,6 +1,6 @@
|
||||
import logo from "./logo.png";
|
||||
import { shell } from "electron";
|
||||
import "./App.css";
|
||||
import logo from './logo.png';
|
||||
import { shell } from 'electron';
|
||||
import './App.css';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@ -11,9 +11,9 @@ function App() {
|
||||
Edit <code>src/App.js</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
onClick={(e) => {
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
shell.openExternal("https://github.com/lecepin/electron-react-tpl");
|
||||
shell.openExternal('https://github.com/lecepin/electron-react-tpl');
|
||||
}}
|
||||
className="App-link"
|
||||
href="#"
|
||||
|
@ -1,13 +1,11 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
</React.StrictMode>,
|
||||
);
|
||||
|
15
webpack.electron.js
Normal file
15
webpack.electron.js
Normal file
@ -0,0 +1,15 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: path.resolve(__dirname, './electron/index.js'),
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
path: path.resolve(__dirname, './build-electron'),
|
||||
},
|
||||
module: {
|
||||
rules: [],
|
||||
},
|
||||
devtool: false && 'source-map',
|
||||
target: 'electron-main',
|
||||
node: false,
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user