🏗️ refactor file selection handling to improve response structure and utilize path data in SystemController

This commit is contained in:
刘嘉伟 2025-02-26 11:49:55 +08:00
parent 1882260096
commit 7515726a71
7 changed files with 136 additions and 61 deletions

View File

@ -3,6 +3,8 @@ import VersionService from "../service/VersionService";
import ResponseUtils from "../utils/ResponseUtils"; import ResponseUtils from "../utils/ResponseUtils";
import VersionRepository from "../repository/VersionRepository"; import VersionRepository from "../repository/VersionRepository";
import Logger from "../core/Logger"; import Logger from "../core/Logger";
import { BrowserWindow, dialog } from "electron";
import BeanFactory from "../core/BeanFactory";
class VersionController extends BaseController { class VersionController extends BaseController {
private readonly _versionService: VersionService; private readonly _versionService: VersionService;
@ -81,15 +83,58 @@ class VersionController extends BaseController {
} }
importLocalFrpcVersion(req: ControllerParam) { importLocalFrpcVersion(req: ControllerParam) {
this._versionService const win: BrowserWindow = BeanFactory.getBean("win");
.importLocalFrpcVersion() dialog
.then(data => { .showOpenDialog(win, {
req.event.reply(req.channel, ResponseUtils.success()); properties: ["openFile"],
filters: [
{ name: "Frpc", extensions: ["tar.gz", "zip"] } // 允许选择的文件类型,分开后缀以确保可以选择
]
}) })
.catch((err: Error) => { .then(result => {
if (result.canceled) {
req.event.reply(
req.channel,
ResponseUtils.success({
canceled: true
})
);
return;
} else {
const filePath = result.filePaths[0];
this._versionService
.importLocalFrpcVersion(filePath)
.then(data => {
req.event.reply(
req.channel,
ResponseUtils.success({
canceled: false
})
);
})
.catch((err: Error) => {
Logger.error("VersionController.importLocalFrpcVersion", err);
req.event.reply(req.channel, ResponseUtils.fail(err));
});
}
})
.catch(err => {
Logger.error("VersionController.importLocalFrpcVersion", err); Logger.error("VersionController.importLocalFrpcVersion", err);
req.event.reply(req.channel, ResponseUtils.fail(err)); req.event.reply(req.channel, ResponseUtils.fail(err));
}); });
// const win: BrowserWindow = BeanFactory.getBean("win");
// const result = await dialog.showOpenDialog(win, {
// properties: ["openFile"],
// filters: [
// { name: "Frpc", extensions: ["tar.gz", "zip"] } // 允许选择的文件类型,分开后缀以确保可以选择
// ]
// });
// if (result.canceled) {
//
// }else {
//
// }
} }
} }

View File

