2023-12-01 15:16:16 +08:00
|
|
|
import {app, ipcMain, Notification} from "electron";
|
2023-11-28 17:49:08 +08:00
|
|
|
import {Config, getConfig} from "../storage/config";
|
2024-07-17 14:21:31 +08:00
|
|
|
import {listProxy, Proxy} from "../storage/proxy";
|
2023-11-28 17:49:08 +08:00
|
|
|
import {getVersionById} from "../storage/version";
|
2023-12-01 14:17:14 +08:00
|
|
|
import treeKill from "tree-kill";
|
2023-11-27 15:03:25 +08:00
|
|
|
|
|
|
|
const fs = require("fs");
|
|
|
|
const path = require("path");
|
2023-11-28 17:49:08 +08:00
|
|
|
const {exec, spawn} = require("child_process");
|
2023-11-27 15:03:25 +08:00
|
|
|
export let frpcProcess = null;
|
|
|
|
const runningCmd = {
|
2023-11-28 17:49:08 +08:00
|
|
|
commandPath: null,
|
|
|
|
configPath: null
|
2023-11-27 15:03:25 +08:00
|
|
|
};
|
2023-12-01 15:16:16 +08:00
|
|
|
let frpcStatusListener = null;
|
2023-11-27 15:03:25 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 获取选择版本的工作目录
|
|
|
|
* @param versionId 版本ID
|
|
|
|
* @param callback
|
|
|
|
*/
|
|
|
|
const getFrpcVersionWorkerPath = (
|
2023-11-28 17:49:08 +08:00
|
|
|
versionId: string,
|
|
|
|
callback: (workerPath: string) => void
|
2023-11-27 15:03:25 +08:00
|
|
|
) => {
|
2023-11-28 17:49:08 +08:00
|
|
|
getVersionById(versionId, (err2, version) => {
|
|
|
|
if (!err2) {
|
2024-07-17 14:21:31 +08:00
|
|
|
if (version) {
|
|
|
|
callback(version["frpcVersionPath"]);
|
|
|
|
}
|
2023-11-28 17:49:08 +08:00
|
|
|
}
|
|
|
|
});
|
2023-11-27 15:03:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2024-07-17 14:21:31 +08:00
|
|
|
* 生成toml配置文件
|
|
|
|
* @param config
|
|
|
|
* @param proxys
|
2023-11-27 15:03:25 +08:00
|
|
|
*/
|
2024-07-17 14:21:31 +08:00
|
|
|
const genTomlConfig = (config: Config, proxys: Proxy[]) => {
|
|
|
|
const proxyToml = proxys.map(m => {
|
|
|
|
let toml = `
|
2023-11-27 15:03:25 +08:00
|
|
|
[[proxies]]
|
|
|
|
name = "${m.name}"
|
|
|
|
type = "${m.type}"
|
|
|
|
localIP = "${m.localIp}"
|
|
|
|
localPort = ${m.localPort}
|
|
|
|
`;
|
2024-07-17 14:21:31 +08:00
|
|
|
switch (m.type) {
|
|
|
|
case "tcp":
|
|
|
|
toml += `remotePort = ${m.remotePort}`;
|
|
|
|
break;
|
|
|
|
case "http":
|
|
|
|
case "https":
|
|
|
|
toml += `customDomains=[${m.customDomains.map(m => `"${m}"`)}]`;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2023-11-27 15:03:25 +08:00
|
|
|
|
2024-07-17 14:21:31 +08:00
|
|
|
return toml;
|
|
|
|
});
|
|
|
|
const toml = `
|
2023-11-27 15:03:25 +08:00
|
|
|
serverAddr = "${config.serverAddr}"
|
|
|
|
serverPort = ${config.serverPort}
|
|
|
|
auth.method = "${config.authMethod}"
|
|
|
|
auth.token = "${config.authToken}"
|
|
|
|
log.to = "frpc.log"
|
2023-11-28 17:49:08 +08:00
|
|
|
log.level = "${config.logLevel}"
|
|
|
|
log.maxDays = ${config.logMaxDays}
|
2023-11-27 15:03:25 +08:00
|
|
|
webServer.addr = "127.0.0.1"
|
|
|
|
webServer.port = 57400
|
2023-12-01 14:17:14 +08:00
|
|
|
transport.tls.enable = ${config.tlsConfigEnable}
|
|
|
|
${config.tlsConfigEnable ? `
|
|
|
|
transport.tls.certFile = "${config.tlsConfigCertFile}"
|
|
|
|
transport.tls.keyFile = "${config.tlsConfigKeyFile}"
|
|
|
|
transport.tls.trustedCaFile = "${config.tlsConfigTrustedCaFile}"
|
|
|
|
transport.tls.serverName = "${config.tlsConfigServerName}"
|
|
|
|
` : ""}
|
2024-01-20 11:24:59 +08:00
|
|
|
${config.proxyConfigEnable ? `
|
|
|
|
transport.proxyURL = "${config.proxyConfigProxyUrl}"
|
|
|
|
` : ""}
|
2023-12-01 14:17:14 +08:00
|
|
|
|
|
|
|
${proxyToml.join("")}
|
2023-11-27 15:03:25 +08:00
|
|
|
`;
|
2024-07-17 14:21:31 +08:00
|
|
|
return toml;
|
|
|
|
}
|
2023-11-27 15:03:25 +08:00
|
|
|
|
2024-07-17 14:21:31 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 生成ini配置
|
|
|
|
* @param config
|
|
|
|
* @param proxys
|
|
|
|
*/
|
|
|
|
const genIniConfig = (config: Config, proxys: Proxy[]) => {
|
|
|
|
const proxyIni = proxys.map(m => {
|
|
|
|
let ini = `
|
|
|
|
[${m.name}]
|
|
|
|
type = "${m.type}"
|
|
|
|
local_ip = "${m.localIp}"
|
|
|
|
local_port = ${m.localPort}
|
|
|
|
`;
|
|
|
|
switch (m.type) {
|
|
|
|
case "tcp":
|
|
|
|
ini += `remote_port = ${m.remotePort}`;
|
|
|
|
break;
|
|
|
|
case "http":
|
|
|
|
case "https":
|
|
|
|
ini += `custom_domains=[${m.customDomains.map(m => `"${m}"`)}]`;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ini;
|
|
|
|
});
|
|
|
|
const ini = `
|
|
|
|
[common]
|
|
|
|
server_addr = ${config.serverAddr}
|
|
|
|
server_port = ${config.serverPort}
|
|
|
|
authentication_method = "${config.authMethod}"
|
|
|
|
auth_token = "${config.authToken}"
|
|
|
|
log_file = "frpc.log"
|
|
|
|
log_level = ${config.logLevel}
|
|
|
|
log_max_days = ${config.logMaxDays}
|
|
|
|
admin_addr = 127.0.0.1
|
|
|
|
admin_port = 57400
|
|
|
|
tls_enable = ${config.tlsConfigEnable}
|
|
|
|
${config.tlsConfigEnable ? `
|
|
|
|
tls_cert_file = ${config.tlsConfigCertFile}
|
|
|
|
tls_key_file = ${config.tlsConfigKeyFile}
|
|
|
|
tls_trusted_ca_file = ${config.tlsConfigTrustedCaFile}
|
|
|
|
tls_server_name = ${config.tlsConfigServerName}
|
|
|
|
` : ""}
|
|
|
|
${config.proxyConfigEnable ? `
|
|
|
|
http_proxy = "${config.proxyConfigProxyUrl}"
|
|
|
|
` : ""}
|
|
|
|
|
|
|
|
${proxyIni.join("")}
|
|
|
|
`
|
|
|
|
return ini;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 生成配置文件
|
|
|
|
*/
|
|
|
|
export const generateConfig = (
|
|
|
|
config: Config,
|
|
|
|
callback: (configPath: string) => void
|
|
|
|
) => {
|
|
|
|
listProxy((err3, proxys) => {
|
|
|
|
if (!err3) {
|
|
|
|
console.log(config, 'config')
|
|
|
|
const {currentVersion} = config;
|
|
|
|
let filename = null;
|
|
|
|
let configContent = "";
|
|
|
|
if (currentVersion < 124395282) {
|
|
|
|
// 版本小于v0.52.0
|
|
|
|
filename = "frp.ini";
|
|
|
|
configContent = genIniConfig(config, proxys)
|
|
|
|
} else {
|
|
|
|
filename = "frp.toml";
|
|
|
|
configContent = genTomlConfig(config, proxys)
|
|
|
|
}
|
2023-12-01 14:17:14 +08:00
|
|
|
const configPath = path.join(app.getPath("userData"), filename)
|
|
|
|
console.debug("生成配置成功", configPath)
|
2023-11-28 17:49:08 +08:00
|
|
|
fs.writeFile(
|
2023-12-01 14:17:14 +08:00
|
|
|
configPath, // 配置文件目录
|
2024-07-17 14:21:31 +08:00
|
|
|
configContent, // 配置文件内容
|
2023-11-28 17:49:08 +08:00
|
|
|
{flag: "w"},
|
|
|
|
err => {
|
|
|
|
if (!err) {
|
|
|
|
callback(filename);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
2023-11-27 15:03:25 +08:00
|
|
|
}
|
2023-11-28 17:49:08 +08:00
|
|
|
});
|
2023-11-27 15:03:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 启动frpc子进程
|
|
|
|
* @param cwd
|
|
|
|
* @param commandPath
|
|
|
|
* @param configPath
|
|
|
|
*/
|
|
|
|
const startFrpcProcess = (commandPath: string, configPath: string) => {
|
2024-07-17 14:21:31 +08:00
|
|
|
console.log(commandPath, 'commandP')
|
2023-11-28 17:49:08 +08:00
|
|
|
const command = `${commandPath} -c ${configPath}`;
|
2023-12-01 14:17:14 +08:00
|
|
|
console.info("启动", command)
|
2023-11-28 17:49:08 +08:00
|
|
|
frpcProcess = spawn(command, {
|
|
|
|
cwd: app.getPath("userData"),
|
|
|
|
shell: true
|
|
|
|
});
|
|
|
|
runningCmd.commandPath = commandPath;
|
|
|
|
runningCmd.configPath = configPath;
|
|
|
|
frpcProcess.stdout.on("data", data => {
|
2023-12-01 14:17:14 +08:00
|
|
|
console.debug(`命令输出: ${data}`);
|
2023-11-28 17:49:08 +08:00
|
|
|
});
|
|
|
|
frpcProcess.stdout.on("error", data => {
|
2023-12-01 14:17:14 +08:00
|
|
|
console.log("启动错误", data)
|
2024-07-17 14:21:31 +08:00
|
|
|
stopFrpcProcess(() => {
|
|
|
|
})
|
2023-11-28 17:49:08 +08:00
|
|
|
});
|
2023-12-01 15:16:16 +08:00
|
|
|
frpcStatusListener = setInterval(() => {
|
|
|
|
const status = frpcProcessStatus()
|
|
|
|
if (!status) {
|
|
|
|
console.log("连接已断开")
|
|
|
|
new Notification({
|
|
|
|
title: "Frpc Desktop",
|
|
|
|
body: "连接已断开,请前往日志查看原因"
|
|
|
|
}).show()
|
|
|
|
clearInterval(frpcStatusListener)
|
|
|
|
}
|
|
|
|
}, 3000)
|
2023-11-27 15:03:25 +08:00
|
|
|
};
|
|
|
|
|
2023-12-01 15:16:16 +08:00
|
|
|
/**
|
|
|
|
* 重载frpc配置
|
|
|
|
*/
|
2023-11-27 15:03:25 +08:00
|
|
|
export const reloadFrpcProcess = () => {
|
2023-11-28 17:49:08 +08:00
|
|
|
if (frpcProcess && !frpcProcess.killed) {
|
|
|
|
getConfig((err1, config) => {
|
|
|
|
if (!err1) {
|
|
|
|
if (config) {
|
|
|
|
generateConfig(config, configPath => {
|
|
|
|
const command = `${runningCmd.commandPath} reload -c ${configPath}`;
|
2023-12-01 14:17:14 +08:00
|
|
|
console.info("重启", command);
|
2023-11-28 17:49:08 +08:00
|
|
|
exec(command, {
|
|
|
|
cwd: app.getPath("userData"),
|
|
|
|
shell: true
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2023-11-27 15:03:25 +08:00
|
|
|
};
|
|
|
|
|
2023-12-01 15:16:16 +08:00
|
|
|
/**
|
|
|
|
* 停止frpc子进程
|
|
|
|
*/
|
2024-01-20 11:24:59 +08:00
|
|
|
export const stopFrpcProcess = (callback?: () => void) => {
|
2023-12-01 14:17:14 +08:00
|
|
|
if (frpcProcess) {
|
|
|
|
treeKill(frpcProcess.pid, (error: Error) => {
|
|
|
|
if (error) {
|
|
|
|
console.log("关闭失败", frpcProcess.pid, error)
|
|
|
|
} else {
|
2023-12-01 16:55:29 +08:00
|
|
|
console.log('关闭成功')
|
2023-12-01 14:17:14 +08:00
|
|
|
frpcProcess = null
|
2023-12-01 15:16:16 +08:00
|
|
|
clearInterval(frpcStatusListener)
|
2023-12-01 14:17:14 +08:00
|
|
|
}
|
2023-12-01 16:55:29 +08:00
|
|
|
callback()
|
2023-12-01 14:17:14 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-01 15:16:16 +08:00
|
|
|
/**
|
|
|
|
* 获取frpc子进程状态
|
|
|
|
*/
|
|
|
|
export const frpcProcessStatus = () => {
|
|
|
|
if (!frpcProcess) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
// 发送信号给进程,如果进程存在,会正常返回
|
|
|
|
process.kill(frpcProcess.pid, 0);
|
|
|
|
return true;
|
|
|
|
} catch (error) {
|
|
|
|
// 进程不存在,抛出异常
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-17 14:21:31 +08:00
|
|
|
/**
|
|
|
|
* 启动frpc流程
|
|
|
|
* @param config
|
|
|
|
*/
|
|
|
|
export const startFrpWorkerProcess = (config: Config) => {
|
|
|
|
getFrpcVersionWorkerPath(
|
|
|
|
config.currentVersion,
|
|
|
|
(frpcVersionPath: string) => {
|
|
|
|
console.log(1, '1')
|
|
|
|
if (frpcVersionPath) {
|
|
|
|
generateConfig(config, configPath => {
|
|
|
|
const platform = process.platform;
|
|
|
|
if (platform === 'win32') {
|
|
|
|
startFrpcProcess(
|
|
|
|
path.join(frpcVersionPath, "frpc.exe"),
|
|
|
|
configPath
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
startFrpcProcess(
|
|
|
|
path.join(frpcVersionPath, "frpc"),
|
|
|
|
configPath
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-12-01 15:16:16 +08:00
|
|
|
|
2023-11-27 15:03:25 +08:00
|
|
|
export const initFrpcApi = () => {
|
2023-11-28 17:49:08 +08:00
|
|
|
ipcMain.handle("frpc.running", async (event, args) => {
|
2023-12-01 15:16:16 +08:00
|
|
|
return frpcProcessStatus()
|
2023-11-28 17:49:08 +08:00
|
|
|
});
|
2023-11-27 15:03:25 +08:00
|
|
|
|
2023-11-28 17:49:08 +08:00
|
|
|
ipcMain.on("frpc.start", async (event, args) => {
|
|
|
|
getConfig((err1, config) => {
|
|
|
|
if (!err1) {
|
|
|
|
if (config) {
|
2024-07-17 14:21:31 +08:00
|
|
|
startFrpWorkerProcess(config)
|
2023-11-28 17:49:08 +08:00
|
|
|
} else {
|
|
|
|
event.reply(
|
2024-07-17 14:21:31 +08:00
|
|
|
"Home.frpc.start.error.hook", "请先前往设置页面,修改配置后再启动"
|
2023-11-28 17:49:08 +08:00
|
|
|
);
|
2023-11-27 17:54:09 +08:00
|
|
|
}
|
2023-11-27 15:03:25 +08:00
|
|
|
}
|
2023-11-28 17:49:08 +08:00
|
|
|
});
|
2024-07-17 14:21:31 +08:00
|
|
|
|
2023-11-27 15:03:25 +08:00
|
|
|
});
|
|
|
|
|
2023-11-28 17:49:08 +08:00
|
|
|
ipcMain.on("frpc.stop", () => {
|
|
|
|
if (frpcProcess && !frpcProcess.killed) {
|
2024-07-17 14:21:31 +08:00
|
|
|
stopFrpcProcess(() => {
|
|
|
|
})
|
2023-11-28 17:49:08 +08:00
|
|
|
}
|
|
|
|
});
|
2023-11-27 15:03:25 +08:00
|
|
|
};
|