🍻 导入导出、清空配置

This commit is contained in:
刘嘉伟 2024-08-22 14:19:19 +08:00
parent 9df5cec23a
commit 3f49841ab2
9 changed files with 250 additions and 45 deletions

View File

@ -1,15 +1,16 @@
import {ipcMain, shell} from "electron";
import { app, ipcMain, shell } from "electron";
import log from "electron-log";
export const initCommonApi = () => {
// 打开链接
ipcMain.on("common.openUrl", async (event, args) => {
if (args) {
log.info(`打开链接:${args}`);
shell.openExternal(args).then(() => {});
}
});
// 打开链接
ipcMain.on("common.openUrl", async (event, args) => {
if (args) {
log.info(`打开链接:${args}`)
shell.openExternal(args).then(() => {
});
}
})
}
ipcMain.on("common.relaunch", () => {
app.relaunch();
});
};

View File

@ -1,15 +1,22 @@
import { app, dialog, ipcMain } from "electron";
import { getConfig, saveConfig } from "../storage/config";
import { listVersion } from "../storage/version";
import { generateConfig, genIniConfig, genTomlConfig } from "./frpc";
import { clearConfig, getConfig, saveConfig } from "../storage/config";
import { clearVersion, listVersion } from "../storage/version";
import {
generateConfig,
genIniConfig,
genTomlConfig,
stopFrpcProcess
} from "./frpc";
import { exec } from "child_process";
import { listProxy } from "../storage/proxy";
import { clearProxy, insertProxy, listProxy } from "../storage/proxy";
import path from "path";
import fs from "fs";
const log = require("electron-log");
const toml = require("@iarna/toml");
const { v4: uuidv4 } = require("uuid");
export const initConfigApi = () => {
export const initConfigApi = win => {
ipcMain.on("config.saveConfig", async (event, args) => {
saveConfig(args, (err, numberOfUpdated, upsert) => {
if (!err) {
@ -60,6 +67,10 @@ export const initConfigApi = () => {
properties: ["openDirectory"]
});
const outputDirectory = result.filePaths[0];
if (!outputDirectory) {
// 取消了
return;
}
log.info(`导出目录 ${outputDirectory} 类型:${args}`);
getConfig((err1, config) => {
if (!err1 && config) {
@ -99,4 +110,135 @@ export const initConfigApi = () => {
}
});
});
const parseTomlConfig = (tomlPath: string) => {
const importConfigPath = tomlPath;
const tomlData = fs.readFileSync(importConfigPath, "utf-8");
log.info(`读取到配置内容 ${tomlData}`);
const sourceConfig = toml.parse(tomlData);
// log.info(`解析结果 ${sourceConfig}`);
// console.log(sourceConfig, "frpcConfig");
// 解析config
const targetConfig: FrpConfig = {
currentVersion: null,
serverAddr: sourceConfig.serverAddr || "",
serverPort: sourceConfig.serverPort || "",
authMethod: sourceConfig?.user
? "multiuser"
: sourceConfig?.auth?.method || "",
authToken: sourceConfig?.auth?.token || "",
transportHeartbeatInterval:
sourceConfig?.transport?.heartbeatInterval || 30,
transportHeartbeatTimeout:
sourceConfig?.transport?.heartbeatTimeout || 90,
tlsConfigEnable: sourceConfig?.transport?.tls?.enable || false,
tlsConfigCertFile: sourceConfig?.transport?.tls?.certFile || "",
tlsConfigKeyFile: sourceConfig?.transport?.tls?.keyFile || "",
tlsConfigServerName: sourceConfig?.transport?.tls?.serverName || "",
tlsConfigTrustedCaFile: sourceConfig?.transport?.tls?.trustedCaFile || "",
logLevel: sourceConfig?.log?.level || "info",
logMaxDays: sourceConfig?.log?.maxDays || 3,
proxyConfigProxyUrl: sourceConfig?.transport?.proxyURL || "",
proxyConfigEnable: Boolean(sourceConfig?.transport?.proxyURL) || false,
user: sourceConfig?.user || "",
metaToken: sourceConfig?.metadatas?.token || "",
systemSelfStart: false,
systemStartupConnect: false
};
let frpcProxys = [];
// 解析proxy
if (sourceConfig?.proxies && sourceConfig.proxies.length > 0) {
const frpcProxys1 = sourceConfig.proxies.map(m => {
const rm: Proxy = {
_id: uuidv4(),
name: m?.name,
type: m?.type,
localIp: m?.localIP || "",
localPort: m?.localPort || null,
remotePort: m?.remotePort || null,
customDomains: m?.customDomains || [],
// 以下为stcp参数
stcpModel: "visited",
serverName: "",
secretKey: m?.secretKey || "",
bindAddr: "",
bindPort: null
};
return rm;
});
frpcProxys = [...frpcProxys, ...frpcProxys1];
}
// 解析stcp的访问者
if (sourceConfig?.visitors && sourceConfig.visitors.length > 0) {
const frpcProxys2 = sourceConfig.visitors.map(m => {
const rm: Proxy = {
_id: uuidv4(),
name: m?.name,
type: m?.type,
localIp: "",
localPort: null,
remotePort: null,
customDomains: [],
// 以下为stcp参数
stcpModel: "visitors",
serverName: m?.serverName,
secretKey: m?.secretKey || "",
bindAddr: m?.bindAddr,
bindPort: m?.bindPort
};
return rm;
});
frpcProxys = [...frpcProxys, ...frpcProxys2];
}
if (targetConfig) {
clearConfig(() => {
saveConfig(targetConfig);
});
}
if (frpcProxys && frpcProxys.length > 0) {
clearProxy(() => {
frpcProxys.forEach(f => {
insertProxy(f, err => {
console.log("插入", f, err);
});
});
});
}
};
ipcMain.on("config.importConfig", async (event, args) => {
const result = await dialog.showOpenDialog(win, {
properties: ["openFile"],
filters: [
{ name: "FrpcConfig Files", extensions: ["toml", "ini"] } // 允许选择的文件类型
]
});
if (result.canceled) {
return;
} else {
const filePath = result.filePaths[0];
const fileExtension = path.extname(filePath); // 获取文件后缀名
log.info(`导入文件 ${filePath} ${fileExtension}`);
if (fileExtension === ".toml") {
parseTomlConfig(filePath);
event.reply("Config.importConfig.hook", {
success: true
});
} else {
event.reply("Config.importConfig.hook", {
success: false,
data: `暂不支持${fileExtension}格式文件`
});
}
}
});
ipcMain.on("config.clearAll", async (event, args) => {
stopFrpcProcess(() => {
clearConfig();
clearProxy();
clearVersion();
event.reply("Config.clearAll.hook", {});
});
});
};

View File

@ -166,7 +166,7 @@ app.whenReady().then(() => {
createTray()
// 初始化各个API
initGitHubApi();
initConfigApi();
initConfigApi(win);
initProxyApi();
initFrpcApi();
initLoggerApi();

View File

@ -30,3 +30,7 @@ export const getConfig = (
) => {
configDB.findOne({ _id: "1" }, cb);
};
export const clearConfig = (cb?: (err: Error | null, n: number) => void) => {
configDB.remove({}, { multi: true }, cb);
};

View File

@ -67,3 +67,7 @@ export const getProxyById = (
) => {
proxyDB.findOne({ _id: id }, callback);
};
export const clearProxy = (cb?: (err: Error | null, n: number) => void) => {
proxyDB.remove({}, { multi: true }, cb);
};

View File

@ -1,11 +1,12 @@
import Datastore from "nedb";
import path from "path";
import {app} from "electron";
const log = require('electron-log');
import { app } from "electron";
const log = require("electron-log");
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,11 +15,11 @@ const versionDB = new Datastore({
* @param cb
*/
export const insertVersion = (
version: FrpVersion,
cb?: (err: Error | null, document: any) => void
version: FrpVersion,
cb?: (err: Error | null, document: any) => void
) => {
log.debug(`新增版本:${JSON.stringify(version)}`);
versionDB.insert(version, cb);
log.debug(`新增版本:${JSON.stringify(version)}`);
versionDB.insert(version, cb);
};
/**
@ -26,19 +27,26 @@ export const insertVersion = (
* @param cb
*/
export const listVersion = (
callback: (err: Error | null, documents: FrpVersion[]) => void
callback: (err: Error | null, documents: FrpVersion[]) => void
) => {
versionDB.find({}, callback);
versionDB.find({}, callback);
};
export const getVersionById = (
id: number,
callback: (err: Error | null, document: FrpVersion) => void
id: number,
callback: (err: Error | null, document: FrpVersion) => void
) => {
versionDB.findOne({id: id}, callback);
versionDB.findOne({ id: id }, callback);
};
export const deleteVersionById = (id: string, callback: (err: Error | null, document: any) => void) => {
log.debug(`删除版本:${id}`);
versionDB.remove({id: id}, callback);
}
export const deleteVersionById = (
id: string,
callback: (err: Error | null, document: any) => void
) => {
log.debug(`删除版本:${id}`);
versionDB.remove({ id: id }, callback);
};
export const clearVersion = (cb?: (err: Error | null, n: number) => void) => {
versionDB.remove({}, { multi: true }, cb);
};

View File

@ -65,9 +65,11 @@
"vue": "^3.3.4",
"vue-router": "^4.2.4",
"vue-tsc": "^2.0.22",
"vue-types": "^5.1.1"
"vue-types": "^5.1.1",
"uuid": "^10.0.0"
},
"dependencies": {
"@iarna/toml": "^2.2.5",
"adm-zip": "^0.5.14",
"animate.css": "^4.1.1",
"electron-dl": "^3.5.1",

View File

@ -32,6 +32,7 @@ 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";
import deviceReset from "@iconify-icons/material-symbols/device-reset";
addIcon("cloud", Cloud);
addIcon("rocket-launch-rounded", RocketLaunchRounded);
@ -59,5 +60,6 @@ addIcon("check-box", CheckBox);
addIcon("export", ExportNotesOutline);
addIcon("uploadRounded", uploadRounded);
addIcon("downloadRounded", downloadRounded);
addIcon("deviceReset", deviceReset);

View File

@ -207,6 +207,29 @@ onMounted(() => {
ElMessageBox.alert(`配置路径:${configPath}`, `🎉 导出成功`);
}
});
ipcRenderer.on("Config.clearAll.hook", (event, args) => {
ElMessageBox.alert("重置成功 请重启软件", `重置`, {
closeOnClickModal: false,
showClose: false,
confirmButtonText: "立即重启"
}).then(() => {
ipcRenderer.send("common.relaunch");
});
});
ipcRenderer.on("Config.importConfig.hook", (event, args) => {
const { success, data } = args;
if (success) {
ElMessageBox.alert("导入成功 请重启软件", `导入成功`, {
closeOnClickModal: false,
showClose: false,
confirmButtonText: "立即重启"
}).then(() => {
ipcRenderer.send("common.relaunch");
});
} else {
ElMessageBox.alert(data, `导入失败`);
}
});
});
const handleSelectFile = (type: number, ext: string[]) => {
@ -307,24 +330,43 @@ const handleExportConfig = useDebounceFn(() => {
visibles.exportConfig = false;
}, 300);
const handleImportConfig = () => {};
const handleImportConfig = () => {
ipcRenderer.send("config.importConfig");
};
const handleResetConfig = () => {
ElMessageBox.alert("是否清空所有配置?", "提示", {
showCancelButton: true,
cancelButtonText: "取消",
confirmButtonText: "清空"
}).then(() => {
ipcRenderer.send("config.clearAll");
});
};
onUnmounted(() => {
ipcRenderer.removeAllListeners("Config.getConfig.hook");
ipcRenderer.removeAllListeners("Config.saveConfig.hook");
ipcRenderer.removeAllListeners("Config.versions.hook");
ipcRenderer.removeAllListeners("Config.exportConfig.hook");
ipcRenderer.removeAllListeners("Config.clearAll.hook");
});
</script>
<template>
<div class="main">
<breadcrumb>
<el-button plain type="primary">
<el-button plain type="primary" @click="handleResetConfig">
<IconifyIconOffline icon="deviceReset" />
</el-button>
<el-button plain type="primary" @click="handleImportConfig">
<IconifyIconOffline icon="uploadRounded" />
</el-button>
<el-button plain type="primary" @click="handleShowExportDialog">
<IconifyIconOffline icon="downloadRounded" />
</el-button>
<el-button type="primary" @click="handleSubmit">
<IconifyIconOffline icon="save-rounded" />
</el-button>
</breadcrumb>
<div class="app-container-breadcrumb pr-2" v-loading="loading > 0">
<div class="w-full bg-white p-4 rounded drop-shadow-lg">
@ -915,14 +957,14 @@ onUnmounted(() => {
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item>
<el-button plain type="primary" @click="handleSubmit">
<IconifyIconOffline icon="save" />
</el-button>
</el-form-item>
</el-col>
<!-- <el-col :span="24">-->
<!-- <el-form-item>-->
<!-- <el-button plain type="primary" @click="handleSubmit">-->
<!-- <IconifyIconOffline icon="save" />-->
<!-- -->
<!-- </el-button>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
</el-row>
</el-form>
</div>