✨ 配置导出
This commit is contained in:
parent
6e028d377a
commit
9fed5fc844
@ -1,52 +1,102 @@
|
|||||||
import {app, ipcMain} from "electron";
|
import { app, dialog, ipcMain } from "electron";
|
||||||
import {getConfig, saveConfig} from "../storage/config";
|
import { getConfig, saveConfig } from "../storage/config";
|
||||||
import {listVersion} from "../storage/version";
|
import { listVersion } from "../storage/version";
|
||||||
|
import { generateConfig, genIniConfig, genTomlConfig } from "./frpc";
|
||||||
|
import { exec } from "child_process";
|
||||||
|
import { listProxy } from "../storage/proxy";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
const log = require('electron-log');
|
const log = require("electron-log");
|
||||||
|
|
||||||
export const initConfigApi = () => {
|
export const initConfigApi = () => {
|
||||||
ipcMain.on("config.saveConfig", async (event, args) => {
|
ipcMain.on("config.saveConfig", async (event, args) => {
|
||||||
saveConfig(args, (err, numberOfUpdated, upsert) => {
|
saveConfig(args, (err, numberOfUpdated, upsert) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
const start = args.systemSelfStart || false;
|
const start = args.systemSelfStart || false;
|
||||||
log.info("开启自启状态", start)
|
log.info("开启自启状态", start);
|
||||||
app.setLoginItemSettings({
|
app.setLoginItemSettings({
|
||||||
openAtLogin: start, //win
|
openAtLogin: start, //win
|
||||||
openAsHidden: start, //macOs
|
openAsHidden: start //macOs
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
event.reply("Config.saveConfig.hook", {
|
||||||
|
err: err,
|
||||||
|
numberOfUpdated: numberOfUpdated,
|
||||||
|
upsert: upsert
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("config.getConfig", async (event, args) => {
|
||||||
|
getConfig((err, doc) => {
|
||||||
|
event.reply("Config.getConfig.hook", {
|
||||||
|
err: err,
|
||||||
|
data: doc
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("config.versions", event => {
|
||||||
|
listVersion((err, doc) => {
|
||||||
|
event.reply("Config.versions.hook", {
|
||||||
|
err: err,
|
||||||
|
data: doc
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("config.hasConfig", event => {
|
||||||
|
getConfig((err, doc) => {
|
||||||
|
event.reply("Config.getConfig.hook", {
|
||||||
|
err: err,
|
||||||
|
data: doc
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("config.exportConfig", async (event, args) => {
|
||||||
|
const result = await dialog.showOpenDialog({
|
||||||
|
properties: ["openDirectory"]
|
||||||
|
});
|
||||||
|
const outputDirectory = result.filePaths[0];
|
||||||
|
log.info(`导出目录 ${outputDirectory} 类型:${args}`);
|
||||||
|
getConfig((err1, config) => {
|
||||||
|
if (!err1 && config) {
|
||||||
|
listProxy((err2, proxys) => {
|
||||||
|
if (!err2) {
|
||||||
|
let configContent = "";
|
||||||
|
if (args === "ini") {
|
||||||
|
configContent = genIniConfig(config, proxys);
|
||||||
|
} else if (args === "toml") {
|
||||||
|
configContent = genTomlConfig(config, proxys);
|
||||||
}
|
}
|
||||||
event.reply("Config.saveConfig.hook", {
|
const configPath = path.join(
|
||||||
err: err,
|
outputDirectory,
|
||||||
numberOfUpdated: numberOfUpdated,
|
`frpc-desktop.${args}`
|
||||||
upsert: upsert
|
);
|
||||||
});
|
fs.writeFile(
|
||||||
});
|
configPath, // 配置文件目录
|
||||||
});
|
configContent, // 配置文件内容
|
||||||
|
{ flag: "w" },
|
||||||
ipcMain.on("config.getConfig", async (event, args) => {
|
err => {
|
||||||
getConfig((err, doc) => {
|
if (!err) {
|
||||||
event.reply("Config.getConfig.hook", {
|
// callback(filename);
|
||||||
err: err,
|
event.reply("config.exportConfig.hook", {
|
||||||
data: doc
|
data: "导出错误",
|
||||||
});
|
err: err
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
}
|
||||||
ipcMain.on("config.versions", event => {
|
);
|
||||||
listVersion((err, doc) => {
|
event.reply("Config.exportConfig.hook", {
|
||||||
event.reply("Config.versions.hook", {
|
data: {
|
||||||
err: err,
|
configPath: configPath
|
||||||
data: doc
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("config.hasConfig", event => {
|
|
||||||
getConfig((err, doc) => {
|
|
||||||
event.reply("Config.getConfig.hook", {
|
|
||||||
err: err,
|
|
||||||
data: doc
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -38,7 +38,7 @@ const getFrpcVersionWorkerPath = (
|
|||||||
* @param config
|
* @param config
|
||||||
* @param proxys
|
* @param proxys
|
||||||
*/
|
*/
|
||||||
const genTomlConfig = (config: FrpConfig, proxys: Proxy[]) => {
|
export const genTomlConfig = (config: FrpConfig, proxys: Proxy[]) => {
|
||||||
const proxyToml = proxys.map(m => {
|
const proxyToml = proxys.map(m => {
|
||||||
let toml = `
|
let toml = `
|
||||||
[[${m.type === "stcp" && m.stcpModel === "visitors" ? "visitors" : "proxies"}]]
|
[[${m.type === "stcp" && m.stcpModel === "visitors" ? "visitors" : "proxies"}]]
|
||||||
@ -155,7 +155,7 @@ ${proxyToml.join("")}
|
|||||||
* @param config
|
* @param config
|
||||||
* @param proxys
|
* @param proxys
|
||||||
*/
|
*/
|
||||||
const genIniConfig = (config: FrpConfig, proxys: Proxy[]) => {
|
export const genIniConfig = (config: FrpConfig, proxys: Proxy[]) => {
|
||||||
const proxyIni = proxys.map(m => {
|
const proxyIni = proxys.map(m => {
|
||||||
let ini = `
|
let ini = `
|
||||||
[${m.name}]
|
[${m.name}]
|
||||||
|
@ -29,6 +29,9 @@ import ContentCopy from "@iconify-icons/material-symbols/content-copy";
|
|||||||
import ContentPasteGo from "@iconify-icons/material-symbols/content-paste-go";
|
import ContentPasteGo from "@iconify-icons/material-symbols/content-paste-go";
|
||||||
import Edit from "@iconify-icons/material-symbols/edit";
|
import Edit from "@iconify-icons/material-symbols/edit";
|
||||||
import CheckBox from "@iconify-icons/material-symbols/check-box";
|
import CheckBox from "@iconify-icons/material-symbols/check-box";
|
||||||
|
import ExportNotesOutline from "@iconify-icons/material-symbols/export-notes-outline";
|
||||||
|
import uploadRounded from "@iconify-icons/material-symbols/upload-rounded";
|
||||||
|
import downloadRounded from "@iconify-icons/material-symbols/download-rounded";
|
||||||
|
|
||||||
addIcon("cloud", Cloud);
|
addIcon("cloud", Cloud);
|
||||||
addIcon("rocket-launch-rounded", RocketLaunchRounded);
|
addIcon("rocket-launch-rounded", RocketLaunchRounded);
|
||||||
@ -53,4 +56,8 @@ addIcon("content-copy", ContentCopy);
|
|||||||
addIcon("content-paste-go", ContentPasteGo);
|
addIcon("content-paste-go", ContentPasteGo);
|
||||||
addIcon("edit", Edit);
|
addIcon("edit", Edit);
|
||||||
addIcon("check-box", CheckBox);
|
addIcon("check-box", CheckBox);
|
||||||
|
addIcon("export", ExportNotesOutline);
|
||||||
|
addIcon("uploadRounded", uploadRounded);
|
||||||
|
addIcon("downloadRounded", downloadRounded);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Icon } from "@iconify/vue";
|
|
||||||
import { computed, defineComponent } from "vue";
|
import { computed, defineComponent } from "vue";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
|
|
||||||
|
|
||||||
defineComponent({
|
defineComponent({
|
||||||
name: "Breadcrumb"
|
name: "Breadcrumb"
|
||||||
});
|
});
|
||||||
@ -14,16 +12,17 @@ const currentRoute = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-between">
|
<div class="breadcrumb flex justify-between items-center">
|
||||||
<div class="breadcrumb animate__animated animate__lightSpeedInLeft">
|
<div
|
||||||
<IconifyIconOffline class="inline-block mr-2" :icon="currentRoute.meta['icon'] as string"/>
|
class="flex items-center justify-center breadcrumb-left animate__animated animate__lightSpeedInLeft"
|
||||||
<!-- <Icon-->
|
>
|
||||||
<!-- class="inline-block mr-2"-->
|
<IconifyIconOffline
|
||||||
<!-- :icon="currentRoute.meta['icon'] as string"-->
|
class="inline-block mr-2"
|
||||||
<!-- />-->
|
:icon="currentRoute.meta['icon'] as string"
|
||||||
|
/>
|
||||||
<span>{{ currentRoute.meta["title"] }}</span>
|
<span>{{ currentRoute.meta["title"] }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="breadcrumb-right">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,24 +21,30 @@ $danger-color: #F56C6C;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
color: $primary-color;
|
margin-bottom: 15px;
|
||||||
font-size: 36px;
|
|
||||||
height: 50px;
|
|
||||||
|
|
||||||
svg {
|
.breadcrumb-left {
|
||||||
vertical-align: top;
|
color: $primary-color;
|
||||||
}
|
font-size: 36px;
|
||||||
|
height: 30px;
|
||||||
|
|
||||||
span {
|
svg {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
color: black;
|
}
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
transform: translateY(5px);
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
|
span {
|
||||||
|
vertical-align: top;
|
||||||
|
color: black;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
//transform: translateY(5px);
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-menu-container {
|
.left-menu-container {
|
||||||
@ -57,6 +63,7 @@ $danger-color: #F56C6C;
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version:hover {
|
.version:hover {
|
||||||
animation: heartBeat 1s;
|
animation: heartBeat 1s;
|
||||||
}
|
}
|
||||||
@ -91,7 +98,6 @@ $danger-color: #F56C6C;
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +117,10 @@ $danger-color: #F56C6C;
|
|||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-primary {
|
||||||
|
background: $primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
.danger-text {
|
.danger-text {
|
||||||
color: $danger-color !important;
|
color: $danger-color !important;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import Breadcrumb from "@/layout/compoenets/Breadcrumb.vue";
|
|||||||
import { useDebounceFn } from "@vueuse/core";
|
import { useDebounceFn } from "@vueuse/core";
|
||||||
import { clone } from "@/utils/clone";
|
import { clone } from "@/utils/clone";
|
||||||
import { Base64 } from "js-base64";
|
import { Base64 } from "js-base64";
|
||||||
|
import IconifyIconOffline from "@/components/IconifyIcon/src/iconifyIconOffline";
|
||||||
|
|
||||||
defineComponent({
|
defineComponent({
|
||||||
name: "Config"
|
name: "Config"
|
||||||
@ -116,9 +117,12 @@ const protocol = ref("frp://");
|
|||||||
|
|
||||||
const visibles = reactive({
|
const visibles = reactive({
|
||||||
copyServerConfig: false,
|
copyServerConfig: false,
|
||||||
pasteServerConfig: false
|
pasteServerConfig: false,
|
||||||
|
exportConfig: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const exportConfigType = ref("toml");
|
||||||
|
|
||||||
const handleSubmit = useDebounceFn(() => {
|
const handleSubmit = useDebounceFn(() => {
|
||||||
if (!formRef.value) return;
|
if (!formRef.value) return;
|
||||||
formRef.value.validate(valid => {
|
formRef.value.validate(valid => {
|
||||||
@ -195,6 +199,14 @@ onMounted(() => {
|
|||||||
checkAndResetVersion();
|
checkAndResetVersion();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
ipcRenderer.on("Config.exportConfig.hook", (event, args) => {
|
||||||
|
const { err, data } = args;
|
||||||
|
console.log(err, data, "export");
|
||||||
|
if (!err) {
|
||||||
|
const { configPath } = data;
|
||||||
|
ElMessageBox.alert(`配置路径:${configPath}`, `🎉 导出成功`);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSelectFile = (type: number, ext: string[]) => {
|
const handleSelectFile = (type: number, ext: string[]) => {
|
||||||
@ -286,15 +298,34 @@ const handlePasteServerConfigBase64 = useDebounceFn(() => {
|
|||||||
visibles.pasteServerConfig = false;
|
visibles.pasteServerConfig = false;
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
|
const handleShowExportDialog = () => {
|
||||||
|
visibles.exportConfig = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExportConfig = useDebounceFn(() => {
|
||||||
|
ipcRenderer.send("config.exportConfig", exportConfigType.value);
|
||||||
|
visibles.exportConfig = false;
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
const handleImportConfig = () => {};
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
ipcRenderer.removeAllListeners("Config.getConfig.hook");
|
ipcRenderer.removeAllListeners("Config.getConfig.hook");
|
||||||
ipcRenderer.removeAllListeners("Config.saveConfig.hook");
|
ipcRenderer.removeAllListeners("Config.saveConfig.hook");
|
||||||
ipcRenderer.removeAllListeners("Config.versions.hook");
|
ipcRenderer.removeAllListeners("Config.versions.hook");
|
||||||
|
ipcRenderer.removeAllListeners("Config.exportConfig.hook");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<breadcrumb />
|
<breadcrumb>
|
||||||
|
<el-button plain type="primary">
|
||||||
|
<IconifyIconOffline icon="uploadRounded" />
|
||||||
|
</el-button>
|
||||||
|
<el-button plain type="primary" @click="handleShowExportDialog">
|
||||||
|
<IconifyIconOffline icon="downloadRounded" />
|
||||||
|
</el-button>
|
||||||
|
</breadcrumb>
|
||||||
<div class="app-container-breadcrumb pr-2" v-loading="loading > 0">
|
<div class="app-container-breadcrumb pr-2" v-loading="loading > 0">
|
||||||
<div class="w-full bg-white p-4 rounded drop-shadow-lg">
|
<div class="w-full bg-white p-4 rounded drop-shadow-lg">
|
||||||
<el-form
|
<el-form
|
||||||
@ -643,8 +674,8 @@ onUnmounted(() => {
|
|||||||
class="ml-2"
|
class="ml-2"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleSelectFile(1, ['crt'])"
|
@click="handleSelectFile(1, ['crt'])"
|
||||||
>选择</el-button
|
>选择
|
||||||
>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
@ -682,8 +713,8 @@ onUnmounted(() => {
|
|||||||
class="ml-2"
|
class="ml-2"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleSelectFile(2, ['key'])"
|
@click="handleSelectFile(2, ['key'])"
|
||||||
>选择</el-button
|
>选择
|
||||||
>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
@ -721,8 +752,8 @@ onUnmounted(() => {
|
|||||||
class="ml-2"
|
class="ml-2"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleSelectFile(3, ['crt'])"
|
@click="handleSelectFile(3, ['crt'])"
|
||||||
>选择</el-button
|
>选择
|
||||||
>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
@ -896,7 +927,7 @@ onUnmounted(() => {
|
|||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 链接导入服务器 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="visibles.copyServerConfig"
|
v-model="visibles.copyServerConfig"
|
||||||
title="复制链接"
|
title="复制链接"
|
||||||
@ -916,7 +947,7 @@ onUnmounted(() => {
|
|||||||
:rows="8"
|
:rows="8"
|
||||||
></el-input>
|
></el-input>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<!-- 链接导出服务器-->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="visibles.pasteServerConfig"
|
v-model="visibles.pasteServerConfig"
|
||||||
title="导入链接"
|
title="导入链接"
|
||||||
@ -946,6 +977,39 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<!-- 配置导出-->
|
||||||
|
<el-dialog
|
||||||
|
v-model="visibles.exportConfig"
|
||||||
|
title="导出配置"
|
||||||
|
width="500"
|
||||||
|
top="5%"
|
||||||
|
>
|
||||||
|
<el-alert
|
||||||
|
class="mb-4"
|
||||||
|
:title="`导出文件名为 frpc-desktop.${exportConfigType} 重复导出则覆盖`"
|
||||||
|
type="warning"
|
||||||
|
:closable="false"
|
||||||
|
/>
|
||||||
|
<el-form>
|
||||||
|
<el-form-item label="导出类型">
|
||||||
|
<el-radio-group v-model="exportConfigType">
|
||||||
|
<el-radio-button label="toml" value="toml" />
|
||||||
|
<el-radio-button label="ini" value="ini" />
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button plain type="primary" @click="handleExportConfig">
|
||||||
|
<IconifyIconOffline
|
||||||
|
class="cursor-pointer mr-2"
|
||||||
|
icon="downloadRounded"
|
||||||
|
/>
|
||||||
|
导 出
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ onUnmounted(() => {
|
|||||||
<breadcrumb>
|
<breadcrumb>
|
||||||
<div class="h-full flex items-center justify-center">
|
<div class="h-full flex items-center justify-center">
|
||||||
<span class="text-sm font-bold">下载源: </span>
|
<span class="text-sm font-bold">下载源: </span>
|
||||||
<el-select class="w-24" v-model="currMirror">
|
<el-select class="w-40" v-model="currMirror">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="m in mirrors"
|
v-for="m in mirrors"
|
||||||
:label="m.name"
|
:label="m.name"
|
||||||
|
@ -300,12 +300,9 @@ onUnmounted(() => {
|
|||||||
<!-- <coming-soon />-->
|
<!-- <coming-soon />-->
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<breadcrumb>
|
<breadcrumb>
|
||||||
<div
|
<el-button class="mr-2" plain type="primary" @click="handleOpenInsert">
|
||||||
class="cursor-pointer h-[36px] w-[36px] bg-[#5f3bb0] rounded text-white flex justify-center items-center"
|
|
||||||
@click="handleOpenInsert"
|
|
||||||
>
|
|
||||||
<IconifyIconOffline icon="add" />
|
<IconifyIconOffline icon="add" />
|
||||||
</div>
|
</el-button>
|
||||||
</breadcrumb>
|
</breadcrumb>
|
||||||
<div class="app-container-breadcrumb pr-2" v-loading="loading.list > 0">
|
<div class="app-container-breadcrumb pr-2" v-loading="loading.list > 0">
|
||||||
<template v-if="proxys && proxys.length > 0">
|
<template v-if="proxys && proxys.length > 0">
|
||||||
|
Loading…
Reference in New Issue
Block a user