Merge branch 'develop'
This commit is contained in:
commit
1f531ee330
@ -21,6 +21,10 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## 里程碑
|
||||||
|
- 2023-12-01: 发布v1.0.1版本
|
||||||
|
- 2023-11-28: 发布v1.0版本
|
||||||
|
|
||||||
## 演示
|
## 演示
|
||||||
|
|
||||||

|

|
||||||
@ -31,11 +35,6 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
[//]: # (## 里程碑)
|
|
||||||
|
|
||||||
[//]: # (- 2023-11-28: 发布v1.0版本)
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
|
13
electron/api/file.ts
Normal file
13
electron/api/file.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import {dialog, ipcMain} from "electron";
|
||||||
|
|
||||||
|
export const initFileApi = () => {
|
||||||
|
ipcMain.handle("file.selectFile", async (event, args) => {
|
||||||
|
const result = dialog.showOpenDialogSync({
|
||||||
|
properties: ['openFile'],
|
||||||
|
filters: [
|
||||||
|
{name: 'Text Files', extensions: args},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
@ -1,25 +1,18 @@
|
|||||||
import { app, ipcMain } from "electron";
|
import {app, ipcMain, Notification} from "electron";
|
||||||
import {Config, getConfig} from "../storage/config";
|
import {Config, getConfig} from "../storage/config";
|
||||||
import {listProxy} from "../storage/proxy";
|
import {listProxy} from "../storage/proxy";
|
||||||
import {getVersionById} from "../storage/version";
|
import {getVersionById} from "../storage/version";
|
||||||
|
import treeKill from "tree-kill";
|
||||||
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
const {exec, spawn} = require("child_process");
|
const {exec, spawn} = require("child_process");
|
||||||
export let frpcProcess = null;
|
export let frpcProcess = null;
|
||||||
|
|
||||||
const runningCmd = {
|
const runningCmd = {
|
||||||
commandPath: null,
|
commandPath: null,
|
||||||
configPath: null
|
configPath: null
|
||||||
};
|
};
|
||||||
|
let frpcStatusListener = null;
|
||||||
// const getFrpc = (config: Config) => {
|
|
||||||
// getVersionById(config.currentVersion, (err, document) => {
|
|
||||||
// if (!err) {
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取选择版本的工作目录
|
* 获取选择版本的工作目录
|
||||||
@ -74,18 +67,28 @@ serverPort = ${config.serverPort}
|
|||||||
auth.method = "${config.authMethod}"
|
auth.method = "${config.authMethod}"
|
||||||
auth.token = "${config.authToken}"
|
auth.token = "${config.authToken}"
|
||||||
log.to = "frpc.log"
|
log.to = "frpc.log"
|
||||||
log.level = "debug"
|
log.level = "${config.logLevel}"
|
||||||
log.maxDays = 3
|
log.maxDays = ${config.logMaxDays}
|
||||||
webServer.addr = "127.0.0.1"
|
webServer.addr = "127.0.0.1"
|
||||||
webServer.port = 57400
|
webServer.port = 57400
|
||||||
|
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}"
|
||||||
|
` : ""}
|
||||||
|
|
||||||
${proxyToml}
|
|
||||||
|
${proxyToml.join("")}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// const configPath = path.join("frp.toml");
|
// const configPath = path.join("frp.toml");
|
||||||
const filename = "frp.toml";
|
const filename = "frp.toml";
|
||||||
|
const configPath = path.join(app.getPath("userData"), filename)
|
||||||
|
console.debug("生成配置成功", configPath)
|
||||||
fs.writeFile(
|
fs.writeFile(
|
||||||
path.join(app.getPath("userData"), filename), // 配置文件目录
|
configPath, // 配置文件目录
|
||||||
toml, // 配置文件内容
|
toml, // 配置文件内容
|
||||||
{flag: "w"},
|
{flag: "w"},
|
||||||
err => {
|
err => {
|
||||||
@ -106,6 +109,7 @@ ${proxyToml}
|
|||||||
*/
|
*/
|
||||||
const startFrpcProcess = (commandPath: string, configPath: string) => {
|
const startFrpcProcess = (commandPath: string, configPath: string) => {
|
||||||
const command = `${commandPath} -c ${configPath}`;
|
const command = `${commandPath} -c ${configPath}`;
|
||||||
|
console.info("启动", command)
|
||||||
frpcProcess = spawn(command, {
|
frpcProcess = spawn(command, {
|
||||||
cwd: app.getPath("userData"),
|
cwd: app.getPath("userData"),
|
||||||
shell: true
|
shell: true
|
||||||
@ -113,14 +117,28 @@ const startFrpcProcess = (commandPath: string, configPath: string) => {
|
|||||||
runningCmd.commandPath = commandPath;
|
runningCmd.commandPath = commandPath;
|
||||||
runningCmd.configPath = configPath;
|
runningCmd.configPath = configPath;
|
||||||
frpcProcess.stdout.on("data", data => {
|
frpcProcess.stdout.on("data", data => {
|
||||||
console.log(`命令输出: ${data}`);
|
console.debug(`命令输出: ${data}`);
|
||||||
});
|
});
|
||||||
frpcProcess.stdout.on("error", data => {
|
frpcProcess.stdout.on("error", data => {
|
||||||
console.log(`执行错误: ${data}`);
|
console.log("启动错误", data)
|
||||||
frpcProcess.kill("SIGINT");
|
stopFrpcProcess()
|
||||||
});
|
});
|
||||||
|
frpcStatusListener = setInterval(() => {
|
||||||
|
const status = frpcProcessStatus()
|
||||||
|
if (!status) {
|
||||||
|
console.log("连接已断开")
|
||||||
|
new Notification({
|
||||||
|
title: "Frpc Desktop",
|
||||||
|
body: "连接已断开,请前往日志查看原因"
|
||||||
|
}).show()
|
||||||
|
clearInterval(frpcStatusListener)
|
||||||
|
}
|
||||||
|
}, 3000)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重载frpc配置
|
||||||
|
*/
|
||||||
export const reloadFrpcProcess = () => {
|
export const reloadFrpcProcess = () => {
|
||||||
if (frpcProcess && !frpcProcess.killed) {
|
if (frpcProcess && !frpcProcess.killed) {
|
||||||
getConfig((err1, config) => {
|
getConfig((err1, config) => {
|
||||||
@ -128,7 +146,7 @@ export const reloadFrpcProcess = () => {
|
|||||||
if (config) {
|
if (config) {
|
||||||
generateConfig(config, configPath => {
|
generateConfig(config, configPath => {
|
||||||
const command = `${runningCmd.commandPath} reload -c ${configPath}`;
|
const command = `${runningCmd.commandPath} reload -c ${configPath}`;
|
||||||
console.log("重启", command);
|
console.info("重启", command);
|
||||||
exec(command, {
|
exec(command, {
|
||||||
cwd: app.getPath("userData"),
|
cwd: app.getPath("userData"),
|
||||||
shell: true
|
shell: true
|
||||||
@ -140,13 +158,45 @@ export const reloadFrpcProcess = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initFrpcApi = () => {
|
/**
|
||||||
ipcMain.handle("frpc.running", async (event, args) => {
|
* 停止frpc子进程
|
||||||
|
*/
|
||||||
|
export const stopFrpcProcess = (callback?:() => void) => {
|
||||||
|
if (frpcProcess) {
|
||||||
|
treeKill(frpcProcess.pid, (error: Error) => {
|
||||||
|
if (error) {
|
||||||
|
console.log("关闭失败", frpcProcess.pid, error)
|
||||||
|
} else {
|
||||||
|
console.log('关闭成功')
|
||||||
|
frpcProcess = null
|
||||||
|
clearInterval(frpcStatusListener)
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取frpc子进程状态
|
||||||
|
*/
|
||||||
|
export const frpcProcessStatus = () => {
|
||||||
if (!frpcProcess) {
|
if (!frpcProcess) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
return !frpcProcess.killed;
|
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
// 发送信号给进程,如果进程存在,会正常返回
|
||||||
|
process.kill(frpcProcess.pid, 0);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
// 进程不存在,抛出异常
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const initFrpcApi = () => {
|
||||||
|
ipcMain.handle("frpc.running", async (event, args) => {
|
||||||
|
return frpcProcessStatus()
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on("frpc.start", async (event, args) => {
|
ipcMain.on("frpc.start", async (event, args) => {
|
||||||
@ -184,8 +234,7 @@ export const initFrpcApi = () => {
|
|||||||
|
|
||||||
ipcMain.on("frpc.stop", () => {
|
ipcMain.on("frpc.stop", () => {
|
||||||
if (frpcProcess && !frpcProcess.killed) {
|
if (frpcProcess && !frpcProcess.killed) {
|
||||||
console.log("关闭");
|
stopFrpcProcess()
|
||||||
frpcProcess.kill();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {app, BrowserWindow, ipcMain, net} from "electron";
|
import {app, BrowserWindow, ipcMain, net, shell} from "electron";
|
||||||
import {insertVersion} from "../storage/version";
|
import {insertVersion} from "../storage/version";
|
||||||
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
@ -162,4 +162,11 @@ export const initGitHubApi = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开GitHub
|
||||||
|
*/
|
||||||
|
ipcMain.on("github.open", () => {
|
||||||
|
shell.openExternal("https://github.com/luckjiawei/frpc-desktop");
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { app, BrowserWindow, ipcMain, shell } from "electron";
|
import {app, BrowserWindow, ipcMain, Menu, MenuItem, MenuItemConstructorOptions, shell, Tray} from "electron";
|
||||||
import {release} from "node:os";
|
import {release} from "node:os";
|
||||||
import { join } from "node:path";
|
import node_path, {join} from "node:path";
|
||||||
import {initGitHubApi} from "../api/github";
|
import {initGitHubApi} from "../api/github";
|
||||||
import {initConfigApi} from "../api/config";
|
import {initConfigApi} from "../api/config";
|
||||||
import {initProxyApi} from "../api/proxy";
|
import {initProxyApi} from "../api/proxy";
|
||||||
import { initFrpcApi } from "../api/frpc";
|
import {initFrpcApi, stopFrpcProcess} from "../api/frpc";
|
||||||
import {initLoggerApi} from "../api/logger";
|
import {initLoggerApi} from "../api/logger";
|
||||||
|
import {initFileApi} from "../api/file";
|
||||||
// The built directory structure
|
// The built directory structure
|
||||||
//
|
//
|
||||||
// ├─┬ dist-electron
|
// ├─┬ dist-electron
|
||||||
@ -39,15 +40,17 @@ if (!app.requestSingleInstanceLock()) {
|
|||||||
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
|
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
|
||||||
|
|
||||||
let win: BrowserWindow | null = null;
|
let win: BrowserWindow | null = null;
|
||||||
|
let tray = null;
|
||||||
// Here, you can also use other preload
|
// Here, you can also use other preload
|
||||||
const preload = join(__dirname, "../preload/index.js");
|
const preload = join(__dirname, "../preload/index.js");
|
||||||
const url = process.env.VITE_DEV_SERVER_URL;
|
const url = process.env.VITE_DEV_SERVER_URL;
|
||||||
const indexHtml = join(process.env.DIST, "index.html");
|
const indexHtml = join(process.env.DIST, "index.html");
|
||||||
|
let isQuiting;
|
||||||
|
|
||||||
async function createWindow() {
|
async function createWindow() {
|
||||||
win = new BrowserWindow({
|
win = new BrowserWindow({
|
||||||
title: "Main window",
|
title: "Frpc Desktop",
|
||||||
icon: join(process.env.VITE_PUBLIC, "favicon.ico"),
|
icon: join(process.env.VITE_PUBLIC, "logo/16x16.png"),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload,
|
preload,
|
||||||
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
|
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
|
||||||
@ -57,7 +60,6 @@ async function createWindow() {
|
|||||||
contextIsolation: false
|
contextIsolation: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.VITE_DEV_SERVER_URL) {
|
if (process.env.VITE_DEV_SERVER_URL) {
|
||||||
// electron-vite-vue#298
|
// electron-vite-vue#298
|
||||||
win.loadURL(url);
|
win.loadURL(url);
|
||||||
@ -82,17 +84,72 @@ async function createWindow() {
|
|||||||
const {Menu} = require("electron");
|
const {Menu} = require("electron");
|
||||||
Menu.setApplicationMenu(null);
|
Menu.setApplicationMenu(null);
|
||||||
// hide menu for Mac
|
// hide menu for Mac
|
||||||
if (process.platform !== "darwin") {
|
// if (process.platform !== "darwin") {
|
||||||
|
// app.dock.hide();
|
||||||
|
// }
|
||||||
|
|
||||||
|
win.on('minimize', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
win.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
win.on('close', function (event) {
|
||||||
|
if (!isQuiting) {
|
||||||
|
event.preventDefault();
|
||||||
|
win.hide();
|
||||||
|
if (process.platform === "darwin") {
|
||||||
app.dock.hide();
|
app.dock.hide();
|
||||||
}
|
}
|
||||||
// win.webContents.on('will-navigate', (event, url) => { }) #344
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.whenReady().then(createWindow);
|
export const createTray = () => {
|
||||||
|
let menu: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
||||||
|
{
|
||||||
|
label: '显示主窗口', click: function () {
|
||||||
|
win.show();
|
||||||
|
if (process.platform === "darwin") {
|
||||||
|
app.dock.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '退出',
|
||||||
|
click: () => {
|
||||||
|
isQuiting = true;
|
||||||
|
stopFrpcProcess(() => {
|
||||||
|
app.quit();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
tray = new Tray(node_path.join(process.env.VITE_PUBLIC, "logo/16x16.png"))
|
||||||
|
tray.setToolTip('Frpc Desktop')
|
||||||
|
const contextMenu = Menu.buildFromTemplate(menu)
|
||||||
|
tray.setContextMenu(contextMenu)
|
||||||
|
|
||||||
|
// 托盘双击打开
|
||||||
|
tray.on('double-click', () => {
|
||||||
|
win.show();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
createWindow().then(r => {
|
||||||
|
createTray()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
app.on("window-all-closed", () => {
|
app.on("window-all-closed", () => {
|
||||||
win = null;
|
win = null;
|
||||||
if (process.platform !== "darwin") app.quit();
|
if (process.platform !== "darwin") {
|
||||||
|
stopFrpcProcess(() => {
|
||||||
|
app.quit();
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on("second-instance", () => {
|
app.on("second-instance", () => {
|
||||||
@ -112,6 +169,10 @@ app.on("activate", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.on('before-quit', () => {
|
||||||
|
isQuiting = true;
|
||||||
|
})
|
||||||
|
|
||||||
// New window example arg: new windows url
|
// New window example arg: new windows url
|
||||||
ipcMain.handle("open-win", (_, arg) => {
|
ipcMain.handle("open-win", (_, arg) => {
|
||||||
const childWindow = new BrowserWindow({
|
const childWindow = new BrowserWindow({
|
||||||
@ -130,7 +191,8 @@ ipcMain.handle("open-win", (_, arg) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('open-url', (event, url) => {
|
ipcMain.on('open-url', (event, url) => {
|
||||||
shell.openExternal(url).then(r => {});
|
shell.openExternal(url).then(r => {
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
initGitHubApi();
|
initGitHubApi();
|
||||||
@ -138,3 +200,4 @@ initConfigApi();
|
|||||||
initProxyApi();
|
initProxyApi();
|
||||||
initFrpcApi();
|
initFrpcApi();
|
||||||
initLoggerApi();
|
initLoggerApi();
|
||||||
|
initFileApi();
|
||||||
|
@ -13,6 +13,13 @@ export type Config = {
|
|||||||
serverPort: number;
|
serverPort: number;
|
||||||
authMethod: string;
|
authMethod: string;
|
||||||
authToken: string;
|
authToken: string;
|
||||||
|
logLevel: string;
|
||||||
|
logMaxDays: number;
|
||||||
|
tlsConfigEnable: boolean;
|
||||||
|
tlsConfigCertFile: string;
|
||||||
|
tlsConfigKeyFile: string;
|
||||||
|
tlsConfigTrustedCaFile: string;
|
||||||
|
tlsConfigServerName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Frpc-Desktop",
|
"name": "Frpc-Desktop",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"main": "dist-electron/main/index.js",
|
"main": "dist-electron/main/index.js",
|
||||||
"description": "一个frpc桌面客户端",
|
"description": "一个frpc桌面客户端",
|
||||||
"author": "刘嘉伟 <8473136@qq.com>",
|
"author": "刘嘉伟 <8473136@qq.com>",
|
||||||
|
@ -3,6 +3,7 @@ import { computed, defineComponent, onMounted, ref } from "vue";
|
|||||||
import { Icon } from "@iconify/vue";
|
import { Icon } from "@iconify/vue";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import { RouteRecordRaw } from "vue-router";
|
import { RouteRecordRaw } from "vue-router";
|
||||||
|
import {ipcRenderer} from "electron";
|
||||||
|
|
||||||
defineComponent({
|
defineComponent({
|
||||||
name: "AppMain"
|
name: "AppMain"
|
||||||
@ -27,6 +28,10 @@ const handleMenuChange = (route: RouteRecordRaw) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenGitHub = () => {
|
||||||
|
ipcRenderer.send("github.open")
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
routes.value = router.options.routes[0].children?.filter(
|
routes.value = router.options.routes[0].children?.filter(
|
||||||
f => !f.meta?.hidden
|
f => !f.meta?.hidden
|
||||||
@ -49,6 +54,12 @@ onMounted(() => {
|
|||||||
>
|
>
|
||||||
<Icon :icon="r?.meta?.icon as string" />
|
<Icon :icon="r?.meta?.icon as string" />
|
||||||
</li>
|
</li>
|
||||||
|
<li
|
||||||
|
class="menu"
|
||||||
|
@click="handleOpenGitHub"
|
||||||
|
>
|
||||||
|
<Icon icon="mdi:github" />
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineComponent, onMounted, onUnmounted, ref, reactive } from "vue";
|
import {defineComponent, onMounted, onUnmounted, reactive, ref} from "vue";
|
||||||
import {ipcRenderer} from "electron";
|
import {ipcRenderer} from "electron";
|
||||||
import {ElMessage, FormInstance, FormRules} from "element-plus";
|
import {ElMessage, FormInstance, FormRules} from "element-plus";
|
||||||
import Breadcrumb from "@/layout/compoenets/Breadcrumb.vue";
|
import Breadcrumb from "@/layout/compoenets/Breadcrumb.vue";
|
||||||
@ -17,6 +17,14 @@ type Config = {
|
|||||||
serverPort: number;
|
serverPort: number;
|
||||||
authMethod: string;
|
authMethod: string;
|
||||||
authToken: string;
|
authToken: string;
|
||||||
|
logLevel: string;
|
||||||
|
logMaxDays: number;
|
||||||
|
tlsConfigEnable: boolean;
|
||||||
|
tlsConfigCertFile: string;
|
||||||
|
tlsConfigKeyFile: string;
|
||||||
|
tlsConfigTrustedCaFile: string;
|
||||||
|
tlsConfigServerName: string;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Version = {
|
type Version = {
|
||||||
@ -29,7 +37,14 @@ const formData = ref<Config>({
|
|||||||
serverAddr: "",
|
serverAddr: "",
|
||||||
serverPort: 7000,
|
serverPort: 7000,
|
||||||
authMethod: "",
|
authMethod: "",
|
||||||
authToken: ""
|
authToken: "",
|
||||||
|
logLevel: "info",
|
||||||
|
logMaxDays: 3,
|
||||||
|
tlsConfigEnable: false,
|
||||||
|
tlsConfigCertFile: "",
|
||||||
|
tlsConfigKeyFile: "",
|
||||||
|
tlsConfigTrustedCaFile: "",
|
||||||
|
tlsConfigServerName: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const loading = ref(1);
|
const loading = ref(1);
|
||||||
@ -48,7 +63,14 @@ const rules = reactive<FormRules>({
|
|||||||
{required: true, message: "请输入服务器端口", trigger: "blur"}
|
{required: true, message: "请输入服务器端口", trigger: "blur"}
|
||||||
],
|
],
|
||||||
// authMethod: [{ required: true, message: "请选择验证方式", trigger: "blur" }],
|
// authMethod: [{ required: true, message: "请选择验证方式", trigger: "blur" }],
|
||||||
authToken: [{ required: true, message: "请输入token值 ", trigger: "blur" }]
|
authToken: [{required: true, message: "请输入token值 ", trigger: "blur"}],
|
||||||
|
logLevel: [{required: true, message: "请选择日志级别 ", trigger: "blur"}],
|
||||||
|
logMaxDays: [{required: true, message: "请输入日志保留天数 ", trigger: "blur"}],
|
||||||
|
tlsConfigEnable: [{required: true, message: "请选择TLS状态", trigger: "change"}],
|
||||||
|
tlsConfigCertFile: [{required: true, message: "请选择TLS证书文件", trigger: "change"}],
|
||||||
|
tlsConfigKeyFile: [{required: true, message: "请选择TLS密钥文件", trigger: "change"}],
|
||||||
|
tlsConfigTrustedCaFile: [{required: true, message: "请选择CA证书文件", trigger: "change"}],
|
||||||
|
tlsConfigServerName: [{required: true, message: "请输入TLS Server名称", trigger: "blur"}]
|
||||||
});
|
});
|
||||||
|
|
||||||
const versions = ref<Array<Version>>([]);
|
const versions = ref<Array<Version>>([]);
|
||||||
@ -97,6 +119,24 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleSelectFile = (type: number, ext: string[]) => {
|
||||||
|
ipcRenderer.invoke("file.selectFile", ext).then(r => {
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
formData.value.tlsConfigCertFile = r[0]
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
formData.value.tlsConfigKeyFile = r[0]
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
formData.value.tlsConfigTrustedCaFile = r[0]
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.log(r)
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
ipcRenderer.removeAllListeners("Config.getConfig.hook");
|
ipcRenderer.removeAllListeners("Config.getConfig.hook");
|
||||||
ipcRenderer.removeAllListeners("Config.saveConfig.hook");
|
ipcRenderer.removeAllListeners("Config.saveConfig.hook");
|
||||||
@ -104,18 +144,18 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="main">
|
||||||
<breadcrumb/>
|
<breadcrumb/>
|
||||||
|
<div class="app-container-breadcrumb pr-2" v-loading="loading > 0">
|
||||||
<div
|
<div
|
||||||
class="w-full bg-white p-4 rounded drop-shadow-lg"
|
class="w-full bg-white p-4 rounded drop-shadow-lg"
|
||||||
v-loading="loading > 0"
|
|
||||||
>
|
>
|
||||||
<el-form
|
<el-form
|
||||||
:model="formData"
|
:model="formData"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
label-position="right"
|
label-position="right"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
label-width="120"
|
label-width="140"
|
||||||
>
|
>
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
@ -133,40 +173,52 @@ onUnmounted(() => {
|
|||||||
></el-option>
|
></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
<div class="w-full flex justify-end">
|
<div class="w-full flex justify-end">
|
||||||
<el-button type="text" @click="handleLoadVersions">
|
<el-link type="primary" @click="handleLoadVersions">
|
||||||
<Icon class="mr-1" icon="material-symbols:refresh-rounded"/>
|
<Icon class="mr-1" icon="material-symbols:refresh-rounded"/>
|
||||||
手动刷新
|
手动刷新
|
||||||
</el-button>
|
</el-link>
|
||||||
<el-button
|
<!-- <el-button type="text" @click="handleLoadVersions">-->
|
||||||
type="text"
|
<!-- <Icon class="mr-1" icon="material-symbols:refresh-rounded"/>-->
|
||||||
@click="$router.replace({ name: 'Download' })"
|
<!-- 手动刷新-->
|
||||||
>
|
<!-- </el-button>-->
|
||||||
|
<el-link class="ml-2" type="primary" @click="$router.replace({ name: 'Download' })">
|
||||||
<Icon class="mr-1" icon="material-symbols:download-2"/>
|
<Icon class="mr-1" icon="material-symbols:download-2"/>
|
||||||
点击这里去下载
|
点击这里去下载
|
||||||
</el-button>
|
</el-link>
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- type="text"-->
|
||||||
|
<!-- @click="$router.replace({ name: 'Download' })"-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <Icon class="mr-1" icon="material-symbols:download-2"/>-->
|
||||||
|
<!-- 点击这里去下载-->
|
||||||
|
<!-- </el-button>-->
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="服务端地址:" prop="serverAddr">
|
<div class="h2">服务器配置</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="服务器地址:" prop="serverAddr">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.serverAddr"
|
v-model="formData.serverAddr"
|
||||||
placeholder="127.0.0.1"
|
placeholder="127.0.0.1"
|
||||||
></el-input>
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="12">
|
||||||
<el-form-item label="服务端端口:" prop="serverPort">
|
<el-form-item label="服务器端口:" prop="serverPort">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
placeholder="7000"
|
placeholder="7000"
|
||||||
v-model="formData.serverPort"
|
v-model="formData.serverPort"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="65535"
|
:max="65535"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
|
class="!w-full"
|
||||||
></el-input-number>
|
></el-input-number>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="12">
|
||||||
<el-form-item label="验证方式:" prop="authMethod">
|
<el-form-item label="验证方式:" prop="authMethod">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="formData.authMethod"
|
v-model="formData.authMethod"
|
||||||
@ -186,6 +238,78 @@ onUnmounted(() => {
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<div class="h2">TSL Config</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="是否启用TSL:" prop="tlsConfigEnable">
|
||||||
|
<el-switch active-text="开"
|
||||||
|
inline-prompt
|
||||||
|
inactive-text="关"
|
||||||
|
v-model="formData.tlsConfigEnable"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<template v-if="formData.tlsConfigEnable">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="TLS证书文件:" prop="tlsConfigCertFile">
|
||||||
|
<el-input
|
||||||
|
class="button-input"
|
||||||
|
v-model="formData.tlsConfigCertFile"
|
||||||
|
placeholder="请选择TLS证书文件"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
<el-button class="ml-2" type="primary" @click="handleSelectFile(1, ['crt'])">选择</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="TLS密钥文件:" prop="tlsConfigKeyFile">
|
||||||
|
<el-input
|
||||||
|
class="button-input"
|
||||||
|
v-model="formData.tlsConfigKeyFile"
|
||||||
|
placeholder="请选择TLS密钥文件"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
<el-button class="ml-2" type="primary" @click="handleSelectFile(2, ['key'])">选择</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="CA证书文件:" prop="tlsConfigTrustedCaFile">
|
||||||
|
<el-input
|
||||||
|
class="button-input"
|
||||||
|
v-model="formData.tlsConfigTrustedCaFile"
|
||||||
|
placeholder="请选择CA证书文件"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
<el-button class="ml-2" type="primary" @click="handleSelectFile(3, ['crt'])">选择</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="TLS Server名称:" prop="tlsConfigServerName">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.tlsConfigServerName"
|
||||||
|
placeholder="请输入TLS Server名称"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
<el-col :span="24">
|
||||||
|
<div class="h2">日志配置</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item class="!w-full" label="日志级别:" prop="logLevel">
|
||||||
|
<el-select v-model="formData.logLevel">
|
||||||
|
<el-option label="info" value="info"/>
|
||||||
|
<el-option label="debug" value="debug"/>
|
||||||
|
<el-option label="waring" value="waring"/>
|
||||||
|
<el-option label="error" value="error"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="日志保留天数:" prop="logMaxDays">
|
||||||
|
<el-input-number class="!w-full" controls-position="right" v-model="formData.logMaxDays"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button plain type="primary" @click="handleSubmit">
|
<el-button plain type="primary" @click="handleSubmit">
|
||||||
@ -198,6 +322,24 @@ onUnmounted(() => {
|
|||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.h2 {
|
||||||
|
color: #5A3DAA;
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 6px 10px 6px 15px;
|
||||||
|
border-left: 5px solid #5A3DAA;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #EEEBF6;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-input {
|
||||||
|
width: calc(100% - 68px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -32,6 +32,7 @@ onMounted(() => {
|
|||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
ipcRenderer.invoke("frpc.running").then(data => {
|
ipcRenderer.invoke("frpc.running").then(data => {
|
||||||
running.value = data;
|
running.value = data;
|
||||||
|
console.log('进程状态', data)
|
||||||
});
|
});
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
@ -94,16 +95,21 @@ onUnmounted(() => {
|
|||||||
<div class="pl-8 h-28 w-52 flex flex-col justify-between">
|
<div class="pl-8 h-28 w-52 flex flex-col justify-between">
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div class="font-bold text-2xl text-center">
|
<div class="font-bold text-2xl text-center">
|
||||||
|
<Icon v-if="running" class="text-[#7EC050] inline-block relative top-1" icon="material-symbols:check-circle-rounded" />
|
||||||
|
<Icon v-else class="text-[#E47470] inline-block relative top-1" icon="material-symbols:error" />
|
||||||
Frpc {{ running ? "已启动" : "已断开" }}
|
Frpc {{ running ? "已启动" : "已断开" }}
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<el-button
|
<!-- <el-button-->
|
||||||
class="block"
|
<!-- class="block"-->
|
||||||
type="text"
|
<!-- type="text"-->
|
||||||
v-if="running"
|
<!-- v-if="running"-->
|
||||||
@click="$router.replace({ name: 'Logger' })"
|
<!-- @click="$router.replace({ name: 'Logger' })"-->
|
||||||
>查看日志
|
<!-- >查看日志-->
|
||||||
</el-button>
|
<!-- </el-button>-->
|
||||||
|
<div class="w-full justify-center text-center">
|
||||||
|
<el-link v-if="running" type="primary" @click="$router.replace({ name: 'Logger' })">查看日志</el-link>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="w-full h-8 bg-[#563EA4] rounded flex justify-center items-center text-white font-bold cursor-pointer"
|
class="w-full h-8 bg-[#563EA4] rounded flex justify-center items-center text-white font-bold cursor-pointer"
|
||||||
@click="handleButtonClick"
|
@click="handleButtonClick"
|
||||||
|
Loading…
Reference in New Issue
Block a user