diff --git a/README.md b/README.md index f507742..ff52d2b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ ## 里程碑 +- 2024-07-17: 发布v1.0.3版本 修复已知bug 增加开机自启 增加删除frp版本 - 2024-01-29: 发布v1.0.2版本 增加Linux客户端和代理模式 - 2023-12-01: 发布v1.0.1版本 - 2023-11-28: 发布v1.0版本 diff --git a/electron/api/frpc.ts b/electron/api/frpc.ts index a3fe7cb..bdbf95d 100644 --- a/electron/api/frpc.ts +++ b/electron/api/frpc.ts @@ -1,6 +1,6 @@ import {app, ipcMain, Notification} from "electron"; import {Config, getConfig} from "../storage/config"; -import {listProxy} from "../storage/proxy"; +import {listProxy, Proxy} from "../storage/proxy"; import {getVersionById} from "../storage/version"; import treeKill from "tree-kill"; @@ -25,43 +25,42 @@ const getFrpcVersionWorkerPath = ( ) => { getVersionById(versionId, (err2, version) => { if (!err2) { - callback(version["frpcVersionPath"]); + if (version) { + callback(version["frpcVersionPath"]); + } } }); }; /** - * 生成配置文件 + * 生成toml配置文件 + * @param config + * @param proxys */ -export const generateConfig = ( - config: Config, - callback: (configPath: string) => void -) => { - listProxy((err3, proxys) => { - if (!err3) { - const proxyToml = proxys.map(m => { - let toml = ` +const genTomlConfig = (config: Config, proxys: Proxy[]) => { + const proxyToml = proxys.map(m => { + let toml = ` [[proxies]] name = "${m.name}" type = "${m.type}" localIP = "${m.localIp}" localPort = ${m.localPort} `; - switch (m.type) { - case "tcp": - toml += `remotePort = ${m.remotePort}`; - break; - case "http": - case "https": - toml += `customDomains=[${m.customDomains.map(m => `"${m}"`)}]`; - break; - default: - break; - } + switch (m.type) { + case "tcp": + toml += `remotePort = ${m.remotePort}`; + break; + case "http": + case "https": + toml += `customDomains=[${m.customDomains.map(m => `"${m}"`)}]`; + break; + default: + break; + } - return toml; - }); - let toml = ` + return toml; + }); + const toml = ` serverAddr = "${config.serverAddr}" serverPort = ${config.serverPort} auth.method = "${config.authMethod}" @@ -82,17 +81,92 @@ ${config.proxyConfigEnable ? ` transport.proxyURL = "${config.proxyConfigProxyUrl}" ` : ""} - ${proxyToml.join("")} `; + return toml; +} - // const configPath = path.join("frp.toml"); - const filename = "frp.toml"; + +/** + * 生成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) + } const configPath = path.join(app.getPath("userData"), filename) console.debug("生成配置成功", configPath) fs.writeFile( configPath, // 配置文件目录 - toml, // 配置文件内容 + configContent, // 配置文件内容 {flag: "w"}, err => { if (!err) { @@ -111,6 +185,7 @@ ${proxyToml.join("")} * @param configPath */ const startFrpcProcess = (commandPath: string, configPath: string) => { + console.log(commandPath, 'commandP') const command = `${commandPath} -c ${configPath}`; console.info("启动", command) frpcProcess = spawn(command, { @@ -124,7 +199,8 @@ const startFrpcProcess = (commandPath: string, configPath: string) => { }); frpcProcess.stdout.on("error", data => { console.log("启动错误", data) - stopFrpcProcess() + stopFrpcProcess(() => { + }) }); frpcStatusListener = setInterval(() => { const status = frpcProcessStatus() @@ -196,6 +272,35 @@ export const frpcProcessStatus = () => { } } +/** + * 启动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 + ); + } + }); + } + } + ); +} + export const initFrpcApi = () => { ipcMain.handle("frpc.running", async (event, args) => { @@ -206,38 +311,21 @@ export const initFrpcApi = () => { getConfig((err1, config) => { if (!err1) { if (config) { - getFrpcVersionWorkerPath( - config.currentVersion, - (frpcVersionPath: string) => { - generateConfig(config, configPath => { - const platform = process.platform; - if (platform === 'win32') { - startFrpcProcess( - path.join(frpcVersionPath, "frpc.exe"), - configPath - ); - } else { - startFrpcProcess( - path.join(frpcVersionPath, "frpc"), - configPath - ); - } - }); - } - ); + startFrpWorkerProcess(config) } else { event.reply( - "Home.frpc.start.error.hook", - "请先前往设置页面,修改配置后再启动" + "Home.frpc.start.error.hook", "请先前往设置页面,修改配置后再启动" ); } } }); + }); ipcMain.on("frpc.stop", () => { if (frpcProcess && !frpcProcess.killed) { - stopFrpcProcess() + stopFrpcProcess(() => { + }) } }); }; diff --git a/electron/api/github.ts b/electron/api/github.ts index 46e2624..46fb6fa 100644 --- a/electron/api/github.ts +++ b/electron/api/github.ts @@ -1,5 +1,5 @@ import {app, BrowserWindow, ipcMain, net, shell} from "electron"; -import {insertVersion} from "../storage/version"; +import {deleteVersionById, insertVersion} from "../storage/version"; const fs = require("fs"); const path = require("path"); @@ -86,7 +86,7 @@ export const initGitHubApi = () => { ipcMain.on("github.getFrpVersions", async event => { const request = net.request({ method: "get", - url: "https://api.github.com/repos/fatedier/frp/releases" + url: "https://api.github.com/repos/fatedier/frp/releases?page=1&per_page=1000" }); request.on("response", response => { let responseData: Buffer = Buffer.alloc(0); @@ -104,6 +104,7 @@ export const initGitHubApi = () => { const asset = getAdaptiveAsset(m.id); if (asset) { const absPath = `${downloadPath}/${asset.name}`; + m.absPath = absPath; m.download_completed = fs.existsSync(absPath); } return m; @@ -163,6 +164,23 @@ export const initGitHubApi = () => { }); }); + /** + * 删除下载 + */ + ipcMain.on("github.deleteVersion", async (event, args) => { + const {absPath, id} = args; + console.log('删除下载', args) + if (fs.existsSync(absPath)) { + deleteVersionById(id, () => { + fs.unlinkSync(absPath) + }) + } + event.reply("Download.deleteVersion.hook", { + err: null, + data: "删除成功" + }); + }) + /** * 打开GitHub */ diff --git a/electron/main/index.ts b/electron/main/index.ts index 3a59204..0531980 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -4,9 +4,10 @@ import node_path, {join} from "node:path"; import {initGitHubApi} from "../api/github"; import {initConfigApi} from "../api/config"; import {initProxyApi} from "../api/proxy"; -import {initFrpcApi, stopFrpcProcess} from "../api/frpc"; +import {initFrpcApi, startFrpWorkerProcess, stopFrpcProcess} from "../api/frpc"; import {initLoggerApi} from "../api/logger"; import {initFileApi} from "../api/file"; +import {getConfig} from "../storage/config"; // The built directory structure // // ├─┬ dist-electron @@ -135,6 +136,16 @@ export const createTray = () => { tray.on('double-click', () => { win.show(); }) + + getConfig((err, config) => { + if (!err) { + if (config) { + if (config.systemStartupConnect) { + startFrpWorkerProcess(config) + } + } + } + }) } app.whenReady().then(() => { diff --git a/electron/storage/version.ts b/electron/storage/version.ts index ce9a80c..30f07b1 100644 --- a/electron/storage/version.ts +++ b/electron/storage/version.ts @@ -1,11 +1,11 @@ import Datastore from "nedb"; import path from "path"; -import { Proxy } from "./proxy"; -import { app } from "electron"; +import {Proxy} from "./proxy"; +import {app} from "electron"; const versionDB = new Datastore({ - autoload: true, - filename: path.join(app.getPath("userData"), "version.db") + autoload: true, + filename: path.join(app.getPath("userData"), "version.db") }); /** @@ -14,10 +14,10 @@ const versionDB = new Datastore({ * @param cb */ export const insertVersion = ( - version: any, - cb?: (err: Error | null, document: any) => void + version: any, + cb?: (err: Error | null, document: any) => void ) => { - versionDB.insert(version, cb); + versionDB.insert(version, cb); }; /** @@ -25,14 +25,18 @@ export const insertVersion = ( * @param cb */ export const listVersion = ( - callback: (err: Error | null, documents: any[]) => void + callback: (err: Error | null, documents: any[]) => void ) => { - versionDB.find({}, callback); + versionDB.find({}, callback); }; export const getVersionById = ( - id: string, - callback: (err: Error | null, document: any) => void + id: string, + callback: (err: Error | null, document: any) => void ) => { - versionDB.findOne({ id: id }, callback); + versionDB.findOne({id: id}, callback); }; + +export const deleteVersionById = (id: string, callback: (err: Error | null, document: any) => void) => { + versionDB.remove({id: id}, callback); +} \ No newline at end of file diff --git a/package.json b/package.json index fdad2f9..5986327 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Frpc-Desktop", - "version": "1.0.2", + "version": "1.0.3", "main": "dist-electron/main/index.js", "description": "一个frpc桌面客户端", "author": "刘嘉伟 <8473136@qq.com>", @@ -54,7 +54,7 @@ "vite-plugin-electron-renderer": "^0.14.5", "vue": "^3.3.4", "vue-router": "^4.2.4", - "vue-tsc": "^1.8.8", + "vue-tsc": "^2.0.22", "vue-types": "^5.1.1" }, "dependencies": { diff --git a/src/layout/compoenets/LeftMenu.vue b/src/layout/compoenets/LeftMenu.vue index 3dd3ecc..1f3ea7d 100644 --- a/src/layout/compoenets/LeftMenu.vue +++ b/src/layout/compoenets/LeftMenu.vue @@ -19,7 +19,7 @@ const currentRoute = computed(() => { * 菜单切换 * @param mi 菜单索引 */ -const handleMenuChange = (route: RouteRecordRaw) => { +const handleMenuChange = (route: any) => { if (currentRoute.value.name === route.name) { return; } diff --git a/src/styles/layout.scss b/src/styles/layout.scss index 8970772..6504d8b 100644 --- a/src/styles/layout.scss +++ b/src/styles/layout.scss @@ -1,5 +1,6 @@ $main-bg: #F3F3F3; $primary-color: #5F3BB0; +$danger-color: #F56C6C; .main-container { background: $main-bg; @@ -84,3 +85,7 @@ $primary-color: #5F3BB0; .primary-text { color: $primary-color; } + +.danger-text { + color: $danger-color !important; +} \ No newline at end of file diff --git a/src/views/download/index.vue b/src/views/download/index.vue index d0a072d..f8630e0 100644 --- a/src/views/download/index.vue +++ b/src/views/download/index.vue @@ -4,6 +4,7 @@ import {ipcRenderer} from "electron"; import moment from "moment"; import Breadcrumb from "@/layout/compoenets/Breadcrumb.vue"; import {Icon} from "@iconify/vue"; +import {ElMessage} from "element-plus"; defineComponent({ name: "Download" @@ -18,6 +19,7 @@ type Version = { name: string; published_at: string; download_completed: boolean; + absPath: string; assets: Asset[] }; @@ -42,6 +44,17 @@ const handleDownload = (version: Version) => { downloading.value.set(version.id, 0); }; +/** + * 删除下载 + * @param version + */ +const handleDeleteVersion = (version: Version) => { + ipcRenderer.send("github.deleteVersion", { + id: version.id, + absPath: version.absPath + }); +} + const handleInitDownloadHook = () => { ipcRenderer.on("Download.frpVersionHook", (event, args) => { loading.value--; @@ -49,6 +62,7 @@ const handleInitDownloadHook = () => { m.published_at = moment(m.published_at).format("YYYY-MM-DD HH:mm:ss") return m as Version; }) as Array; + console.log(versions, 'versions') }); // 进度监听 ipcRenderer.on("Download.frpVersionDownloadOnProgress", (event, args) => { @@ -67,6 +81,18 @@ const handleInitDownloadHook = () => { version.download_completed = true; } }); + ipcRenderer.on("Download.deleteVersion.hook", (event, args) => { + const {err, data} = args + if (!err) { + loading.value++; + ElMessage({ + type: "success", + message: "删除成功" + }); + handleLoadVersions(); + } + + }) }; onMounted(() => { @@ -81,6 +107,7 @@ onUnmounted(() => { ipcRenderer.removeAllListeners("Download.frpVersionDownloadOnProgress"); ipcRenderer.removeAllListeners("Download.frpVersionDownloadOnCompleted"); ipcRenderer.removeAllListeners("Download.frpVersionHook"); + ipcRenderer.removeAllListeners("Download.deleteVersion.hook"); });