@ -1,8 +1,11 @@
enum ResponseCode { enum ResponseCode {
SUCCESS = "A1000:successful.", SUCCESS = "A1000;successful.",
INTERNAL_ERROR = "B1000:internal error.", INTERNAL_ERROR = "B1000;internal error.",
NOT_CONFIG = "B1001:未配置" NOT_CONFIG = "B1001;未配置",
VERSION_EXISTS = "B1002;导入失败,版本已存在",
VERSION_ARGS_ERROR = "B1003;所选 frp 架构与操作系统不符",
UNKNOWN_VERSION = "B1004;无法识别文件"
} }
class BusinessError extends Error { class BusinessError extends Error {
@ -14,7 +17,7 @@ class BusinessError extends Error {
// } // }
constructor(bizErrorEnum: ResponseCode) { constructor(bizErrorEnum: ResponseCode) {
const [bizCode, message] = bizErrorEnum.split(":"); const [bizCode, message] = bizErrorEnum.split(";");
super(message); super(message);
this._bizCode = bizCode; this._bizCode = bizCode;
this.name = "BusinessError"; this.name = "BusinessError";

View File

@ -7,12 +7,14 @@ import treeKill from "tree-kill";
import BeanFactory from "../core/BeanFactory"; import BeanFactory from "../core/BeanFactory";
import ResponseUtils from "../utils/ResponseUtils"; import ResponseUtils from "../utils/ResponseUtils";
import { BusinessError, ResponseCode } from "../core/BusinessError"; import { BusinessError, ResponseCode } from "../core/BusinessError";
import Logger from "../core/Logger";
class FrpcProcessService { class FrpcProcessService {
private readonly _serverService: ServerService; private readonly _serverService: ServerService;
private readonly _versionDao: VersionRepository; private readonly _versionDao: VersionRepository;
private _frpcProcess: any; private _frpcProcess: any;
private _frpcProcessListener: any; private _frpcProcessListener: any;
private _frpcLastStartTime: number = -1;
constructor(serverService: ServerService, versionDao: VersionRepository) { constructor(serverService: ServerService, versionDao: VersionRepository) {
this._serverService = serverService; this._serverService = serverService;
@ -42,7 +44,6 @@ class FrpcProcessService {
const version = await this._versionDao.findByGithubReleaseId( const version = await this._versionDao.findByGithubReleaseId(
config.frpcVersion config.frpcVersion
); );
// todo genConfigfile.
const configPath = PathUtils.getTomlConfigFilePath(); const configPath = PathUtils.getTomlConfigFilePath();
await this._serverService.genTomlConfig(configPath); await this._serverService.genTomlConfig(configPath);
const command = `./${PathUtils.getFrpcFilename()} -c "${configPath}"`; const command = `./${PathUtils.getFrpcFilename()} -c "${configPath}"`;
@ -50,22 +51,33 @@ class FrpcProcessService {
cwd: version.localPath, cwd: version.localPath,
shell: true shell: true
}); });
this._frpcLastStartTime = Date.now();
Logger.debug(
`FrpcProcessService.startFrpcProcess`,
`start command: ${command}`
);
this._frpcProcess.stdout.on("data", data => { this._frpcProcess.stdout.on("data", data => {
console.log(`stdout: ${data}`); Logger.debug(`FrpcProcessService.startFrpcProcess`, `stdout: ${data}`);
}); });
this._frpcProcess.stderr.on("data", data => { this._frpcProcess.stderr.on("data", data => {
console.error(`stderr: ${data}`); Logger.debug(`FrpcProcessService.startFrpcProcess`, `stderr: ${data}`);
}); });
} }
async stopFrpcProcess() { async stopFrpcProcess() {
if (this._frpcProcess && this.isRunning()) { if (this._frpcProcess && this.isRunning()) {
Logger.debug(
`FrpcProcessService.stopFrpcProcess`,
`pid: ${this._frpcProcess.pid}`
);
treeKill(this._frpcProcess.pid, (error: Error) => { treeKill(this._frpcProcess.pid, (error: Error) => {
if (error) { if (error) {
throw error; throw error;
} else { } else {
this._frpcProcess = null; this._frpcProcess = null;
this._frpcLastStartTime = -1;
// clearInterval(this._frpcProcessListener); // clearInterval(this._frpcProcessListener);
} }
}); });
@ -80,12 +92,17 @@ class FrpcProcessService {
// LogModule.FRP_CLIENT, // LogModule.FRP_CLIENT,
// `Monitoring frpc process status: ${status}, Listener ID: ${frpcStatusListener}` // `Monitoring frpc process status: ${status}, Listener ID: ${frpcStatusListener}`
// ); // );
console.log("running", running); Logger.debug(
`FrpcProcessService.watchFrpcProcess`,
`running: ${running}`
);
if (!running) { if (!running) {
new Notification({ if (this._frpcLastStartTime !== -1) {
title: app.getName(), new Notification({
body: "Connection lost, please check the logs for details." title: app.getName(),
}).show(); body: "Connection lost, please check the logs for details."
}).show();
}
// logError( // logError(
// LogModule.FRP_CLIENT, // LogModule.FRP_CLIENT,
// "Frpc process status check failed. Connection lost." // "Frpc process status check failed. Connection lost."

View File

@ -65,6 +65,8 @@ class ServerService extends BaseService<OpenSourceFrpcDesktopServer> {
const { frpcVersion, _id, system, ...commonConfig } = server; const { frpcVersion, _id, system, ...commonConfig } = server;
const frpcConfig = { ...commonConfig }; const frpcConfig = { ...commonConfig };
frpcConfig.log.to = PathUtils.getFrpcLogFilePath(); frpcConfig.log.to = PathUtils.getFrpcLogFilePath();
frpcConfig.loginFailExit = false;
frpcConfig.webServer.addr = "127.0.0.1";
const toml = TOML.stringify({ ...frpcConfig, proxies: enabledProxies }); const toml = TOML.stringify({ ...frpcConfig, proxies: enabledProxies });
fs.writeFileSync( fs.writeFileSync(

View File

@ -3,7 +3,7 @@ import BaseService from "./BaseService";
import GitHubService from "./GitHubService"; import GitHubService from "./GitHubService";
import frpReleasesJson from "../json/frp-releases.json"; import frpReleasesJson from "../json/frp-releases.json";
import { download } from "electron-dl"; import { download } from "electron-dl";
import { BrowserWindow, dialog } from "electron"; import { BrowserWindow } from "electron";
import GlobalConstant from "../core/GlobalConstant"; import GlobalConstant from "../core/GlobalConstant";
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
@ -12,7 +12,7 @@ import PathUtils from "../utils/PathUtils";
import FileUtils from "../utils/FileUtils"; import FileUtils from "../utils/FileUtils";
import frpChecksums from "../json/frp_all_sha256_checksums.json"; import frpChecksums from "../json/frp_all_sha256_checksums.json";
import SystemService from "./SystemService"; import SystemService from "./SystemService";
import BeanFactory from "../core/BeanFactory"; import { BusinessError, ResponseCode } from "../core/BusinessError";
class VersionService extends BaseService<FrpcVersion> { class VersionService extends BaseService<FrpcVersion> {
private readonly _versionDao: VersionRepository; private readonly _versionDao: VersionRepository;
@ -165,36 +165,24 @@ class VersionService extends BaseService<FrpcVersion> {
return false; return false;
} }
async importLocalFrpcVersion() { async importLocalFrpcVersion(filePath: string) {
const win: BrowserWindow = BeanFactory.getBean("win"); const checksum = FileUtils.calculateFileChecksum(filePath);
const result = await dialog.showOpenDialog(win, { const frpName = frpChecksums[checksum];
properties: ["openFile"], if (frpName) {
filters: [ if (this._currFrpArch.every(item => frpName.includes(item))) {
{ name: "Frpc", extensions: ["tar.gz", "zip"] } // 允许选择的文件类型,分开后缀以确保可以选择 const version = this.getFrpVersionByAssetName(frpName);
] const existsVersion = await this._versionDao.findByGithubReleaseId(
}); version.githubReleaseId
if (result.canceled) { );
return; if (existsVersion) {
} else { throw new BusinessError(ResponseCode.VERSION_EXISTS);
const filePath = result.filePaths[0];
const checksum = FileUtils.calculateFileChecksum(filePath);
const frpName = frpChecksums[checksum];
if (frpName) {
if (this._currFrpArch.every(item => frpName.includes(item))) {
const version = this.getFrpVersionByAssetName(frpName);
const existsVersion = await this._versionDao.findByGithubReleaseId(
version.githubReleaseId
);
if (existsVersion) {
throw new Error("导入失败,版本已存在");
}
return this.decompressFrp(version, filePath);
} else {
throw new Error(`导入失败,所选 frp 架构与操作系统不符`);
} }
return this.decompressFrp(version, filePath);
} else { } else {
throw new Error("导入失败,无法识别文件"); throw new BusinessError(ResponseCode.VERSION_ARGS_ERROR);
} }
} else {
throw new BusinessError(ResponseCode.UNKNOWN_VERSION);
} }
} }

View File

@ -2,7 +2,7 @@ import { BusinessError, ResponseCode } from "../core/BusinessError";
class ResponseUtils { class ResponseUtils {
public static success<T>(data?: any, message?: string) { public static success<T>(data?: any, message?: string) {
const [bizCode, message2] = ResponseCode.SUCCESS.split(":"); const [bizCode, message2] = ResponseCode.SUCCESS.split(";");
const resp: ApiResponse<T> = { const resp: ApiResponse<T> = {
bizCode: bizCode, bizCode: bizCode,
data: data, data: data,
@ -22,16 +22,16 @@ class ResponseUtils {
// } // }
public static fail(err: Error) { public static fail(err: Error) {
let bizCode = ""; if (!(err instanceof BusinessError)) {
let message = ""; err = new BusinessError(ResponseCode.INTERNAL_ERROR);
if (err instanceof BusinessError) {
bizCode = (err as BusinessError).bizCode;
message = (err as BusinessError).message;
} }
const bizCode = (err as BusinessError).bizCode;
const message = (err as BusinessError).message;
const resp: ApiResponse<any> = { const resp: ApiResponse<any> = {
bizCode: bizCode, bizCode: bizCode,
data: null, data: null,
message: message || "internal error." message: message
}; };
return resp; return resp;
} }

View File

@ -133,14 +133,34 @@ onMounted(() => {
handleLoadAllVersions(); handleLoadAllVersions();
}); });
on(ipcRouters.VERSION.importLocalFrpcVersion, () => { on(
loading.value++; ipcRouters.VERSION.importLocalFrpcVersion,
ElMessage({ data => {
type: "success", const { canceled } = data;
message: "导入成功" if (!canceled) {
}); loading.value++;
handleLoadAllVersions(); ElMessage({
}); type: "success",
message: "导入成功"
});
handleLoadAllVersions();
}
},
(bizCode: string, message: string) => {
if (bizCode === "B1002") {
//
ElMessageBox.alert(`${message}`, `导入失败`);
}
if (bizCode === "B1003") {
// frp
ElMessageBox.alert(`${message}`, `导入失败`);
}
if (bizCode === "B1004") {
//
ElMessageBox.alert(`${message}`, `导入失败`);
}
}
);
}); });
const handleImportFrp = () => { const handleImportFrp = () => {