🏗️ refactor data access layer and enhance service architecture
This commit is contained in:
parent
ff8b01c360
commit
9946b50d5d
@ -1,337 +1,337 @@
|
||||
import { app, dialog, ipcMain, shell } from "electron";
|
||||
import { clearConfig, getConfig, saveConfig } from "../storage/config";
|
||||
import { clearVersion, listVersion } from "../storage/version";
|
||||
import { genIniConfig, genTomlConfig, stopFrpcProcess } from "./frpc";
|
||||
import { clearProxy, insertProxy, listProxy } from "../storage/proxy";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { logDebug, logError, logInfo, LogModule, logWarn } from "../utils/log";
|
||||
|
||||
const toml = require("@iarna/toml");
|
||||
const { v4: uuidv4 } = require("uuid");
|
||||
|
||||
export const initConfigApi = win => {
|
||||
ipcMain.on("config.saveConfig", async (event, args) => {
|
||||
logInfo(LogModule.APP, "Attempting to save configuration.");
|
||||
saveConfig(args, (err, numberOfUpdated, upsert) => {
|
||||
if (!err) {
|
||||
const start = args.systemSelfStart || false;
|
||||
logDebug(LogModule.APP, "Startup status set to: " + start);
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: start, //win
|
||||
openAsHidden: start //macOs
|
||||
});
|
||||
logInfo(LogModule.APP, "Configuration saved successfully.");
|
||||
} else {
|
||||
logError(LogModule.APP, `Error saving configuration: ${err}`);
|
||||
}
|
||||
event.reply("Config.saveConfig.hook", {
|
||||
err: err,
|
||||
numberOfUpdated: numberOfUpdated,
|
||||
upsert: upsert
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on("config.getConfig", async (event, args) => {
|
||||
logInfo(LogModule.APP, "Requesting configuration.");
|
||||
getConfig((err, doc) => {
|
||||
if (err) {
|
||||
logError(LogModule.APP, `Error retrieving configuration: ${err}`);
|
||||
}
|
||||
event.reply("Config.getConfig.hook", {
|
||||
err: err,
|
||||
data: doc
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on("config.versions", event => {
|
||||
logInfo(LogModule.APP, "Requesting version information.");
|
||||
listVersion((err, doc) => {
|
||||
if (err) {
|
||||
logError(LogModule.APP, `Error retrieving version information: ${err}`);
|
||||
}
|
||||
event.reply("Config.versions.hook", {
|
||||
err: err,
|
||||
data: doc
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on("config.hasConfig", event => {
|
||||
logInfo(LogModule.APP, "Checking if configuration exists.");
|
||||
getConfig((err, doc) => {
|
||||
if (err) {
|
||||
logError(LogModule.APP, `Error checking configuration: ${err}`);
|
||||
}
|
||||
event.reply("Config.getConfig.hook", {
|
||||
err: err,
|
||||
data: doc
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on("config.exportConfig", async (event, args) => {
|
||||
logInfo(LogModule.APP, "Attempting to export configuration.");
|
||||
const result = await dialog.showOpenDialog({
|
||||
properties: ["openDirectory"]
|
||||
});
|
||||
const outputDirectory = result.filePaths[0];
|
||||
if (!outputDirectory) {
|
||||
logWarn(LogModule.APP, "Export canceled by user.");
|
||||
return;
|
||||
}
|
||||
|
||||
logInfo(
|
||||
LogModule.APP,
|
||||
`Exporting configuration to directory ${outputDirectory} with type: ${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);
|
||||
}
|
||||
const configPath = path.join(
|
||||
outputDirectory,
|
||||
`frpc-desktop.${args}`
|
||||
);
|
||||
fs.writeFile(
|
||||
configPath, // 配置文件目录
|
||||
configContent, // 配置文件内容
|
||||
{ flag: "w" },
|
||||
err => {
|
||||
if (err) {
|
||||
logError(
|
||||
LogModule.APP,
|
||||
`Error writing configuration file: ${err}`
|
||||
);
|
||||
event.reply("config.exportConfig.hook", {
|
||||
data: "导出错误",
|
||||
err: err
|
||||
});
|
||||
} else {
|
||||
logInfo(
|
||||
LogModule.APP,
|
||||
"Configuration exported successfully."
|
||||
);
|
||||
event.reply("Config.exportConfig.hook", {
|
||||
data: {
|
||||
configPath: configPath
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
logError(LogModule.APP, `Error listing proxies: ${err2}`);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
logError(LogModule.APP, `Error retrieving configuration: ${err1}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const parseTomlConfig = (tomlPath: string) => {
|
||||
logInfo(LogModule.APP, `Parsing TOML configuration from ${tomlPath}`);
|
||||
const importConfigPath = tomlPath;
|
||||
const tomlData = fs.readFileSync(importConfigPath, "utf-8");
|
||||
logInfo(LogModule.APP, "Configuration content read successfully.");
|
||||
const sourceConfig = toml.parse(tomlData);
|
||||
// 解析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,
|
||||
systemSilentStartup: false,
|
||||
webEnable: true,
|
||||
webPort: sourceConfig?.webServer?.port || 57400,
|
||||
transportProtocol: sourceConfig?.transport?.protocol || "tcp",
|
||||
transportDialServerTimeout:
|
||||
sourceConfig?.transport?.dialServerTimeout || 10,
|
||||
transportDialServerKeepalive:
|
||||
sourceConfig?.transport?.dialServerKeepalive || 70,
|
||||
transportPoolCount: sourceConfig?.transport?.poolCount || 0,
|
||||
transportTcpMux: sourceConfig?.transport?.tcpMux || true,
|
||||
transportTcpMuxKeepaliveInterval:
|
||||
sourceConfig?.transport?.tcpMuxKeepaliveInterval || 7200
|
||||
};
|
||||
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 || [],
|
||||
subdomain: m.subdomain || "",
|
||||
basicAuth: m.basicAuth || false,
|
||||
httpUser: m.httpUser || "",
|
||||
httpPassword: m.httpPassword || "",
|
||||
// 以下为stcp参数
|
||||
stcpModel: "visited",
|
||||
serverName: "",
|
||||
secretKey: m?.secretKey || "",
|
||||
bindAddr: "",
|
||||
bindPort: null,
|
||||
status: m?.status || true,
|
||||
fallbackTo: m?.fallbackTo,
|
||||
fallbackTimeoutMs: m?.fallbackTimeoutMs || 500,
|
||||
keepTunnelOpen: m?.keepTunnelOpen || false,
|
||||
https2http: m?.https2http || false,
|
||||
https2httpCaFile: m?.https2httpCaFile || "",
|
||||
https2httpKeyFile: m?.https2httpKeyFile || ""
|
||||
};
|
||||
return rm;
|
||||
});
|
||||
frpcProxys = [...frpcProxys, ...frpcProxys1];
|
||||
logInfo(LogModule.APP, "Parsed proxies from configuration.");
|
||||
}
|
||||
// 解析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: [],
|
||||
subdomain: m.subdomain || "",
|
||||
basicAuth: m.basicAuth || false,
|
||||
httpUser: m.httpUser || "",
|
||||
httpPassword: m.httpPassword || "",
|
||||
// 以下为stcp参数
|
||||
stcpModel: "visitors",
|
||||
serverName: m?.serverName,
|
||||
secretKey: m?.secretKey || "",
|
||||
bindAddr: m?.bindAddr,
|
||||
bindPort: m?.bindPort,
|
||||
status: m?.status || true,
|
||||
fallbackTo: m?.fallbackTo,
|
||||
fallbackTimeoutMs: m?.fallbackTimeoutMs || 500,
|
||||
keepTunnelOpen: m?.keepTunnelOpen || false,
|
||||
https2http: m?.https2http || false,
|
||||
https2httpCaFile: m?.https2httpCaFile || "",
|
||||
https2httpKeyFile: m?.https2httpKeyFile || ""
|
||||
};
|
||||
return rm;
|
||||
});
|
||||
frpcProxys = [...frpcProxys, ...frpcProxys2];
|
||||
logInfo(LogModule.APP, "Parsed visitors from configuration.");
|
||||
}
|
||||
if (targetConfig) {
|
||||
clearConfig(() => {
|
||||
logInfo(LogModule.APP, "Clearing existing configuration.");
|
||||
saveConfig(targetConfig);
|
||||
logInfo(LogModule.APP, "New configuration saved.");
|
||||
});
|
||||
}
|
||||
if (frpcProxys && frpcProxys.length > 0) {
|
||||
clearProxy(() => {
|
||||
frpcProxys.forEach(f => {
|
||||
insertProxy(f, err => {
|
||||
if (err) {
|
||||
logError(LogModule.APP, `Error inserting proxy: ${err}`);
|
||||
} else {
|
||||
logInfo(LogModule.APP, `Inserted proxy: ${JSON.stringify(f)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ipcMain.on("config.importConfig", async (event, args) => {
|
||||
logInfo(LogModule.APP, "Attempting to import configuration.");
|
||||
const result = await dialog.showOpenDialog(win, {
|
||||
properties: ["openFile"],
|
||||
filters: [
|
||||
{ name: "FrpcConfig Files", extensions: ["toml", "ini"] } // 允许选择的文件类型
|
||||
]
|
||||
});
|
||||
if (result.canceled) {
|
||||
logWarn(LogModule.APP, "Import canceled by user.");
|
||||
return;
|
||||
} else {
|
||||
const filePath = result.filePaths[0];
|
||||
const fileExtension = path.extname(filePath); // 获取文件后缀名
|
||||
logWarn(
|
||||
LogModule.APP,
|
||||
`Importing file ${filePath} with extension ${fileExtension}`
|
||||
);
|
||||
if (fileExtension === ".toml") {
|
||||
parseTomlConfig(filePath);
|
||||
event.reply("Config.importConfig.hook", {
|
||||
success: true
|
||||
});
|
||||
} else {
|
||||
logError(
|
||||
LogModule.APP,
|
||||
`Import failed, unsupported file format: ${fileExtension}`
|
||||
);
|
||||
event.reply("Config.importConfig.hook", {
|
||||
success: false,
|
||||
data: `导入失败,暂不支持 ${fileExtension} 格式文件`
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on("config.clearAll", async (event, args) => {
|
||||
logInfo(LogModule.APP, "Clearing all configurations.");
|
||||
stopFrpcProcess(() => {
|
||||
clearConfig();
|
||||
clearProxy();
|
||||
clearVersion();
|
||||
event.reply("Config.clearAll.hook", {});
|
||||
logInfo(LogModule.APP, "All configurations cleared.");
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on("config.openDataFolder", async (event, args) => {
|
||||
const userDataPath = app.getPath("userData");
|
||||
logInfo(LogModule.APP, "Attempting to open data folder.");
|
||||
shell.openPath(userDataPath).then(errorMessage => {
|
||||
if (errorMessage) {
|
||||
logError(LogModule.APP, `Failed to open data folder: ${errorMessage}`);
|
||||
event.reply("Config.openDataFolder.hook", false);
|
||||
} else {
|
||||
logInfo(LogModule.APP, "Data folder opened successfully.");
|
||||
event.reply("Config.openDataFolder.hook", true);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
// import { app, dialog, ipcMain, shell } from "electron";
|
||||
// import { clearConfig, getConfig, saveConfig } from "../storage/config";
|
||||
// import { clearVersion, listVersion } from "../storage/version";
|
||||
// import { genIniConfig, genTomlConfig, stopFrpcProcess } from "./frpc";
|
||||
// import { clearProxy, insertProxy, listProxy } from "../storage/proxy";
|
||||
// import path from "path";
|
||||
// import fs from "fs";
|
||||
// import { logDebug, logError, logInfo, LogModule, logWarn } from "../utils/log";
|
||||
//
|
||||
// const toml = require("@iarna/toml");
|
||||
// const { v4: uuidv4 } = require("uuid");
|
||||
//
|
||||
// export const initConfigApi = win => {
|
||||
// ipcMain.on("config.saveConfig", async (event, args) => {
|
||||
// logInfo(LogModule.APP, "Attempting to save configuration.");
|
||||
// saveConfig(args, (err, numberOfUpdated, upsert) => {
|
||||
// if (!err) {
|
||||
// const start = args.systemSelfStart || false;
|
||||
// logDebug(LogModule.APP, "Startup status set to: " + start);
|
||||
// app.setLoginItemSettings({
|
||||
// openAtLogin: start, //win
|
||||
// openAsHidden: start //macOs
|
||||
// });
|
||||
// logInfo(LogModule.APP, "Configuration saved successfully.");
|
||||
// } else {
|
||||
// logError(LogModule.APP, `Error saving configuration: ${err}`);
|
||||
// }
|
||||
// event.reply("Config.saveConfig.hook", {
|
||||
// err: err,
|
||||
// numberOfUpdated: numberOfUpdated,
|
||||
// upsert: upsert
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// ipcMain.on("config.getConfig", async (event, args) => {
|
||||
// logInfo(LogModule.APP, "Requesting configuration.");
|
||||
// getConfig((err, doc) => {
|
||||
// if (err) {
|
||||
// logError(LogModule.APP, `Error retrieving configuration: ${err}`);
|
||||
// }
|
||||
// event.reply("Config.getConfig.hook", {
|
||||
// err: err,
|
||||
// data: doc
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// ipcMain.on("config.versions", event => {
|
||||
// logInfo(LogModule.APP, "Requesting version information.");
|
||||
// listVersion((err, doc) => {
|
||||
// if (err) {
|
||||
// logError(LogModule.APP, `Error retrieving version information: ${err}`);
|
||||
// }
|
||||
// event.reply("Config.versions.hook", {
|
||||
// err: err,
|
||||
// data: doc
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// ipcMain.on("config.hasConfig", event => {
|
||||
// logInfo(LogModule.APP, "Checking if configuration exists.");
|
||||
// getConfig((err, doc) => {
|
||||
// if (err) {
|
||||
// logError(LogModule.APP, `Error checking configuration: ${err}`);
|
||||
// }
|
||||
// event.reply("Config.getConfig.hook", {
|
||||
// err: err,
|
||||
// data: doc
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// ipcMain.on("config.exportConfig", async (event, args) => {
|
||||
// logInfo(LogModule.APP, "Attempting to export configuration.");
|
||||
// const result = await dialog.showOpenDialog({
|
||||
// properties: ["openDirectory"]
|
||||
// });
|
||||
// const outputDirectory = result.filePaths[0];
|
||||
// if (!outputDirectory) {
|
||||
// logWarn(LogModule.APP, "Export canceled by user.");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// logInfo(
|
||||
// LogModule.APP,
|
||||
// `Exporting configuration to directory ${outputDirectory} with type: ${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);
|
||||
// }
|
||||
// const configPath = path.join(
|
||||
// outputDirectory,
|
||||
// `frpc-desktop.${args}`
|
||||
// );
|
||||
// fs.writeFile(
|
||||
// configPath, // 配置文件目录
|
||||
// configContent, // 配置文件内容
|
||||
// { flag: "w" },
|
||||
// err => {
|
||||
// if (err) {
|
||||
// logError(
|
||||
// LogModule.APP,
|
||||
// `Error writing configuration file: ${err}`
|
||||
// );
|
||||
// event.reply("config.exportConfig.hook", {
|
||||
// data: "导出错误",
|
||||
// err: err
|
||||
// });
|
||||
// } else {
|
||||
// logInfo(
|
||||
// LogModule.APP,
|
||||
// "Configuration exported successfully."
|
||||
// );
|
||||
// event.reply("Config.exportConfig.hook", {
|
||||
// data: {
|
||||
// configPath: configPath
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// } else {
|
||||
// logError(LogModule.APP, `Error listing proxies: ${err2}`);
|
||||
// }
|
||||
// });
|
||||
// } else {
|
||||
// logError(LogModule.APP, `Error retrieving configuration: ${err1}`);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// const parseTomlConfig = (tomlPath: string) => {
|
||||
// logInfo(LogModule.APP, `Parsing TOML configuration from ${tomlPath}`);
|
||||
// const importConfigPath = tomlPath;
|
||||
// const tomlData = fs.readFileSync(importConfigPath, "utf-8");
|
||||
// logInfo(LogModule.APP, "Configuration content read successfully.");
|
||||
// const sourceConfig = toml.parse(tomlData);
|
||||
// // 解析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,
|
||||
// systemSilentStartup: false,
|
||||
// webEnable: true,
|
||||
// webPort: sourceConfig?.webServer?.port || 57400,
|
||||
// transportProtocol: sourceConfig?.transport?.protocol || "tcp",
|
||||
// transportDialServerTimeout:
|
||||
// sourceConfig?.transport?.dialServerTimeout || 10,
|
||||
// transportDialServerKeepalive:
|
||||
// sourceConfig?.transport?.dialServerKeepalive || 70,
|
||||
// transportPoolCount: sourceConfig?.transport?.poolCount || 0,
|
||||
// transportTcpMux: sourceConfig?.transport?.tcpMux || true,
|
||||
// transportTcpMuxKeepaliveInterval:
|
||||
// sourceConfig?.transport?.tcpMuxKeepaliveInterval || 7200
|
||||
// };
|
||||
// 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 || [],
|
||||
// subdomain: m.subdomain || "",
|
||||
// basicAuth: m.basicAuth || false,
|
||||
// httpUser: m.httpUser || "",
|
||||
// httpPassword: m.httpPassword || "",
|
||||
// // 以下为stcp参数
|
||||
// stcpModel: "visited",
|
||||
// serverName: "",
|
||||
// secretKey: m?.secretKey || "",
|
||||
// bindAddr: "",
|
||||
// bindPort: null,
|
||||
// status: m?.status || true,
|
||||
// fallbackTo: m?.fallbackTo,
|
||||
// fallbackTimeoutMs: m?.fallbackTimeoutMs || 500,
|
||||
// keepTunnelOpen: m?.keepTunnelOpen || false,
|
||||
// https2http: m?.https2http || false,
|
||||
// https2httpCaFile: m?.https2httpCaFile || "",
|
||||
// https2httpKeyFile: m?.https2httpKeyFile || ""
|
||||
// };
|
||||
// return rm;
|
||||
// });
|
||||
// frpcProxys = [...frpcProxys, ...frpcProxys1];
|
||||
// logInfo(LogModule.APP, "Parsed proxies from configuration.");
|
||||
// }
|
||||
// // 解析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: [],
|
||||
// subdomain: m.subdomain || "",
|
||||
// basicAuth: m.basicAuth || false,
|
||||
// httpUser: m.httpUser || "",
|
||||
// httpPassword: m.httpPassword || "",
|
||||
// // 以下为stcp参数
|
||||
// stcpModel: "visitors",
|
||||
// serverName: m?.serverName,
|
||||
// secretKey: m?.secretKey || "",
|
||||
// bindAddr: m?.bindAddr,
|
||||
// bindPort: m?.bindPort,
|
||||
// status: m?.status || true,
|
||||
// fallbackTo: m?.fallbackTo,
|
||||
// fallbackTimeoutMs: m?.fallbackTimeoutMs || 500,
|
||||
// keepTunnelOpen: m?.keepTunnelOpen || false,
|
||||
// https2http: m?.https2http || false,
|
||||
// https2httpCaFile: m?.https2httpCaFile || "",
|
||||
// https2httpKeyFile: m?.https2httpKeyFile || ""
|
||||
// };
|
||||
// return rm;
|
||||
// });
|
||||
// frpcProxys = [...frpcProxys, ...frpcProxys2];
|
||||
// logInfo(LogModule.APP, "Parsed visitors from configuration.");
|
||||
// }
|
||||
// if (targetConfig) {
|
||||
// clearConfig(() => {
|
||||
// logInfo(LogModule.APP, "Clearing existing configuration.");
|
||||
// saveConfig(targetConfig);
|
||||
// logInfo(LogModule.APP, "New configuration saved.");
|
||||
// });
|
||||
// }
|
||||
// if (frpcProxys && frpcProxys.length > 0) {
|
||||
// clearProxy(() => {
|
||||
// frpcProxys.forEach(f => {
|
||||
// insertProxy(f, err => {
|
||||
// if (err) {
|
||||
// logError(LogModule.APP, `Error inserting proxy: ${err}`);
|
||||
// } else {
|
||||
// logInfo(LogModule.APP, `Inserted proxy: ${JSON.stringify(f)}`);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// ipcMain.on("config.importConfig", async (event, args) => {
|
||||
// logInfo(LogModule.APP, "Attempting to import configuration.");
|
||||
// const result = await dialog.showOpenDialog(win, {
|
||||
// properties: ["openFile"],
|
||||
// filters: [
|
||||
// { name: "FrpcConfig Files", extensions: ["toml", "ini"] } // 允许选择的文件类型
|
||||
// ]
|
||||
// });
|
||||
// if (result.canceled) {
|
||||
// logWarn(LogModule.APP, "Import canceled by user.");
|
||||
// return;
|
||||
// } else {
|
||||
// const filePath = result.filePaths[0];
|
||||
// const fileExtension = path.extname(filePath); // 获取文件后缀名
|
||||
// logWarn(
|
||||
// LogModule.APP,
|
||||
// `Importing file ${filePath} with extension ${fileExtension}`
|
||||
// );
|
||||
// if (fileExtension === ".toml") {
|
||||
// parseTomlConfig(filePath);
|
||||
// event.reply("Config.importConfig.hook", {
|
||||
// success: true
|
||||
// });
|
||||
// } else {
|
||||
// logError(
|
||||
// LogModule.APP,
|
||||
// `Import failed, unsupported file format: ${fileExtension}`
|
||||
// );
|
||||
// event.reply("Config.importConfig.hook", {
|
||||
// success: false,
|
||||
// data: `导入失败,暂不支持 ${fileExtension} 格式文件`
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// ipcMain.on("config.clearAll", async (event, args) => {
|
||||
// logInfo(LogModule.APP, "Clearing all configurations.");
|
||||
// stopFrpcProcess(() => {
|
||||
// clearConfig();
|
||||
// clearProxy();
|
||||
// clearVersion();
|
||||
// event.reply("Config.clearAll.hook", {});
|
||||
// logInfo(LogModule.APP, "All configurations cleared.");
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// ipcMain.on("config.openDataFolder", async (event, args) => {
|
||||
// const userDataPath = app.getPath("userData");
|
||||
// logInfo(LogModule.APP, "Attempting to open data folder.");
|
||||
// shell.openPath(userDataPath).then(errorMessage => {
|
||||
// if (errorMessage) {
|
||||
// logError(LogModule.APP, `Failed to open data folder: ${errorMessage}`);
|
||||
// event.reply("Config.openDataFolder.hook", false);
|
||||
// } else {
|
||||
// logInfo(LogModule.APP, "Data folder opened successfully.");
|
||||
// event.reply("Config.openDataFolder.hook", true);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// };
|
||||
|
@ -1,21 +1,21 @@
|
||||
import {dialog, ipcMain} from "electron";
|
||||
import { logInfo, logError, LogModule } from "../utils/log";
|
||||
|
||||
export const initFileApi = () => {
|
||||
ipcMain.handle("file.selectFile", async (event, args) => {
|
||||
logInfo(LogModule.APP, `Attempting to open file dialog with filters: ${JSON.stringify(args)}`);
|
||||
try {
|
||||
const result = dialog.showOpenDialogSync({
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: 'Text Files', extensions: args },
|
||||
]
|
||||
});
|
||||
logInfo(LogModule.APP, `File dialog result: ${JSON.stringify(result)}`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logError(LogModule.APP, `Error opening file dialog: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
// import {dialog, ipcMain} from "electron";
|
||||
// import { logInfo, logError, LogModule } from "../utils/log";
|
||||
//
|
||||
// export const initFileApi = () => {
|
||||
// ipcMain.handle("file.selectFile", async (event, args) => {
|
||||
// logInfo(LogModule.APP, `Attempting to open file dialog with filters: ${JSON.stringify(args)}`);
|
||||
// try {
|
||||
// const result = dialog.showOpenDialogSync({
|
||||
// properties: ['openFile'],
|
||||
// filters: [
|
||||
// { name: 'Text Files', extensions: args },
|
||||
// ]
|
||||
// });
|
||||
// logInfo(LogModule.APP, `File dialog result: ${JSON.stringify(result)}`);
|
||||
// return result;
|
||||
// } catch (error) {
|
||||
// logError(LogModule.APP, `Error opening file dialog: ${error.message}`);
|
||||
// return null;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
1444
electron/api/frpc.ts
1444
electron/api/frpc.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,81 +1,81 @@
|
||||
import {app, dialog, autoUpdater, BrowserWindow} from "electron";
|
||||
|
||||
const log = require('electron-log');
|
||||
|
||||
|
||||
export const initUpdaterApi = (win: BrowserWindow) => {
|
||||
//更新测试打开
|
||||
Object.defineProperty(app, 'isPackaged', {
|
||||
get() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
const server = 'https://hazel-git-master-uiluck.vercel.app'
|
||||
let packageName = null
|
||||
const platform = process.platform;
|
||||
const arch = process.arch;
|
||||
switch (platform) {
|
||||
case "darwin":
|
||||
if (arch == "arm64") {
|
||||
packageName = "darwin_arm64";
|
||||
} else {
|
||||
packageName = "darwin";
|
||||
}
|
||||
break;
|
||||
case "win32":
|
||||
packageName = "exe";
|
||||
break;
|
||||
case "linux":
|
||||
packageName = "AppImage";
|
||||
if (arch == "arm64") {
|
||||
packageName = "AppImage_arm64";
|
||||
} else {
|
||||
packageName = "AppImage";
|
||||
}
|
||||
break;
|
||||
}
|
||||
const url = `${server}/update/${packageName}/${app.getVersion()}`
|
||||
log.info(`开启自动更新 ${url}`);
|
||||
autoUpdater.setFeedURL({url: url})
|
||||
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
log.info("正在检查更新")
|
||||
})
|
||||
|
||||
autoUpdater.on('update-available', (event, info) => {
|
||||
log.info(`发现新版本`)
|
||||
})
|
||||
|
||||
autoUpdater.on('update-not-available', () => {
|
||||
log.info('没有可用的更新')
|
||||
|
||||
})
|
||||
|
||||
autoUpdater.on('error', (err) => {
|
||||
log.error(`更新错误:${err.message}`)
|
||||
|
||||
})
|
||||
|
||||
autoUpdater.on('update-downloaded', () => {
|
||||
dialog.showMessageBox({
|
||||
type: 'info',
|
||||
title: '应用更新',
|
||||
message: '发现新版本,是否更新?',
|
||||
buttons: ['是', '否']
|
||||
}).then((buttonIndex) => {
|
||||
if (buttonIndex.response == 0) { //选择是,则退出程序,安装新版本
|
||||
autoUpdater.quitAndInstall()
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// setInterval(() => {
|
||||
// log.initialize("定时检查更新")
|
||||
// // autoUpdater.checkForUpdates();
|
||||
// }, 60000)
|
||||
autoUpdater.checkForUpdates();
|
||||
log.info("手动检查更新一次")
|
||||
|
||||
|
||||
}
|
||||
// import {app, dialog, autoUpdater, BrowserWindow} from "electron";
|
||||
//
|
||||
// const log = require('electron-log');
|
||||
//
|
||||
//
|
||||
// export const initUpdaterApi = (win: BrowserWindow) => {
|
||||
// //更新测试打开
|
||||
// Object.defineProperty(app, 'isPackaged', {
|
||||
// get() {
|
||||
// return true;
|
||||
// }
|
||||
// });
|
||||
// const server = 'https://hazel-git-master-uiluck.vercel.app'
|
||||
// let packageName = null
|
||||
// const platform = process.platform;
|
||||
// const arch = process.arch;
|
||||
// switch (platform) {
|
||||
// case "darwin":
|
||||
// if (arch == "arm64") {
|
||||
// packageName = "darwin_arm64";
|
||||
// } else {
|
||||
// packageName = "darwin";
|
||||
// }
|
||||
// break;
|
||||
// case "win32":
|
||||
// packageName = "exe";
|
||||
// break;
|
||||
// case "linux":
|
||||
// packageName = "AppImage";
|
||||
// if (arch == "arm64") {
|
||||
// packageName = "AppImage_arm64";
|
||||
// } else {
|
||||
// packageName = "AppImage";
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// const url = `${server}/update/${packageName}/${app.getVersion()}`
|
||||
// log.info(`开启自动更新 ${url}`);
|
||||
// autoUpdater.setFeedURL({url: url})
|
||||
//
|
||||
// autoUpdater.on('checking-for-update', () => {
|
||||
// log.info("正在检查更新")
|
||||
// })
|
||||
//
|
||||
// autoUpdater.on('update-available', (event, info) => {
|
||||
// log.info(`发现新版本`)
|
||||
// })
|
||||
//
|
||||
// autoUpdater.on('update-not-available', () => {
|
||||
// log.info('没有可用的更新')
|
||||
//
|
||||
// })
|
||||
//
|
||||
// autoUpdater.on('error', (err) => {
|
||||
// log.error(`更新错误:${err.message}`)
|
||||
//
|
||||
// })
|
||||
//
|
||||
// autoUpdater.on('update-downloaded', () => {
|
||||
// dialog.showMessageBox({
|
||||
// type: 'info',
|
||||
// title: '应用更新',
|
||||
// message: '发现新版本,是否更新?',
|
||||
// buttons: ['是', '否']
|
||||
// }).then((buttonIndex) => {
|
||||
// if (buttonIndex.response == 0) { //选择是,则退出程序,安装新版本
|
||||
// autoUpdater.quitAndInstall()
|
||||
// app.quit()
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// // setInterval(() => {
|
||||
// // log.initialize("定时检查更新")
|
||||
// // // autoUpdater.checkForUpdates();
|
||||
// // }, 60000)
|
||||
// autoUpdater.checkForUpdates();
|
||||
// log.info("手动检查更新一次")
|
||||
//
|
||||
//
|
||||
// }
|
||||
|
@ -5,6 +5,7 @@ import PathUtils from "../utils/PathUtils";
|
||||
import fs from "fs";
|
||||
import FrpcProcessService from "../service/FrpcProcessService";
|
||||
import SystemService from "../service/SystemService";
|
||||
import moment from "moment";
|
||||
|
||||
class ConfigController extends BaseController {
|
||||
private readonly _serverService: ServerService;
|
||||
@ -70,8 +71,11 @@ class ConfigController extends BaseController {
|
||||
|
||||
exportConfig(req: ControllerParam) {
|
||||
this._systemService.openDirectory().then(folder => {
|
||||
this._serverService.genTomlConfig(folder.filePaths[0]).then(() => {
|
||||
req.event.reply(req.channel, success());
|
||||
const path = `${folder.filePaths[0]}/frpc-${moment(new Date()).format(
|
||||
"YYYYMMDDhhmmss"
|
||||
)}.toml`;
|
||||
this._serverService.genTomlConfig(path).then(() => {
|
||||
req.event.reply(req.channel, success(path));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import BaseController from "./BaseController";
|
||||
import ProxyService from "../service/ProxyService";
|
||||
import { success } from "../utils/response";
|
||||
import ProxyDao from "../dao/ProxyDao";
|
||||
import ProxyRepository from "../repository/ProxyRepository";
|
||||
|
||||
class ProxyController extends BaseController {
|
||||
private readonly _proxyService: ProxyService;
|
||||
private readonly _proxyDao: ProxyDao;
|
||||
private readonly _proxyDao: ProxyRepository;
|
||||
|
||||
constructor(proxyService: ProxyService, proxyDao: ProxyDao) {
|
||||
constructor(proxyService: ProxyService, proxyDao: ProxyRepository) {
|
||||
super();
|
||||
this._proxyService = proxyService;
|
||||
this._proxyDao = proxyDao;
|
||||
|
@ -1,13 +1,13 @@
|
||||
import BaseController from "./BaseController";
|
||||
import VersionService from "../service/VersionService";
|
||||
import { fail, success } from "../utils/response";
|
||||
import VersionDao from "../dao/VersionDao";
|
||||
import VersionRepository from "../repository/VersionRepository";
|
||||
|
||||
class VersionController extends BaseController {
|
||||
private readonly _versionService: VersionService;
|
||||
private readonly _versionDao: VersionDao;
|
||||
private readonly _versionDao: VersionRepository;
|
||||
|
||||
constructor(versionService: VersionService, versionDao: VersionDao) {
|
||||
constructor(versionService: VersionService, versionDao: VersionRepository) {
|
||||
super();
|
||||
this._versionService = versionService;
|
||||
this._versionDao = versionDao;
|
||||
|
@ -1,13 +1,23 @@
|
||||
/**
|
||||
* todo DI
|
||||
*/
|
||||
import Logger from "./Logger";
|
||||
|
||||
class BeanFactory {
|
||||
private static _beans: Map<string, any> = new Map<string, any>();
|
||||
|
||||
private static registerBean(name: string, instance: any): void {
|
||||
if (!this._beans.has(name)) {
|
||||
this._beans.set(name, instance);
|
||||
static registerBean(clazz: Function, beanName?: string): void {
|
||||
if (!beanName) {
|
||||
beanName = this.getBeanName(clazz.name);
|
||||
}
|
||||
if (this.hasBean(beanName)) {
|
||||
return;
|
||||
}
|
||||
const instance = new (clazz as any)();
|
||||
|
||||
this._beans.set(beanName, instance);
|
||||
}
|
||||
|
||||
public static setBean<T>(name: string, bean: T): void {
|
||||
this._beans.set(name, bean);
|
||||
Logger.info(`register bean ${name} ${bean}`);
|
||||
}
|
||||
|
||||
public static getBean<T>(name: string): T {
|
||||
@ -21,6 +31,14 @@ class BeanFactory {
|
||||
public static clear(): void {
|
||||
this._beans.clear();
|
||||
}
|
||||
|
||||
public static getBeanName(className: string) {
|
||||
return className.charAt(0).toLowerCase() + className.slice(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export default BeanFactory;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { app } from "electron";
|
||||
|
||||
class GlobalConstant {
|
||||
public static APP_NAME = "Frpc Desktop";
|
||||
public static ZIP_EXT = ".zip";
|
||||
public static GZ_EXT = ".gz";
|
||||
public static TAR_GZ_EXT = ".tar.gz";
|
||||
|
@ -1,20 +1,3 @@
|
||||
import ConfigController from "../controller/ConfigController";
|
||||
import ServerDao from "../dao/ServerDao";
|
||||
import ServerService from "../service/ServerService";
|
||||
import LogService from "../service/LogService";
|
||||
import VersionService from "../service/VersionService";
|
||||
import { BrowserWindow, ipcMain } from "electron";
|
||||
import LogController from "../controller/LogController";
|
||||
import VersionController from "../controller/VersionController";
|
||||
import VersionDao from "../dao/VersionDao";
|
||||
import GitHubService from "../service/GitHubService";
|
||||
import FrpcProcessService from "../service/FrpcProcessService";
|
||||
import LaunchController from "../controller/LaunchController";
|
||||
import ProxyDao from "../dao/ProxyDao";
|
||||
import ProxyService from "../service/ProxyService";
|
||||
import ProxyController from "../controller/ProxyController";
|
||||
import SystemService from "../service/SystemService";
|
||||
import SystemController from "../controller/SystemController";
|
||||
|
||||
export const ipcRouters: IpcRouters = {
|
||||
SERVER: {
|
||||
@ -133,119 +116,3 @@ export const listeners: Listeners = {
|
||||
channel: "frpcProcess:watchFrpcLog"
|
||||
}
|
||||
};
|
||||
|
||||
class IpcRouterConfigurate {
|
||||
ipcRouters: Array<IpcRouter>;
|
||||
private readonly _beans: Map<string, any> = new Map<string, any>();
|
||||
private readonly _win: BrowserWindow;
|
||||
|
||||
/**
|
||||
* initBeans
|
||||
* @private
|
||||
*/
|
||||
private initializeBeans() {
|
||||
const serverDao = new ServerDao();
|
||||
const versionDao = new VersionDao();
|
||||
const proxyDao = new ProxyDao();
|
||||
const systemService = new SystemService();
|
||||
const serverService = new ServerService(serverDao, proxyDao);
|
||||
const gitHubService = new GitHubService();
|
||||
const versionService = new VersionService(
|
||||
versionDao,
|
||||
systemService,
|
||||
gitHubService
|
||||
);
|
||||
const logService = new LogService(systemService);
|
||||
const frpcProcessService = new FrpcProcessService(
|
||||
serverService,
|
||||
versionDao
|
||||
);
|
||||
const proxyService = new ProxyService(proxyDao);
|
||||
const configController = new ConfigController(
|
||||
serverService,
|
||||
systemService,
|
||||
frpcProcessService
|
||||
);
|
||||
const versionController = new VersionController(versionService, versionDao);
|
||||
const logController = new LogController(logService);
|
||||
const launchController = new LaunchController(frpcProcessService);
|
||||
const proxyController = new ProxyController(proxyService, proxyDao);
|
||||
const systemController = new SystemController(systemService);
|
||||
|
||||
this._beans.set("serverDao", serverDao);
|
||||
this._beans.set("versionDao", versionDao);
|
||||
this._beans.set("proxyDao", proxyDao);
|
||||
this._beans.set("systemService", systemService);
|
||||
this._beans.set("serverService", serverService);
|
||||
this._beans.set("versionService", versionService);
|
||||
this._beans.set("logService", logService);
|
||||
this._beans.set("proxyService", proxyService);
|
||||
this._beans.set("frpcProcessService", frpcProcessService);
|
||||
this._beans.set("configController", configController);
|
||||
this._beans.set("versionController", versionController);
|
||||
this._beans.set("logController", logController);
|
||||
this._beans.set("launchController", launchController);
|
||||
this._beans.set("proxyController", proxyController);
|
||||
this._beans.set("systemController", systemController);
|
||||
}
|
||||
|
||||
/**
|
||||
* initJob
|
||||
* @private
|
||||
*/
|
||||
private initializeListeners() {
|
||||
Object.keys(listeners).forEach(listenerKey => {
|
||||
console.log(listenerKey, "listenerKey", listeners[listenerKey]);
|
||||
const { listenerMethod, channel } = listeners[listenerKey];
|
||||
const [beanName, method] = listenerMethod.split(".");
|
||||
const bean = this._beans.get(beanName);
|
||||
const listenerParam: ListenerParam = {
|
||||
win: this._win,
|
||||
channel: channel,
|
||||
args: []
|
||||
};
|
||||
bean[method].call(bean, listenerParam);
|
||||
});
|
||||
console.log("initialize listeners success");
|
||||
// this._beans.get("logService").watchFrpcLog(this._win);
|
||||
}
|
||||
|
||||
/**
|
||||
* initRouters
|
||||
* @private
|
||||
*/
|
||||
private initializeRouters() {
|
||||
Object.keys(ipcRouters).forEach(routerKey => {
|
||||
const routerGroup = ipcRouters[routerKey];
|
||||
|
||||
Object.keys(routerGroup).forEach(method => {
|
||||
const router = routerGroup[method];
|
||||
ipcMain.on(router.path, (event, args) => {
|
||||
const req: ControllerParam = {
|
||||
win: this._win,
|
||||
channel: `${router.path}:hook`,
|
||||
event: event,
|
||||
args: args
|
||||
};
|
||||
const [beanName, method] = router.controller.split(".");
|
||||
const bean = this._beans.get(beanName);
|
||||
bean[method].call(bean, req);
|
||||
// bean[method].call(bean, req);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param win mainWindows
|
||||
*/
|
||||
constructor(win: BrowserWindow) {
|
||||
this._win = win;
|
||||
this.initializeBeans();
|
||||
this.initializeListeners();
|
||||
this.initializeRouters();
|
||||
}
|
||||
}
|
||||
|
||||
export default IpcRouterConfigurate;
|
||||
|
18
electron/core/Logger.ts
Normal file
18
electron/core/Logger.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import log from "electron-log";
|
||||
|
||||
class Logger {
|
||||
static {
|
||||
log.transports.file.level = "debug";
|
||||
log.transports.console.level = "debug";
|
||||
}
|
||||
|
||||
static info(msg: string) {}
|
||||
|
||||
static debug(msg: string) {}
|
||||
|
||||
static warn(msg: string) {}
|
||||
|
||||
static error(msg: string) {}
|
||||
}
|
||||
|
||||
export default Logger;
|
25
electron/core/annotation/Component.ts
Normal file
25
electron/core/annotation/Component.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// // Class decorator
|
||||
// import "reflect-metadata";
|
||||
// import BeanFactory from "../BeanFactory";
|
||||
//
|
||||
// const Component =
|
||||
// (): ClassDecorator =>
|
||||
// target => {
|
||||
// // BeanFactory.registerBean(
|
||||
// // beanName || BeanFactory.getBeanName(target.name),
|
||||
// // target
|
||||
// // );
|
||||
// };
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// export default Component;
|
||||
//
|
||||
//
|
||||
// // export default function Component(beanName?: string): ClassDecorator {
|
||||
// // return function (target) {
|
||||
// // const paramtypes = Reflect.getMetadata('design:paramtypes', target);
|
||||
// // console.log(paramtypes);
|
||||
// // };
|
||||
// // }
|
5
electron/core/annotation/Resource.ts
Normal file
5
electron/core/annotation/Resource.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// export default function Resource(beanName?: string): PropertyDecorator {
|
||||
// return function (target: Object, propertyKey: string | symbol) {
|
||||
// console.log(target, propertyKey);
|
||||
// };
|
||||
// }
|
@ -10,14 +10,25 @@ import {
|
||||
} from "electron";
|
||||
import { release } from "node:os";
|
||||
import node_path, { join } from "node:path";
|
||||
import { initGitHubApi } from "../api/github";
|
||||
import { initConfigApi } from "../api/config";
|
||||
import { startFrpWorkerProcess, stopFrpcProcess } from "../api/frpc";
|
||||
import { initFileApi } from "../api/file";
|
||||
import { getConfig } from "../storage/config";
|
||||
import { initLog, logError, logInfo, LogModule } from "../utils/log";
|
||||
import { maskSensitiveData } from "../utils/desensitize";
|
||||
import IpcRouterConfigurate from "../core/IpcRouter";
|
||||
import BeanFactory from "../core/BeanFactory";
|
||||
import ServerRepository from "../repository/ServerRepository";
|
||||
import VersionRepository from "../repository/VersionRepository";
|
||||
import ProxyRepository from "../repository/ProxyRepository";
|
||||
import SystemService from "../service/SystemService";
|
||||
import ServerService from "../service/ServerService";
|
||||
import GitHubService from "../service/GitHubService";
|
||||
import VersionService from "../service/VersionService";
|
||||
import LogService from "../service/LogService";
|
||||
import FrpcProcessService from "../service/FrpcProcessService";
|
||||
import ProxyService from "../service/ProxyService";
|
||||
import ConfigController from "../controller/ConfigController";
|
||||
import VersionController from "../controller/VersionController";
|
||||
import LogController from "../controller/LogController";
|
||||
import LaunchController from "../controller/LaunchController";
|
||||
import ProxyController from "../controller/ProxyController";
|
||||
import SystemController from "../controller/SystemController";
|
||||
import { ipcRouters, listeners } from "../core/IpcRouter";
|
||||
import Logger from "../core/Logger";
|
||||
|
||||
process.env.DIST_ELECTRON = join(__dirname, "..");
|
||||
process.env.DIST = join(process.env.DIST_ELECTRON, "../dist");
|
||||
@ -25,260 +36,351 @@ process.env.VITE_PUBLIC = process.env.VITE_DEV_SERVER_URL
|
||||
? join(process.env.DIST_ELECTRON, "../public")
|
||||
: process.env.DIST;
|
||||
|
||||
let win: BrowserWindow | null = null;
|
||||
let tray = null;
|
||||
const preload = join(__dirname, "../preload/index.js");
|
||||
const url = process.env.VITE_DEV_SERVER_URL;
|
||||
const indexHtml = join(process.env.DIST, "index.html");
|
||||
let isQuiting;
|
||||
|
||||
// Disable GPU Acceleration for Windows 7
|
||||
if (release().startsWith("6.1")) app.disableHardwareAcceleration();
|
||||
|
||||
// Set application name for Windows 10+ notifications
|
||||
if (process.platform === "win32") app.setAppUserModelId(app.getName());
|
||||
class FrpcDesktopApp {
|
||||
private _win: BrowserWindow | null = null;
|
||||
private readonly _silentStart = false;
|
||||
private _quitting = false;
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
async function createWindow(config: FrpConfig) {
|
||||
let show = true;
|
||||
if (config) {
|
||||
show = !config.systemSilentStartup;
|
||||
}
|
||||
win = new BrowserWindow({
|
||||
title: "Frpc Desktop",
|
||||
icon: join(process.env.VITE_PUBLIC, "logo/only/16x16.png"),
|
||||
width: 800,
|
||||
height: 600,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
maxWidth: 1280,
|
||||
maxHeight: 960,
|
||||
webPreferences: {
|
||||
preload,
|
||||
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
|
||||
// Consider using contextBridge.exposeInMainWorld
|
||||
// Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
},
|
||||
show: show
|
||||
});
|
||||
if (process.env.VITE_DEV_SERVER_URL) {
|
||||
// electron-vite-vue#298
|
||||
win.loadURL(url);
|
||||
// Open devTool if the app is not packaged
|
||||
win.webContents.openDevTools();
|
||||
} else {
|
||||
win.loadFile(indexHtml);
|
||||
constructor() {
|
||||
this.initializeBeans();
|
||||
this.initializeListeners();
|
||||
this.initializeRouters();
|
||||
this.initializeElectronApp();
|
||||
}
|
||||
|
||||
// Test actively push message to the Electron-Renderer
|
||||
win.webContents.on("did-finish-load", () => {
|
||||
win?.webContents.send("main-process-message", new Date().toLocaleString());
|
||||
});
|
||||
|
||||
// Make all links open with the browser, not with the application
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith("https:")) shell.openExternal(url);
|
||||
return { action: "deny" };
|
||||
});
|
||||
|
||||
// 隐藏菜单栏
|
||||
const { Menu } = require("electron");
|
||||
Menu.setApplicationMenu(null);
|
||||
// hide menu for Mac
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
export const createTray = (config: FrpConfig) => {
|
||||
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/only/16x16.png")
|
||||
);
|
||||
tray.setToolTip("Frpc Desktop");
|
||||
const contextMenu = Menu.buildFromTemplate(menu);
|
||||
tray.setContextMenu(contextMenu);
|
||||
|
||||
// 托盘双击打开
|
||||
tray.on("double-click", () => {
|
||||
win.show();
|
||||
});
|
||||
|
||||
logInfo(LogModule.APP, `Tray created successfully.`);
|
||||
};
|
||||
app.whenReady().then(() => {
|
||||
initLog();
|
||||
logInfo(
|
||||
LogModule.APP,
|
||||
`Application started. Current system architecture: ${
|
||||
process.arch
|
||||
}, platform: ${process.platform}, version: ${app.getVersion()}.`
|
||||
);
|
||||
|
||||
getConfig((err, config) => {
|
||||
if (err) {
|
||||
logError(LogModule.APP, `Failed to get config: ${err.message}`);
|
||||
initializeWindow() {
|
||||
if (this._win) {
|
||||
return;
|
||||
}
|
||||
this._win = new BrowserWindow({
|
||||
title: app.getName(),
|
||||
icon: join(process.env.VITE_PUBLIC, "logo/only/16x16.png"),
|
||||
width: 800,
|
||||
height: 600,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
maxWidth: 1280,
|
||||
maxHeight: 960,
|
||||
webPreferences: {
|
||||
preload,
|
||||
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
|
||||
// Consider using contextBridge.exposeInMainWorld
|
||||
// Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
},
|
||||
show: true
|
||||
});
|
||||
BeanFactory.setBean("win", this._win);
|
||||
if (process.env.VITE_DEV_SERVER_URL) {
|
||||
// electron-vite-vue#298
|
||||
this._win.loadURL(url).then(() => {});
|
||||
// Open devTool if the app is not packaged
|
||||
this._win.webContents.openDevTools();
|
||||
} else {
|
||||
this._win.loadFile(indexHtml).then(() => {});
|
||||
}
|
||||
|
||||
createWindow(config)
|
||||
.then(r => {
|
||||
logInfo(LogModule.APP, `Window created successfully.`);
|
||||
createTray(config);
|
||||
this._win.webContents.on("did-finish-load", () => {
|
||||
this._win?.webContents.send(
|
||||
"main-process-message",
|
||||
new Date().toLocaleString()
|
||||
);
|
||||
});
|
||||
this._win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith("https:")) shell.openExternal(url);
|
||||
return { action: "deny" };
|
||||
});
|
||||
const { Menu } = require("electron");
|
||||
Menu.setApplicationMenu(null);
|
||||
|
||||
if (config) {
|
||||
logInfo(
|
||||
LogModule.APP,
|
||||
`Config retrieved: ${JSON.stringify(
|
||||
maskSensitiveData(config, [
|
||||
"serverAddr",
|
||||
"serverPort",
|
||||
"authToken",
|
||||
"user",
|
||||
"metaToken"
|
||||
])
|
||||
)}`
|
||||
);
|
||||
const that = this;
|
||||
this._win.on("minimize", function (event) {
|
||||
event.preventDefault();
|
||||
that._win.hide();
|
||||
});
|
||||
|
||||
if (config.systemStartupConnect) {
|
||||
startFrpWorkerProcess(config);
|
||||
this._win.on("close", function (event) {
|
||||
if (!that._quitting) {
|
||||
event.preventDefault();
|
||||
that._win.hide();
|
||||
if (process.platform === "darwin") {
|
||||
app.dock.hide();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
initializeTray() {
|
||||
const that = this;
|
||||
let menu: Array<MenuItemConstructorOptions | MenuItem> = [
|
||||
{
|
||||
label: "显示主窗口",
|
||||
click: function () {
|
||||
that._win.show();
|
||||
if (process.platform === "darwin") {
|
||||
app.dock.show().then(() => {});
|
||||
}
|
||||
}
|
||||
const ipcRouterConfig = new IpcRouterConfigurate(win);
|
||||
// Initialize APIs
|
||||
try {
|
||||
initGitHubApi(win);
|
||||
logInfo(LogModule.APP, `GitHub API initialized.`);
|
||||
|
||||
initConfigApi(win);
|
||||
logInfo(LogModule.APP, `Config API initialized.`);
|
||||
|
||||
initFileApi();
|
||||
logInfo(LogModule.APP, `File API initialized.`);
|
||||
|
||||
|
||||
// initUpdaterApi(win);
|
||||
logInfo(LogModule.APP, `Updater API initialization skipped.`);
|
||||
} catch (error) {
|
||||
logError(
|
||||
LogModule.APP,
|
||||
`Error during API initialization: ${error.message}`
|
||||
);
|
||||
},
|
||||
{
|
||||
label: "退出",
|
||||
click: () => {
|
||||
that._quitting = true;
|
||||
// todo stop frpc process
|
||||
app.quit();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
logError(LogModule.APP, `Error creating window: ${error.message}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.on("window-all-closed", () => {
|
||||
logInfo(LogModule.APP, `All windows closed.`);
|
||||
win = null;
|
||||
if (process.platform !== "darwin") {
|
||||
stopFrpcProcess(() => {
|
||||
logInfo(LogModule.APP, `FRPC process stopped. Quitting application.`);
|
||||
app.quit();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.on("second-instance", () => {
|
||||
logInfo(LogModule.APP, `Second instance detected.`);
|
||||
if (win) {
|
||||
// Focus on the main window if the user tried to open another
|
||||
if (win.isMinimized()) win.restore();
|
||||
win.focus();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
logInfo(LogModule.APP, `Application activated.`);
|
||||
const allWindows = BrowserWindow.getAllWindows();
|
||||
if (allWindows.length) {
|
||||
allWindows[0].focus();
|
||||
} else {
|
||||
getConfig((err, config) => {
|
||||
if (err) {
|
||||
logError(
|
||||
LogModule.APP,
|
||||
`Failed to get config on activate: ${err.message}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
createWindow(config).then(r => {
|
||||
logInfo(LogModule.APP, `Window created on activate.`);
|
||||
});
|
||||
];
|
||||
const tray = new Tray(
|
||||
node_path.join(process.env.VITE_PUBLIC, "logo/only/16x16.png")
|
||||
);
|
||||
tray.setToolTip(app.getName());
|
||||
const contextMenu = Menu.buildFromTemplate(menu);
|
||||
tray.setContextMenu(contextMenu);
|
||||
|
||||
// 托盘双击打开
|
||||
tray.on("double-click", () => {
|
||||
this._win.show();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.on("before-quit", () => {
|
||||
logInfo(LogModule.APP, `Application is about to quit.`);
|
||||
stopFrpcProcess(() => {
|
||||
isQuiting = true;
|
||||
});
|
||||
});
|
||||
initializeElectronApp() {
|
||||
// Disable GPU Acceleration for Windows 7
|
||||
if (release().startsWith("6.1")) app.disableHardwareAcceleration();
|
||||
|
||||
ipcMain.handle("open-win", (_, arg) => {
|
||||
logInfo(LogModule.APP, `Opening new window with argument: ${arg}`);
|
||||
const childWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload,
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
// Set application name for Windows 10+ notifications
|
||||
if (process.platform === "win32") app.setAppUserModelId(app.getName());
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit();
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
app.whenReady().then(() => {
|
||||
this.initializeWindow();
|
||||
this.initializeTray();
|
||||
// initLog();
|
||||
// logInfo(
|
||||
// LogModule.APP,
|
||||
// `Application started. Current system architecture: ${
|
||||
// process.arch
|
||||
// }, platform: ${process.platform}, version: ${app.getVersion()}.`
|
||||
// );
|
||||
|
||||
if (process.env.VITE_DEV_SERVER_URL) {
|
||||
childWindow.loadURL(`${url}#${arg}`);
|
||||
logInfo(LogModule.APP, `Child window loaded URL: ${url}#${arg}`);
|
||||
} else {
|
||||
childWindow.loadFile(indexHtml, { hash: arg });
|
||||
logInfo(
|
||||
LogModule.APP,
|
||||
`Child window loaded file: ${indexHtml} with hash: ${arg}`
|
||||
// getConfig((err, config) => {
|
||||
// if (err) {
|
||||
// logError(LogModule.APP, `Failed to get config: ${err.message}`);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// createWindow(config)
|
||||
// .then(r => {
|
||||
// logInfo(LogModule.APP, `Window created successfully.`);
|
||||
// createTray(config);
|
||||
//
|
||||
// // if (config) {
|
||||
// // logInfo(
|
||||
// // LogModule.APP,
|
||||
// // `Config retrieved: ${JSON.stringify(
|
||||
// // maskSensitiveData(config, [
|
||||
// // "serverAddr",
|
||||
// // "serverPort",
|
||||
// // "authToken",
|
||||
// // "user",
|
||||
// // "metaToken"
|
||||
// // ])
|
||||
// // )}`
|
||||
// // );
|
||||
// //
|
||||
// // if (config.systemStartupConnect) {
|
||||
// // startFrpWorkerProcess(config);
|
||||
// // }
|
||||
// // }
|
||||
// // const ipcRouterConfig = new IpcRouterConfigurate(win);
|
||||
// // Initialize APIs
|
||||
// // try {
|
||||
// // initGitHubApi(win);
|
||||
// // logInfo(LogModule.APP, `GitHub API initialized.`);
|
||||
// //
|
||||
// // initConfigApi(win);
|
||||
// // logInfo(LogModule.APP, `Config API initialized.`);
|
||||
// //
|
||||
// // initFileApi();
|
||||
// // logInfo(LogModule.APP, `File API initialized.`);
|
||||
// //
|
||||
// // // initUpdaterApi(win);
|
||||
// // logInfo(LogModule.APP, `Updater API initialization skipped.`);
|
||||
// // } catch (error) {
|
||||
// // logError(
|
||||
// // LogModule.APP,
|
||||
// // `Error during API initialization: ${error.message}`
|
||||
// // );
|
||||
// // }
|
||||
// })
|
||||
// .catch(error => {
|
||||
// logError(LogModule.APP, `Error creating window: ${error.message}`);
|
||||
// });
|
||||
// });
|
||||
});
|
||||
|
||||
app.on("window-all-closed", () => {
|
||||
// logInfo(LogModule.APP, `All windows closed.`);
|
||||
this._win = null;
|
||||
if (process.platform !== "darwin") {
|
||||
// todo stop frpc process
|
||||
// stopFrpcProcess(() => {
|
||||
// logInfo(LogModule.APP, `FRPC process stopped. Quitting application.`);
|
||||
app.quit();
|
||||
// });
|
||||
}
|
||||
});
|
||||
|
||||
app.on("second-instance", () => {
|
||||
if (this._win) {
|
||||
if (this._win.isMinimized()) this._win.restore();
|
||||
this._win.focus();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
// logInfo(LogModule.APP, `Application activated.`);
|
||||
const allWindows = BrowserWindow.getAllWindows();
|
||||
if (allWindows.length) {
|
||||
allWindows[0].focus();
|
||||
} else {
|
||||
this.initializeWindow();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("before-quit", () => {
|
||||
// todo stop frpc process
|
||||
this._quitting = true;
|
||||
});
|
||||
}
|
||||
|
||||
initializeBeans() {
|
||||
BeanFactory.setBean("serverRepository", new ServerRepository());
|
||||
BeanFactory.setBean("versionRepository", new VersionRepository());
|
||||
BeanFactory.setBean("proxyRepository", new ProxyRepository());
|
||||
BeanFactory.setBean("systemService", new SystemService());
|
||||
BeanFactory.setBean(
|
||||
"serverService",
|
||||
new ServerService(
|
||||
BeanFactory.getBean("serverRepository"),
|
||||
BeanFactory.getBean("proxyRepository")
|
||||
)
|
||||
);
|
||||
BeanFactory.setBean("gitHubService", new GitHubService());
|
||||
BeanFactory.setBean(
|
||||
"versionService",
|
||||
new VersionService(
|
||||
BeanFactory.getBean("versionRepository"),
|
||||
BeanFactory.getBean("systemService"),
|
||||
BeanFactory.getBean("gitHubService")
|
||||
)
|
||||
);
|
||||
BeanFactory.setBean(
|
||||
"logService",
|
||||
new LogService(BeanFactory.getBean("systemService"))
|
||||
);
|
||||
BeanFactory.setBean(
|
||||
"frpcProcessService",
|
||||
new FrpcProcessService(
|
||||
BeanFactory.getBean("serverService"),
|
||||
BeanFactory.getBean("versionRepository")
|
||||
)
|
||||
);
|
||||
BeanFactory.setBean(
|
||||
"proxyService",
|
||||
new ProxyService(BeanFactory.getBean("proxyRepository"))
|
||||
);
|
||||
BeanFactory.setBean(
|
||||
"configController",
|
||||
new ConfigController(
|
||||
BeanFactory.getBean("serverService"),
|
||||
BeanFactory.getBean("systemService"),
|
||||
BeanFactory.getBean("frpcProcessService")
|
||||
)
|
||||
);
|
||||
BeanFactory.setBean(
|
||||
"versionController",
|
||||
new VersionController(
|
||||
BeanFactory.getBean("versionService"),
|
||||
BeanFactory.getBean("versionRepository")
|
||||
)
|
||||
);
|
||||
BeanFactory.setBean(
|
||||
"logController",
|
||||
new LogController(BeanFactory.getBean("logService"))
|
||||
);
|
||||
BeanFactory.setBean(
|
||||
"launchController",
|
||||
new LaunchController(BeanFactory.getBean("frpcProcessService"))
|
||||
);
|
||||
BeanFactory.setBean(
|
||||
"proxyController",
|
||||
new ProxyController(
|
||||
BeanFactory.getBean("proxyService"),
|
||||
BeanFactory.getBean("proxyRepository")
|
||||
)
|
||||
);
|
||||
BeanFactory.setBean(
|
||||
"systemController",
|
||||
new SystemController(BeanFactory.getBean("systemService"))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* initJob
|
||||
* @private
|
||||
*/
|
||||
private initializeListeners() {
|
||||
Object.keys(listeners).forEach(listenerKey => {
|
||||
console.log(listenerKey, "listenerKey", listeners[listenerKey]);
|
||||
const { listenerMethod, channel } = listeners[listenerKey];
|
||||
const [beanName, method] = listenerMethod.split(".");
|
||||
const bean = BeanFactory.getBean(beanName);
|
||||
const listenerParam: ListenerParam = {
|
||||
// win: BeanFactory.getBean("win"),
|
||||
channel: channel,
|
||||
args: []
|
||||
};
|
||||
bean[method].call(bean, listenerParam);
|
||||
});
|
||||
Logger.info("initialize listeners success");
|
||||
// this._beans.get("logService").watchFrpcLog(this._win);
|
||||
}
|
||||
|
||||
/**
|
||||
* initRouters
|
||||
* @private
|
||||
*/
|
||||
private initializeRouters() {
|
||||
Object.keys(ipcRouters).forEach(routerKey => {
|
||||
const routerGroup = ipcRouters[routerKey];
|
||||
|
||||
Object.keys(routerGroup).forEach(method => {
|
||||
const router = routerGroup[method];
|
||||
ipcMain.on(router.path, (event, args) => {
|
||||
const req: ControllerParam = {
|
||||
// win: BeanFactory.getBean("win"),
|
||||
channel: `${router.path}:hook`,
|
||||
event: event,
|
||||
args: args
|
||||
};
|
||||
const [beanName, method] = router.controller.split(".");
|
||||
const bean = BeanFactory.getBean(beanName);
|
||||
bean[method].call(bean, req);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new FrpcDesktopApp();
|
@ -20,7 +20,7 @@ import IdUtils from "../utils/IdUtils";
|
||||
// }
|
||||
|
||||
|
||||
class BaseDao<T> {
|
||||
class BaseRepository<T> {
|
||||
protected readonly db: Datastore;
|
||||
|
||||
constructor(dbName: string) {
|
||||
@ -135,4 +135,4 @@ class BaseDao<T> {
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseDao;
|
||||
export default BaseRepository;
|
@ -1,6 +1,8 @@
|
||||
import BaseDao from "./BaseDao";
|
||||
import BaseRepository from "./BaseRepository";
|
||||
import Component from "../core/annotation/Component";
|
||||
|
||||
class ProxyDao extends BaseDao<FrpcProxy> {
|
||||
// @Component()
|
||||
class ProxyRepository extends BaseRepository<FrpcProxy> {
|
||||
constructor() {
|
||||
super("proxy");
|
||||
}
|
||||
@ -23,4 +25,4 @@ class ProxyDao extends BaseDao<FrpcProxy> {
|
||||
}
|
||||
}
|
||||
|
||||
export default ProxyDao;
|
||||
export default ProxyRepository;
|
@ -1,6 +1,8 @@
|
||||
import BaseDao from "./BaseDao";
|
||||
import BaseRepository from "./BaseRepository";
|
||||
import Component from "../core/annotation/Component";
|
||||
|
||||
class ServerDao extends BaseDao<OpenSourceFrpcDesktopServer> {
|
||||
// @Component()
|
||||
class ServerRepository extends BaseRepository<OpenSourceFrpcDesktopServer> {
|
||||
constructor() {
|
||||
super("server");
|
||||
}
|
||||
@ -18,4 +20,4 @@ class ServerDao extends BaseDao<OpenSourceFrpcDesktopServer> {
|
||||
}
|
||||
}
|
||||
|
||||
export default ServerDao
|
||||
export default ServerRepository
|
@ -1,6 +1,8 @@
|
||||
import BaseDao from "./BaseDao";
|
||||
import BaseRepository from "./BaseRepository";
|
||||
import Component from "../core/annotation/Component";
|
||||
|
||||
class VersionDao extends BaseDao<FrpcVersion> {
|
||||
// @Component()
|
||||
class VersionRepository extends BaseRepository<FrpcVersion> {
|
||||
constructor() {
|
||||
super("version");
|
||||
}
|
||||
@ -18,7 +20,7 @@ class VersionDao extends BaseDao<FrpcVersion> {
|
||||
}
|
||||
|
||||
exists(githubReleaseId: number): Promise<boolean> {
|
||||
return new Promise(( resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db.count({ githubReleaseId: githubReleaseId }, (err, count) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
@ -30,4 +32,4 @@ class VersionDao extends BaseDao<FrpcVersion> {
|
||||
}
|
||||
}
|
||||
|
||||
export default VersionDao;
|
||||
export default VersionRepository;
|
@ -1,14 +1,14 @@
|
||||
import BaseDao from "../dao/BaseDao";
|
||||
import BaseRepository from "../repository/BaseRepository";
|
||||
|
||||
|
||||
interface BaseServiceInterface<T> {
|
||||
// dao: BaseDao<T>;
|
||||
// dao: BaseRepository<T>;
|
||||
}
|
||||
|
||||
class BaseService<T> implements BaseServiceInterface<T> {
|
||||
// dao: BaseDao<T>;
|
||||
// dao: BaseRepository<T>;
|
||||
//
|
||||
// constructor(dao: BaseDao<T>) {
|
||||
// constructor(dao: BaseRepository<T>) {
|
||||
// this.dao = dao;
|
||||
// }
|
||||
}
|
||||
|
@ -1,18 +1,19 @@
|
||||
import ServerService from "./ServerService";
|
||||
import VersionDao from "../dao/VersionDao";
|
||||
import VersionRepository from "../repository/VersionRepository";
|
||||
import PathUtils from "../utils/PathUtils";
|
||||
import GlobalConstant from "../core/GlobalConstant";
|
||||
import { Notification } from "electron";
|
||||
import { app, BrowserWindow, Notification } from "electron";
|
||||
import { success } from "../utils/response";
|
||||
import treeKill from "tree-kill";
|
||||
import BeanFactory from "../core/BeanFactory";
|
||||
|
||||
class FrpcProcessService {
|
||||
private readonly _serverService: ServerService;
|
||||
private readonly _versionDao: VersionDao;
|
||||
private readonly _versionDao: VersionRepository;
|
||||
private _frpcProcess: any;
|
||||
private _frpcProcessListener: any;
|
||||
|
||||
constructor(serverService: ServerService, versionDao: VersionDao) {
|
||||
constructor(serverService: ServerService, versionDao: VersionRepository) {
|
||||
this._serverService = serverService;
|
||||
this._versionDao = versionDao;
|
||||
}
|
||||
@ -81,7 +82,7 @@ class FrpcProcessService {
|
||||
console.log("running", running);
|
||||
if (!running) {
|
||||
new Notification({
|
||||
title: GlobalConstant.APP_NAME,
|
||||
title: app.getName(),
|
||||
body: "Connection lost, please check the logs for details."
|
||||
}).show();
|
||||
// logError(
|
||||
@ -90,7 +91,8 @@ class FrpcProcessService {
|
||||
// );
|
||||
// clearInterval(this._frpcProcessListener);
|
||||
}
|
||||
listenerParam.win.webContents.send(
|
||||
const win: BrowserWindow = BeanFactory.getBean("win");
|
||||
win.webContents.send(
|
||||
listenerParam.channel,
|
||||
success(running)
|
||||
);
|
||||
|
@ -2,6 +2,8 @@ import fs from "fs";
|
||||
import { success } from "../utils/response";
|
||||
import PathUtils from "../utils/PathUtils";
|
||||
import SystemService from "./SystemService";
|
||||
import BeanFactory from "../core/BeanFactory";
|
||||
import { BrowserWindow } from "electron";
|
||||
|
||||
class LogService {
|
||||
private readonly _systemService: SystemService;
|
||||
@ -11,8 +13,11 @@ class LogService {
|
||||
this._systemService = systemService;
|
||||
}
|
||||
|
||||
getFrpLogContent(): Promise<string> {
|
||||
async getFrpLogContent() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!fs.existsSync(this._logPath)) {
|
||||
resolve("");
|
||||
}
|
||||
fs.readFile(this._logPath, "utf-8", (error, data) => {
|
||||
if (!error) {
|
||||
resolve(data);
|
||||
@ -28,11 +33,12 @@ class LogService {
|
||||
setTimeout(() => this.watchFrpcLog(listenerParam), 1000);
|
||||
return;
|
||||
}
|
||||
console.log('watchFrpcLog succcess');
|
||||
console.log("watchFrpcLog succcess");
|
||||
fs.watch(this._logPath, (eventType, filename) => {
|
||||
if (eventType === "change") {
|
||||
console.log("change", eventType, listenerParam.channel);
|
||||
listenerParam.win.webContents.send(
|
||||
const win: BrowserWindow = BeanFactory.getBean("win");
|
||||
win.webContents.send(
|
||||
listenerParam.channel,
|
||||
success(true)
|
||||
);
|
||||
|
@ -1,11 +1,13 @@
|
||||
import ProxyDao from "../dao/ProxyDao";
|
||||
import ProxyRepository from "../repository/ProxyRepository";
|
||||
import Component from "../core/annotation/Component";
|
||||
|
||||
const { exec, spawn } = require("child_process");
|
||||
|
||||
class ProxyService {
|
||||
private readonly _proxyDao: ProxyDao;
|
||||
|
||||
constructor(proxyDao: ProxyDao) {
|
||||
private readonly _proxyDao: ProxyRepository;
|
||||
|
||||
constructor(public proxyDao: ProxyRepository) {
|
||||
this._proxyDao = proxyDao;
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
import BaseService from "./BaseService";
|
||||
import ServerDao from "../dao/ServerDao";
|
||||
import ServerRepository from "../repository/ServerRepository";
|
||||
import TOML from "smol-toml";
|
||||
import fs from "fs";
|
||||
import PathUtils from "../utils/PathUtils";
|
||||
import ProxyDao from "../dao/ProxyDao";
|
||||
import ProxyRepository from "../repository/ProxyRepository";
|
||||
|
||||
class ServerService extends BaseService<OpenSourceFrpcDesktopServer> {
|
||||
private readonly _serverDao: ServerDao;
|
||||
private readonly _proxyDao: ProxyDao;
|
||||
private readonly _serverDao: ServerRepository;
|
||||
private readonly _proxyDao: ProxyRepository;
|
||||
private readonly _serverId: string = "1";
|
||||
|
||||
constructor(serverDao: ServerDao, proxyDao: ProxyDao) {
|
||||
constructor(serverDao: ServerRepository, proxyDao: ProxyRepository) {
|
||||
super();
|
||||
this._serverDao = serverDao;
|
||||
this._proxyDao = proxyDao;
|
||||
|
@ -4,6 +4,7 @@ import path from "path";
|
||||
import fs from "fs";
|
||||
import zlib from "zlib";
|
||||
import admZip from "adm-zip";
|
||||
import Component from "../core/annotation/Component";
|
||||
|
||||
const tar = require("tar");
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import VersionDao from "../dao/VersionDao";
|
||||
import VersionRepository from "../repository/VersionRepository";
|
||||
import BaseService from "./BaseService";
|
||||
import GitHubService from "./GitHubService";
|
||||
import frpReleasesJson from "../json/frp-releases.json";
|
||||
@ -14,14 +14,14 @@ import frpChecksums from "../json/frp_all_sha256_checksums.json";
|
||||
import SystemService from "./SystemService";
|
||||
|
||||
class VersionService extends BaseService<FrpcVersion> {
|
||||
private readonly _versionDao: VersionDao;
|
||||
private readonly _versionDao: VersionRepository;
|
||||
private readonly _systemService: SystemService;
|
||||
private readonly _gitHubService: GitHubService;
|
||||
private readonly _currFrpArch: Array<string>;
|
||||
private _versions: Array<FrpcVersion> = [];
|
||||
|
||||
constructor(
|
||||
versionDao: VersionDao,
|
||||
versionDao: VersionRepository,
|
||||
systemService: SystemService,
|
||||
gitHubService: GitHubService
|
||||
) {
|
||||
|
10
package.json
10
package.json
@ -64,9 +64,9 @@
|
||||
"sass": "^1.66.1",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tree-kill": "^1.2.2",
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.4.9",
|
||||
"vite-plugin-electron": "^0.15.3",
|
||||
"typescript": "5.7.3",
|
||||
"vite": "^5.4.11",
|
||||
"vite-plugin-electron": "^0.28.6",
|
||||
"vite-plugin-electron-renderer": "^0.14.5",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4",
|
||||
@ -82,10 +82,10 @@
|
||||
"intro.js": "^8.0.0-beta.1",
|
||||
"isbinaryfile": "4.0.10",
|
||||
"js-base64": "^3.7.7",
|
||||
"smol-toml": "^1.3.1",
|
||||
"snowflakify": "^1.0.5",
|
||||
"tar": "^6.2.0",
|
||||
"unused-filename": "^4.0.1",
|
||||
"uuid": "^10.0.0",
|
||||
"smol-toml": "^1.3.1"
|
||||
"uuid": "^10.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -362,21 +362,22 @@ onMounted(() => {
|
||||
});
|
||||
});
|
||||
|
||||
on(ipcRouters.SERVER.exportConfig, () => {
|
||||
// 礼花
|
||||
confetti({
|
||||
zIndex: 12002,
|
||||
particleCount: 200,
|
||||
spread: 70,
|
||||
origin: { y: 0.6 }
|
||||
});
|
||||
ElMessageBox.alert("🎉 恭喜你,导入成功 请重启软件", `提示`, {
|
||||
closeOnClickModal: false,
|
||||
showClose: false,
|
||||
confirmButtonText: "立即重启"
|
||||
}).then(() => {
|
||||
send(ipcRouters.SYSTEM.relaunchApp);
|
||||
});
|
||||
on(ipcRouters.SERVER.exportConfig, (data) => {
|
||||
ElMessageBox.alert(`配置路径:${data}`, `🎉 导出成功`);
|
||||
// // 礼花
|
||||
// confetti({
|
||||
// zIndex: 12002,
|
||||
// particleCount: 200,
|
||||
// spread: 70,
|
||||
// origin: { y: 0.6 }
|
||||
// });
|
||||
// ElMessageBox.alert("🎉 恭喜你,导入成功 请重启软件", `提示`, {
|
||||
// closeOnClickModal: false,
|
||||
// showClose: false,
|
||||
// confirmButtonText: "立即重启"
|
||||
// }).then(() => {
|
||||
// send(ipcRouters.SYSTEM.relaunchApp);
|
||||
// });
|
||||
});
|
||||
// ElMessageBox.alert(data, `提示`);
|
||||
on(ipcRouters.SYSTEM.openAppData, () => {
|
||||
@ -514,7 +515,6 @@ onUnmounted(() => {
|
||||
removeRouterListeners(ipcRouters.SERVER.resetAllConfig);
|
||||
removeRouterListeners(ipcRouters.VERSION.getDownloadedVersions);
|
||||
removeRouterListeners(ipcRouters.SERVER.exportConfig);
|
||||
// ipcRenderer.removeAllListeners("Config.clearAll.hook");
|
||||
removeRouterListeners(ipcRouters.SYSTEM.openAppData);
|
||||
});
|
||||
</script>
|
||||
|
@ -22,7 +22,7 @@
|
||||
]
|
||||
},
|
||||
"types": [
|
||||
// "node",
|
||||
// "node",
|
||||
"vite/client",
|
||||
"element-plus/global"
|
||||
],
|
||||
|
4
types/core.d.ts
vendored
4
types/core.d.ts
vendored
@ -5,14 +5,14 @@ interface ApiResponse<T> {
|
||||
}
|
||||
|
||||
interface ControllerParam {
|
||||
win: BrowserWindow;
|
||||
// win: BrowserWindow;
|
||||
channel: string;
|
||||
event: Electron.IpcMainEvent;
|
||||
args: any;
|
||||
}
|
||||
|
||||
interface ListenerParam {
|
||||
win: BrowserWindow;
|
||||
// win: BrowserWindow;
|
||||
channel: string;
|
||||
args: any[];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user