frpc-desktop/electron/api/frpc.ts

717 lines
18 KiB
TypeScript
Raw Normal View History

import { app, ipcMain, Notification } from "electron";
import { getConfig } from "../storage/config";
import { listProxy } from "../storage/proxy";
import { getVersionById } from "../storage/version";
2023-12-01 14:17:14 +08:00
import treeKill from "tree-kill";
2025-01-08 12:01:00 +08:00
import { logInfo, logError, LogModule, logDebug, logWarn } from "../utils/log";
2023-11-27 15:03:25 +08:00
const fs = require("fs");
const path = require("path");
const { exec, spawn } = require("child_process");
2025-01-08 12:01:00 +08:00
2023-11-27 15:03:25 +08:00
export let frpcProcess = null;
const runningCmd = {
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
const getFrpcVersionWorkerPath = (
versionId: number,
callback: (workerPath: string) => void
2023-11-27 15:03:25 +08:00
) => {
getVersionById(versionId, (err2, version) => {
if (!err2) {
if (version) {
callback(version["frpcVersionPath"]);
}
}
});
2023-11-27 15:03:25 +08:00
};
2024-09-07 17:24:51 +08:00
const isRangePort = (m: Proxy) => {
return (
(m.type === "tcp" || m.type === "udp") &&
(String(m.localPort).indexOf("-") !== -1 ||
String(m.localPort).indexOf(",") !== -1)
2024-09-07 17:24:51 +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-08-21 22:31:12 +08:00
export const genTomlConfig = (config: FrpConfig, proxys: Proxy[]) => {
const proxyToml = proxys.map(m => {
2024-09-07 17:24:51 +08:00
const rangePort = isRangePort(m);
config.tlsConfigKeyFile = config.tlsConfigKeyFile.replace(/\\/g, "\\\\");
config.tlsConfigCertFile = config.tlsConfigCertFile.replace(/\\/g, "\\\\");
config.tlsConfigTrustedCaFile = config.tlsConfigTrustedCaFile.replace(
/\\/g,
"\\\\"
);
2025-01-06 16:14:52 +08:00
let toml = `${
2025-01-08 12:01:00 +08:00
rangePort
? `{{- range $_, $v := parseNumberRangePair "${m.localPort}" "${m.remotePort}" }}`
: ""
}
[[${
2024-12-02 14:53:21 +08:00
(m.type === "stcp" || m.type === "xtcp" || m.type === "sudp") &&
m.stcpModel === "visitors"
? "visitors"
: "proxies"
}]]
2025-01-06 16:14:52 +08:00
${rangePort ? "" : `name = "${m.name}"`}
2023-11-27 15:03:25 +08:00
type = "${m.type}"
`;
2024-09-07 17:01:49 +08:00
switch (m.type) {
case "tcp":
case "udp":
2024-09-07 17:01:49 +08:00
if (rangePort) {
2025-01-06 16:14:52 +08:00
toml += `name = "${m.name}-{{ $v.First }}"
2024-09-07 17:01:49 +08:00
localPort = {{ $v.First }}
2025-01-06 16:14:52 +08:00
remotePort = {{ $v.Second }}`;
2024-09-07 17:01:49 +08:00
} else {
2025-01-06 16:14:52 +08:00
toml += `localIP = "${m.localIp}"
2024-09-07 17:01:49 +08:00
localPort = ${m.localPort}
2025-01-06 16:14:52 +08:00
remotePort = ${m.remotePort}`;
2024-09-07 17:01:49 +08:00
}
break;
case "http":
case "https":
2024-12-26 18:00:59 +08:00
const customDomains = m.customDomains.filter(f1 => f1 !== "");
if (customDomains && customDomains.length > 0) {
2025-01-06 16:14:52 +08:00
toml += `customDomains=[${m.customDomains.map(m => `"${m}"`)}]`;
2024-12-26 18:00:59 +08:00
}
if (m.subdomain) {
2025-01-06 16:14:52 +08:00
toml += `subdomain="${m.subdomain}"`;
2024-12-26 18:00:59 +08:00
}
if (m.basicAuth) {
2025-01-06 16:14:52 +08:00
toml += `httpUser = "${m.httpUser}"
httpPassword = "${m.httpPassword}"`;
}
2024-12-26 18:00:59 +08:00
if (m.https2http) {
2025-01-06 16:14:52 +08:00
toml += `[proxies.plugin]
2024-12-26 18:00:59 +08:00
type = "https2http"
localAddr = "${m.localIp}:${m.localPort}"
crtPath = "${m.https2httpCaFile}"
2025-01-06 16:14:52 +08:00
keyPath = "${m.https2httpKeyFile}"`;
2024-12-26 18:00:59 +08:00
} else {
2025-01-06 16:14:52 +08:00
toml += `localIP = "${m.localIp}"
localPort = ${m.localPort}`;
2024-12-26 18:00:59 +08:00
}
break;
case "stcp":
2024-10-14 11:53:19 +08:00
case "xtcp":
2024-12-02 14:53:21 +08:00
case "sudp":
if (m.stcpModel === "visitors") {
// 访问者
2025-01-06 16:14:52 +08:00
toml += `serverName = "${m.serverName}"
bindAddr = "${m.bindAddr}"
2025-01-06 16:14:52 +08:00
bindPort = ${m.bindPort}`;
if (m.fallbackTo) {
2025-01-06 16:14:52 +08:00
toml += `fallbackTo = "${m.fallbackTo}"
fallbackTimeoutMs = ${m.fallbackTimeoutMs || 500}`;
}
} else if (m.stcpModel === "visited") {
// 被访问者
2025-01-06 16:14:52 +08:00
toml += `localIP = "${m.localIp}"
localPort = ${m.localPort}`;
2024-07-17 14:21:31 +08:00
}
2025-01-06 16:14:52 +08:00
toml += `secretKey="${m.secretKey}"
`;
2024-08-17 00:43:23 +08:00
break;
default:
break;
}
2023-11-27 15:03:25 +08:00
2024-09-07 17:01:49 +08:00
if (rangePort) {
toml += `{{- end }}`;
}
return toml;
});
2025-01-06 16:14:52 +08:00
const toml = `serverAddr = "${config.serverAddr}"
2023-11-27 15:03:25 +08:00
serverPort = ${config.serverPort}
${
config.authMethod === "token"
2025-01-06 16:14:52 +08:00
? `auth.method = "token"
auth.token = "${config.authToken}"`
: ""
}
${
config.authMethod === "multiuser"
2025-01-06 16:14:52 +08:00
? `user = "${config.user}"
metadatas.token = "${config.metaToken}"`
: ""
}
log.to = "frpc.log"
log.level = "${config.logLevel}"
log.maxDays = ${config.logMaxDays}
webServer.addr = "127.0.0.1"
webServer.port = ${config.webPort}
${
config.transportProtocol
? `transport.protocol = "${config.transportProtocol}"`
: ""
}
${
config.transportPoolCount
? `transport.poolCount = ${config.transportPoolCount}`
: ""
}
${
config.transportDialServerTimeout
? `transport.dialServerTimeout = ${config.transportDialServerTimeout}`
: ""
}
${
config.transportDialServerKeepalive
? `transport.dialServerKeepalive = ${config.transportDialServerKeepalive}`
: ""
}
${
config.transportHeartbeatInterval
2025-01-06 16:14:52 +08:00
? `transport.heartbeatInterval = ${config.transportHeartbeatInterval}`
: ""
}
${
config.transportHeartbeatTimeout
2025-01-06 16:14:52 +08:00
? `transport.heartbeatTimeout = ${config.transportHeartbeatTimeout}`
: ""
}
${config.transportTcpMux ? `transport.tcpMux = ${config.transportTcpMux}` : ""}
${
config.transportTcpMux && config.transportTcpMuxKeepaliveInterval
? `transport.tcpMuxKeepaliveInterval = ${config.transportTcpMuxKeepaliveInterval}`
: ""
}
2023-12-01 14:17:14 +08:00
transport.tls.enable = ${config.tlsConfigEnable}
${
2024-09-04 13:08:16 +08:00
config.tlsConfigEnable && config.tlsConfigCertFile
2025-01-06 16:14:52 +08:00
? `transport.tls.certFile = "${config.tlsConfigCertFile}"`
2024-09-04 13:08:16 +08:00
: ""
}
2025-01-06 16:14:52 +08:00
${
2025-01-08 12:01:00 +08:00
config.tlsConfigEnable && config.tlsConfigKeyFile
? `transport.tls.keyFile = "${config.tlsConfigKeyFile}"`
: ""
2025-01-06 16:14:52 +08:00
}
2024-09-04 13:08:16 +08:00
${
config.tlsConfigEnable && config.tlsConfigTrustedCaFile
2025-01-06 16:14:52 +08:00
? `transport.tls.trustedCaFile = "${config.tlsConfigTrustedCaFile}"`
2024-09-04 13:08:16 +08:00
: ""
}
${
config.tlsConfigEnable && config.tlsConfigServerName
2025-01-06 16:14:52 +08:00
? `transport.tls.serverName = "${config.tlsConfigServerName}"`
2024-09-04 13:08:16 +08:00
: ""
}
${
config.proxyConfigEnable
2025-01-06 16:14:52 +08:00
? `transport.proxyURL = "${config.proxyConfigProxyUrl}"`
: ""
}
2025-01-06 16:14:52 +08:00
${proxyToml.join("")}`;
return toml;
};
2024-07-17 14:21:31 +08:00
/**
* ini配置
* @param config
* @param proxys
*/
2024-08-21 22:31:12 +08:00
export const genIniConfig = (config: FrpConfig, proxys: Proxy[]) => {
const proxyIni = proxys.map(m => {
2024-09-07 17:24:51 +08:00
const rangePort = isRangePort(m);
2025-01-06 16:14:52 +08:00
let ini = `[${rangePort ? "range:" : ""}${m.name}]
2024-07-17 14:21:31 +08:00
type = "${m.type}"
`;
switch (m.type) {
case "tcp":
case "udp":
2024-08-17 00:43:23 +08:00
ini += `
local_ip = "${m.localIp}"
local_port = ${m.localPort}
remote_port = ${m.remotePort}
`;
break;
case "http":
2025-01-05 21:17:56 +08:00
ini += `
local_ip = "${m.localIp}"
local_port = ${m.localPort}
custom_domains=[${m.customDomains.map(m => `${m}`)}]
subdomain="${m.subdomain}"
`;
if (m.basicAuth) {
ini += `
httpUser = "${m.httpUser}"
httpPassword = "${m.httpPassword}"
`;
}
break;
case "https":
2024-08-17 00:43:23 +08:00
ini += `
2024-12-20 19:03:11 +08:00
custom_domains=[${m.customDomains.map(m => `${m}`)}]
subdomain="${m.subdomain}"
2025-01-05 21:17:56 +08:00
`;
if (m.basicAuth) {
ini += `
httpUser = "${m.httpUser}"
httpPassword = "${m.httpPassword}"
2025-01-05 21:17:56 +08:00
`;
}
if (m.https2http) {
ini += `
plugin = https2http
plugin_local_addr = ${m.localIp}:${m.localPort}
plugin_crt_path = ${m.https2httpCaFile}
plugin_key_path = ${m.https2httpKeyFile}
`;
} else {
ini += `
local_ip = "${m.localIp}"
local_port = ${m.localPort}
`;
}
2024-08-17 00:43:23 +08:00
break;
case "stcp":
2024-10-14 11:53:19 +08:00
case "xtcp":
2024-12-02 14:53:21 +08:00
case "sudp":
2024-08-17 00:43:23 +08:00
if (m.stcpModel === "visitors") {
// 访问者
ini += `
role = visitor
server_name = "${m.serverName}"
bind_addr = "${m.bindAddr}"
bind_port = ${m.bindPort}
`;
if (m.fallbackTo) {
ini += `
fallback_to = ${m.fallbackTo}
fallback_timeout_ms = ${m.fallbackTimeoutMs || 500}
`;
}
2024-08-17 00:43:23 +08:00
} else if (m.stcpModel === "visited") {
// 被访问者
ini += `
local_ip = "${m.localIp}"
local_port = ${m.localPort}`;
}
ini += `
sk="${m.secretKey}"
`;
break;
default:
break;
}
2024-07-17 14:21:31 +08:00
return ini;
});
const ini = `
2024-07-17 14:21:31 +08:00
[common]
server_addr = ${config.serverAddr}
server_port = ${config.serverPort}
${
config.authMethod === "token"
? `
2024-08-05 22:31:32 +08:00
authentication_method = ${config.authMethod}
token = ${config.authToken}
`
: ""
}
${
config.authMethod === "multiuser"
? `
2024-08-05 22:31:32 +08:00
user = ${config.user}
meta_token = ${config.metaToken}
`
: ""
}
${
config.transportHeartbeatInterval
? `
heartbeat_interval = ${config.transportHeartbeatInterval}
`
: ""
}
${
config.transportHeartbeatTimeout
? `
heartbeat_timeout = ${config.transportHeartbeatTimeout}
`
: ""
}
2024-07-17 14:21:31 +08:00
log_file = "frpc.log"
log_level = ${config.logLevel}
log_max_days = ${config.logMaxDays}
admin_addr = 127.0.0.1
2024-12-02 17:49:04 +08:00
admin_port = ${config.webPort}
2024-07-17 14:21:31 +08:00
tls_enable = ${config.tlsConfigEnable}
2024-09-04 13:08:16 +08:00
${
2024-09-04 13:08:16 +08:00
config.tlsConfigEnable && config.tlsConfigCertFile
? `
2024-07-17 14:21:31 +08:00
tls_cert_file = ${config.tlsConfigCertFile}
2024-09-04 13:08:16 +08:00
`
: ""
}
${
config.tlsConfigEnable && config.tlsConfigKeyFile
? `
2024-07-17 14:21:31 +08:00
tls_key_file = ${config.tlsConfigKeyFile}
2024-09-04 13:08:16 +08:00
`
: ""
}
${
config.tlsConfigEnable && config.tlsConfigTrustedCaFile
? `
2024-07-17 14:21:31 +08:00
tls_trusted_ca_file = ${config.tlsConfigTrustedCaFile}
2024-09-04 13:08:16 +08:00
`
: ""
}
${
config.tlsConfigEnable && config.tlsConfigServerName
? `
2024-07-17 14:21:31 +08:00
tls_server_name = ${config.tlsConfigServerName}
`
2024-09-04 13:08:16 +08:00
: ""
}
${
config.proxyConfigEnable
? `
2024-07-17 14:21:31 +08:00
http_proxy = "${config.proxyConfigProxyUrl}"
`
: ""
}
2024-07-17 14:21:31 +08:00
${proxyIni.join("")}
`;
return ini;
};
2024-07-17 14:21:31 +08:00
/**
*
*/
export const generateConfig = (
config: FrpConfig,
callback: (configPath: string) => void
2024-07-17 14:21:31 +08:00
) => {
listProxy((err3, proxys) => {
2025-01-08 12:01:00 +08:00
if (err3) {
logError(LogModule.FRP_CLIENT, `Failed to list proxies: ${err3.message}`);
return;
}
const { currentVersion } = config;
let filename = null;
let configContent = "";
const filtered = proxys
.map(m => {
if (m.status == null || m.status == undefined) {
m.status = true;
2023-11-27 15:03:25 +08:00
}
2025-01-08 12:01:00 +08:00
return m;
})
.filter(f => f.status);
if (currentVersion < 124395282) {
// 版本小于v0.52.0
filename = "frp.ini";
configContent = genIniConfig(config, filtered);
logInfo(
LogModule.FRP_CLIENT,
`Using INI format for configuration: ${filename}`
);
} else {
filename = "frp.toml";
configContent = genTomlConfig(config, filtered);
logInfo(
LogModule.FRP_CLIENT,
`Using TOML format for configuration: ${filename}`
);
}
2025-01-08 12:01:00 +08:00
const configPath = path.join(app.getPath("userData"), filename);
logInfo(
LogModule.FRP_CLIENT,
`Writing configuration to file: ${configPath}`
);
fs.writeFile(
configPath, // 配置文件目录
configContent, // 配置文件内容
{ flag: "w" },
err => {
if (err) {
logError(
LogModule.FRP_CLIENT,
`Failed to write configuration file: ${err.message}`
);
} else {
logInfo(
LogModule.FRP_CLIENT,
`Configuration file written successfully: ${filename}`
);
callback(filename);
}
}
);
});
2023-11-27 15:03:25 +08:00
};
/**
* frpc子进程
* @param cwd
* @param commandPath
* @param configPath
*/
const startFrpcProcess = (commandPath: string, configPath: string) => {
2025-01-08 12:01:00 +08:00
logInfo(
LogModule.FRP_CLIENT,
`Starting frpc process. Directory: ${app.getPath(
"userData"
)}, Command: ${commandPath}`
);
const command = `${commandPath} -c ${configPath}`;
frpcProcess = spawn(command, {
cwd: app.getPath("userData"),
shell: true
});
runningCmd.commandPath = commandPath;
runningCmd.configPath = configPath;
2025-01-08 12:01:00 +08:00
frpcProcess.stdout.on("data", data => {
2025-01-08 12:01:00 +08:00
logDebug(LogModule.FRP_CLIENT, `Frpc process output: ${data}`);
});
2025-01-08 12:01:00 +08:00
frpcProcess.stdout.on("error", data => {
2025-01-08 12:01:00 +08:00
logError(LogModule.FRP_CLIENT, `Frpc process error: ${data}`);
stopFrpcProcess(() => {});
});
2025-01-08 12:01:00 +08:00
frpcStatusListener = setInterval(() => {
const status = frpcProcessStatus();
2025-01-08 12:01:00 +08:00
logDebug(
LogModule.FRP_CLIENT,
`Monitoring frpc process status: ${status}, Listener ID: ${frpcStatusListener}`
);
if (!status) {
new Notification({
title: "Frpc Desktop",
2025-01-08 12:01:00 +08:00
body: "Connection lost, please check the logs for details."
}).show();
2025-01-08 12:01:00 +08:00
logError(
LogModule.FRP_CLIENT,
"Frpc process status check failed. Connection lost."
);
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 = () => {
if (frpcProcess && !frpcProcess.killed) {
2025-01-08 12:01:00 +08:00
logDebug(
LogModule.FRP_CLIENT,
"Attempting to reload frpc process configuration."
);
getConfig((err1, config) => {
if (!err1) {
if (config) {
generateConfig(config, configPath => {
const command = `${runningCmd.commandPath} reload -c ${configPath}`;
2025-01-08 12:01:00 +08:00
logInfo(
LogModule.FRP_CLIENT,
`Reloading configuration: ${command}`
);
exec(
command,
{
cwd: app.getPath("userData"),
shell: true
},
(error, stdout, stderr) => {
if (error) {
logError(
LogModule.FRP_CLIENT,
`Error reloading configuration: ${error.message}`
);
return;
}
logDebug(
LogModule.FRP_CLIENT,
`Configuration reload output: ${stdout}`
);
if (stderr) {
logWarn(
LogModule.FRP_CLIENT,
`Configuration reload warnings: ${stderr}`
);
}
}
);
});
2025-01-08 12:01:00 +08:00
} else {
logWarn(LogModule.FRP_CLIENT, "No configuration found to reload.");
}
2025-01-08 12:01:00 +08:00
} else {
logError(LogModule.FRP_CLIENT, `Error getting configuration: ${err1}`);
}
});
2025-01-08 12:01:00 +08:00
} else {
2025-01-08 12:07:42 +08:00
logDebug(
2025-01-08 12:01:00 +08:00
LogModule.FRP_CLIENT,
"frpc process is not running or has been killed."
);
}
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) => {
if (frpcProcess) {
treeKill(frpcProcess.pid, (error: Error) => {
if (error) {
2025-01-08 12:01:00 +08:00
logError(
LogModule.FRP_CLIENT,
`Failed to stop frpc process with pid: ${frpcProcess.pid}. Error: ${error.message}`
);
callback();
} else {
2025-01-08 12:01:00 +08:00
logInfo(
LogModule.FRP_CLIENT,
`Successfully stopped frpc process with pid: ${frpcProcess.pid}.`
);
frpcProcess = null;
clearInterval(frpcStatusListener);
callback();
}
});
} else {
2025-01-08 12:01:00 +08:00
logWarn(
LogModule.FRP_CLIENT,
"Attempted to stop frpc process, but no process is running."
);
logWarn(LogModule.FRP_CLIENT, "No frpc process to stop.");
callback();
}
};
2023-12-01 14:17:14 +08:00
2023-12-01 15:16:16 +08:00
/**
* frpc子进程状态
*/
export const frpcProcessStatus = () => {
if (!frpcProcess) {
2025-01-08 12:07:42 +08:00
logDebug(LogModule.FRP_CLIENT, "frpc process is not running.");
return false;
}
try {
// 发送信号给进程,如果进程存在,会正常返回
process.kill(frpcProcess.pid, 0);
2025-01-08 12:01:00 +08:00
logDebug(
LogModule.FRP_CLIENT,
`frpc process is running with pid: ${frpcProcess.pid}`
);
return true;
} catch (error) {
// 进程不存在,抛出异常
2025-01-08 12:01:00 +08:00
logError(
LogModule.FRP_CLIENT,
`frpc process not found. Error: ${error.message}`
);
return false;
}
};
2023-12-01 15:16:16 +08:00
2024-07-17 14:21:31 +08:00
/**
* frpc流程
* @param config
*/
export const startFrpWorkerProcess = async (config: FrpConfig) => {
2025-01-08 12:01:00 +08:00
logInfo(LogModule.FRP_CLIENT, "Starting frpc worker process...");
getFrpcVersionWorkerPath(config.currentVersion, (frpcVersionPath: string) => {
if (frpcVersionPath) {
2025-01-08 12:01:00 +08:00
logInfo(
LogModule.FRP_CLIENT,
`Found frpc version path: ${frpcVersionPath}`
);
generateConfig(config, configPath => {
const platform = process.platform;
if (platform === "win32") {
2025-01-08 12:01:00 +08:00
logInfo(LogModule.FRP_CLIENT, "Starting frpc on Windows.");
startFrpcProcess(path.join(frpcVersionPath, "frpc.exe"), configPath);
} else {
2025-01-08 12:01:00 +08:00
logInfo(
LogModule.FRP_CLIENT,
"Starting frpc on non-Windows platform."
);
startFrpcProcess(path.join(frpcVersionPath, "frpc"), configPath);
2024-07-17 14:21:31 +08:00
}
});
2025-01-08 12:01:00 +08:00
} else {
logError(LogModule.FRP_CLIENT, "frpc version path not found.");
}
});
};
2023-12-01 15:16:16 +08:00
2023-11-27 15:03:25 +08:00
export const initFrpcApi = () => {
ipcMain.handle("frpc.running", async (event, args) => {
2025-01-08 12:01:00 +08:00
logDebug(LogModule.FRP_CLIENT, "Checking if frpc process is running...");
return frpcProcessStatus();
});
2023-11-27 15:03:25 +08:00
ipcMain.on("frpc.start", async (event, args) => {
2025-01-08 12:01:00 +08:00
logInfo(LogModule.FRP_CLIENT, "Received request to start frpc process.");
getConfig((err1, config) => {
if (!err1) {
2024-08-22 14:31:19 +08:00
if (!config) {
2025-01-08 12:01:00 +08:00
logWarn(
LogModule.FRP_CLIENT,
"Configuration not found. Prompting user to set configuration."
);
2024-08-22 14:31:19 +08:00
event.reply(
"Home.frpc.start.error.hook",
"请先前往设置页面,修改配置后再启动"
);
return;
}
if (!config.currentVersion) {
2025-01-08 12:01:00 +08:00
logWarn(
LogModule.FRP_CLIENT,
"Current version not set in configuration. Prompting user."
);
event.reply(
"Home.frpc.start.error.hook",
"请先前往设置页面,修改配置后再启动"
);
2024-08-22 14:31:19 +08:00
return;
}
2024-08-22 14:31:19 +08:00
startFrpWorkerProcess(config);
2025-01-08 12:01:00 +08:00
} else {
logError(LogModule.FRP_CLIENT, `Error getting configuration: ${err1}`);
}
});
});
ipcMain.on("frpc.stop", () => {
2025-01-08 12:01:00 +08:00
logInfo(LogModule.FRP_CLIENT, "Received request to stop frpc process.");
if (frpcProcess && !frpcProcess.killed) {
stopFrpcProcess(() => {});
2025-01-08 12:01:00 +08:00
} else {
logWarn(LogModule.FRP_CLIENT, "No frpc process to stop.");
}
});
2023-11-27 15:03:25 +08:00
};