From 1d9ac090f381d27a438c81ea018759c6e1467a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=98=89=E4=BC=9F?= <8473136@qq.com> Date: Sat, 17 Aug 2024 00:20:47 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=94=AF=E6=8C=81toml=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E7=9A=84stcp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/api/frpc.ts | 513 ++++++++++++++++++++---------------- electron/storage/version.ts | 2 +- src/views/proxy/index.vue | 12 +- types/global.d.ts | 2 +- 4 files changed, 295 insertions(+), 234 deletions(-) diff --git a/electron/api/frpc.ts b/electron/api/frpc.ts index 99eae16..4a1eab4 100644 --- a/electron/api/frpc.ts +++ b/electron/api/frpc.ts @@ -1,17 +1,17 @@ -import {app, ipcMain, Notification} from "electron"; -import {Config, getConfig} from "../storage/config"; -import {listProxy, Proxy} from "../storage/proxy"; -import {getVersionById} from "../storage/version"; +import { app, ipcMain, Notification } from "electron"; +import { getConfig } from "../storage/config"; +import { listProxy } from "../storage/proxy"; +import { getVersionById } from "../storage/version"; import treeKill from "tree-kill"; const fs = require("fs"); const path = require("path"); -const {exec, spawn} = require("child_process"); -const log = require('electron-log'); +const { exec, spawn } = require("child_process"); +const log = require("electron-log"); export let frpcProcess = null; const runningCmd = { - commandPath: null, - configPath: null + commandPath: null, + configPath: null }; let frpcStatusListener = null; @@ -21,16 +21,16 @@ let frpcStatusListener = null; * @param callback */ const getFrpcVersionWorkerPath = ( - versionId: string, - callback: (workerPath: string) => void + versionId: number, + callback: (workerPath: string) => void ) => { - getVersionById(versionId, (err2, version) => { - if (!err2) { - if (version) { - callback(version["frpcVersionPath"]); - } - } - }); + getVersionById(versionId, (err2, version) => { + if (!err2) { + if (version) { + callback(version["frpcVersionPath"]); + } + } + }); }; /** @@ -38,47 +38,86 @@ const getFrpcVersionWorkerPath = ( * @param config * @param proxys */ -const genTomlConfig = (config: Config, proxys: Proxy[]) => { - const proxyToml = proxys.map(m => { - let toml = ` -[[proxies]] +const genTomlConfig = (config: FrpConfig, proxys: Proxy[]) => { + const proxyToml = proxys.map(m => { + let toml = ` +[[${m.type === 'stcp' && m.stcpModel === 'visitors' ? 'visitors' : 'proxies'}]] name = "${m.name}" type = "${m.type}" +`; + switch (m.type) { + case "tcp": + case "udp": + toml += ` localIP = "${m.localIp}" localPort = ${m.localPort} +remotePort = ${m.remotePort} `; - switch (m.type) { - case "tcp": - case "udp": - toml += `remotePort = ${m.remotePort}`; - break; - case "http": - case "https": - toml += `customDomains=[${m.customDomains.map(m => `"${m}"`)}]`; - break; - default: - break; + break; + case "http": + case "https": + toml += ` +localIP = "${m.localIp}" +localPort = ${m.localPort} +customDomains=[${m.customDomains.map(m => `"${m}"`)}] +`; + break; + case "stcp": + if (m.stcpModel === "visitors") { + // 访问者 + toml += ` +serverName = "${m.serverName}" +bindAddr = "${m.bindAddr}" +bindPort = ${m.bindPort} +`; + } else if (m.stcpModel === "visited") { + // 被访问者 + toml += ` +localIP = "${m.localIp}" +localPort = ${m.localPort}`; } + toml += ` +secretKey="${m.secretKey}" +`; + default: + break; + } - return toml; - }); - const toml = ` + return toml; + }); + const toml = ` serverAddr = "${config.serverAddr}" serverPort = ${config.serverPort} -${config.authMethod === 'token' ? ` +${ + config.authMethod === "token" + ? ` auth.method = "token" auth.token = "${config.authToken}" -` : ""} -${config.authMethod === 'multiuser' ? ` +` + : "" +} +${ + config.authMethod === "multiuser" + ? ` user = "${config.user}" metadatas.token = "${config.metaToken}" -` : ""} -${config.transportHeartbeatInterval ? ` +` + : "" +} +${ + config.transportHeartbeatInterval + ? ` transport.heartbeatInterval = ${config.transportHeartbeatInterval} -` : ""} -${config.transportHeartbeatTimeout ? ` +` + : "" +} +${ + config.transportHeartbeatTimeout + ? ` transport.heartbeatTimeout = ${config.transportHeartbeatTimeout} -` : ""} +` + : "" +} log.to = "frpc.log" @@ -87,69 +126,92 @@ log.maxDays = ${config.logMaxDays} webServer.addr = "127.0.0.1" webServer.port = 57400 transport.tls.enable = ${config.tlsConfigEnable} -${config.tlsConfigEnable ? ` +${ + config.tlsConfigEnable + ? ` transport.tls.certFile = "${config.tlsConfigCertFile}" transport.tls.keyFile = "${config.tlsConfigKeyFile}" transport.tls.trustedCaFile = "${config.tlsConfigTrustedCaFile}" transport.tls.serverName = "${config.tlsConfigServerName}" -` : ""} -${config.proxyConfigEnable ? ` +` + : "" +} +${ + config.proxyConfigEnable + ? ` transport.proxyURL = "${config.proxyConfigProxyUrl}" -` : ""} +` + : "" +} ${proxyToml.join("")} `; - return toml; -} - + return toml; +}; /** * 生成ini配置 * @param config * @param proxys */ -const genIniConfig = (config: Config, proxys: Proxy[]) => { - const proxyIni = proxys.map(m => { - let ini = ` +const genIniConfig = (config: FrpConfig, 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": - case "udp": - ini += `remote_port = ${m.remotePort}`; - break; - case "http": - case "https": - ini += `custom_domains=[${m.customDomains.map(m => `"${m}"`)}]`; - break; - default: - break; - } + switch (m.type) { + case "tcp": + case "udp": + 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 = ` + return ini; + }); + const ini = ` [common] server_addr = ${config.serverAddr} server_port = ${config.serverPort} -${config.authMethod === 'token' ? ` +${ + config.authMethod === "token" + ? ` authentication_method = ${config.authMethod} token = ${config.authToken} -` : ""} -${config.authMethod === 'multiuser' ? ` +` + : "" +} +${ + config.authMethod === "multiuser" + ? ` user = ${config.user} meta_token = ${config.metaToken} -` : ""} +` + : "" +} -${config.transportHeartbeatInterval ? ` +${ + config.transportHeartbeatInterval + ? ` heartbeat_interval = ${config.transportHeartbeatInterval} -` : ""} -${config.transportHeartbeatTimeout ? ` +` + : "" +} +${ + config.transportHeartbeatTimeout + ? ` heartbeat_timeout = ${config.transportHeartbeatTimeout} -` : ""} +` + : "" +} log_file = "frpc.log" log_level = ${config.logLevel} @@ -157,55 +219,63 @@ log_max_days = ${config.logMaxDays} admin_addr = 127.0.0.1 admin_port = 57400 tls_enable = ${config.tlsConfigEnable} -${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 ? ` +` + : "" +} +${ + config.proxyConfigEnable + ? ` http_proxy = "${config.proxyConfigProxyUrl}" -` : ""} +` + : "" +} ${proxyIni.join("")} - ` - return ini; -} + `; + return ini; +}; /** * 生成配置文件 */ export const generateConfig = ( - config: Config, - callback: (configPath: string) => void + config: FrpConfig, + callback: (configPath: string) => void ) => { - listProxy((err3, proxys) => { - if (!err3) { - 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) - } - const configPath = path.join(app.getPath("userData"), filename) - log.info(`生成配置成功 配置路径:${configPath}`) - fs.writeFile( - configPath, // 配置文件目录 - configContent, // 配置文件内容 - {flag: "w"}, - err => { - if (!err) { - callback(filename); - } - } - ); + listProxy((err3, proxys) => { + if (!err3) { + 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); + } + const configPath = path.join(app.getPath("userData"), filename); + log.info(`生成配置成功 配置路径:${configPath}`); + fs.writeFile( + configPath, // 配置文件目录 + configContent, // 配置文件内容 + { flag: "w" }, + err => { + if (!err) { + callback(filename); + } } - }); + ); + } + }); }; /** @@ -215,149 +285,136 @@ export const generateConfig = ( * @param configPath */ const startFrpcProcess = (commandPath: string, configPath: string) => { - log.info(`启动frpc 目录:${app.getPath("userData")} 命令:${commandPath}`,) - const command = `${commandPath} -c ${configPath}`; - frpcProcess = spawn(command, { - cwd: app.getPath("userData"), - shell: true - }); - runningCmd.commandPath = commandPath; - runningCmd.configPath = configPath; - frpcProcess.stdout.on("data", data => { - log.debug(`启动输出:${data}`) - }); - frpcProcess.stdout.on("error", data => { - log.error(`启动错误:${data}`) - stopFrpcProcess(() => { - }) - }); - frpcStatusListener = setInterval(() => { - const status = frpcProcessStatus() - log.debug(`监听frpc子进程状态:${status}`) - if (!status) { - new Notification({ - title: "Frpc Desktop", - body: "连接已断开,请前往日志查看原因" - }).show() - clearInterval(frpcStatusListener) - } - }, 3000) + log.info(`启动frpc 目录:${app.getPath("userData")} 命令:${commandPath}`); + const command = `${commandPath} -c ${configPath}`; + frpcProcess = spawn(command, { + cwd: app.getPath("userData"), + shell: true + }); + runningCmd.commandPath = commandPath; + runningCmd.configPath = configPath; + frpcProcess.stdout.on("data", data => { + log.debug(`启动输出:${data}`); + }); + frpcProcess.stdout.on("error", data => { + log.error(`启动错误:${data}`); + stopFrpcProcess(() => {}); + }); + frpcStatusListener = setInterval(() => { + const status = frpcProcessStatus(); + log.debug(`监听frpc子进程状态:${status}`); + if (!status) { + new Notification({ + title: "Frpc Desktop", + body: "连接已断开,请前往日志查看原因" + }).show(); + clearInterval(frpcStatusListener); + } + }, 3000); }; /** * 重载frpc配置 */ export const reloadFrpcProcess = () => { - if (frpcProcess && !frpcProcess.killed) { - getConfig((err1, config) => { - if (!err1) { - if (config) { - generateConfig(config, configPath => { - const command = `${runningCmd.commandPath} reload -c ${configPath}`; - log.info(`重载配置:${command}`) - exec(command, { - cwd: app.getPath("userData"), - shell: true - }); - }); - } - } - }); - } + if (frpcProcess && !frpcProcess.killed) { + getConfig((err1, config) => { + if (!err1) { + if (config) { + generateConfig(config, configPath => { + const command = `${runningCmd.commandPath} reload -c ${configPath}`; + log.info(`重载配置:${command}`); + exec(command, { + cwd: app.getPath("userData"), + shell: true + }); + }); + } + } + }); + } }; /** * 停止frpc子进程 */ export const stopFrpcProcess = (callback?: () => void) => { - if (frpcProcess) { - treeKill(frpcProcess.pid, (error: Error) => { - if (error) { - log.error(`关闭frpc子进程失败 pid:${frpcProcess.pid} error:${error}`) - callback() - } else { - log.info(`关闭frpc子进程成功`) - frpcProcess = null - clearInterval(frpcStatusListener) - callback() - } - }) - } else { - callback() - } -} + if (frpcProcess) { + treeKill(frpcProcess.pid, (error: Error) => { + if (error) { + log.error(`关闭frpc子进程失败 pid:${frpcProcess.pid} error:${error}`); + callback(); + } else { + log.info(`关闭frpc子进程成功`); + frpcProcess = null; + clearInterval(frpcStatusListener); + callback(); + } + }); + } else { + callback(); + } +}; /** * 获取frpc子进程状态 */ export const frpcProcessStatus = () => { - if (!frpcProcess) { - return false; - } - try { - // 发送信号给进程,如果进程存在,会正常返回 - process.kill(frpcProcess.pid, 0); - return true; - } catch (error) { - // 进程不存在,抛出异常 - return false; - } -} + if (!frpcProcess) { + return false; + } + try { + // 发送信号给进程,如果进程存在,会正常返回 + process.kill(frpcProcess.pid, 0); + return true; + } catch (error) { + // 进程不存在,抛出异常 + return false; + } +}; /** * 启动frpc流程 * @param config */ -export const startFrpWorkerProcess = async (config: Config) => { - getFrpcVersionWorkerPath( - config.currentVersion, - (frpcVersionPath: string) => { - 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 - ); - } - }); - } +export const startFrpWorkerProcess = async (config: FrpConfig) => { + getFrpcVersionWorkerPath(config.currentVersion, (frpcVersionPath: string) => { + 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); } - ); -} - + }); + } + }); +}; export const initFrpcApi = () => { - ipcMain.handle("frpc.running", async (event, args) => { - return frpcProcessStatus() - }); + ipcMain.handle("frpc.running", async (event, args) => { + return frpcProcessStatus(); + }); - ipcMain.on("frpc.start", async (event, args) => { - getConfig((err1, config) => { - if (!err1) { - if (config) { - startFrpWorkerProcess(config) - } else { - event.reply( - "Home.frpc.start.error.hook", "请先前往设置页面,修改配置后再启动" - ); - } - } - }); - - }); - - ipcMain.on("frpc.stop", () => { - if (frpcProcess && !frpcProcess.killed) { - stopFrpcProcess(() => { - - }) + ipcMain.on("frpc.start", async (event, args) => { + getConfig((err1, config) => { + if (!err1) { + if (config) { + startFrpWorkerProcess(config); + } else { + event.reply( + "Home.frpc.start.error.hook", + "请先前往设置页面,修改配置后再启动" + ); } + } }); + }); + + ipcMain.on("frpc.stop", () => { + if (frpcProcess && !frpcProcess.killed) { + stopFrpcProcess(() => {}); + } + }); }; diff --git a/electron/storage/version.ts b/electron/storage/version.ts index e700f3b..d165fb7 100644 --- a/electron/storage/version.ts +++ b/electron/storage/version.ts @@ -32,7 +32,7 @@ export const listVersion = ( }; export const getVersionById = ( - id: string, + id: number, callback: (err: Error | null, document: FrpVersion) => void ) => { versionDB.findOne({id: id}, callback); diff --git a/src/views/proxy/index.vue b/src/views/proxy/index.vue index 399645e..8e14599 100644 --- a/src/views/proxy/index.vue +++ b/src/views/proxy/index.vue @@ -43,7 +43,7 @@ const defaultForm = ref({ localPort: 8080, remotePort: 8080, customDomains: [""], - stcpModel: "visitor", + stcpModel: "visitors", serverName: "", secretKey: "", bindAddr: "", @@ -63,7 +63,7 @@ const proxyTypes = ref(["http", "https", "tcp", "udp", "stcp"]); const stcpModels = ref([ { label: "访问者", - value: "visitor" + value: "visitors" }, { label: "被访问者", @@ -469,7 +469,7 @@ onUnmounted(() => { - + {