🐛 fix version handling and improve IPC communication for downloads
This commit is contained in:
parent
8d56faeb80
commit
2edbbcb871
@ -11,7 +11,9 @@ class ServerController extends BaseController {
|
||||
}
|
||||
|
||||
saveConfig(req: ControllerParam) {
|
||||
console.log("save", req.args);
|
||||
this._serverService.saveServerConfig(req.args).then(() => {
|
||||
req.event.reply(req.channel, success());
|
||||
})
|
||||
}
|
||||
|
||||
getServerConfig(req: ControllerParam) {
|
||||
|
@ -1,13 +1,16 @@
|
||||
import BaseController from "./BaseController";
|
||||
import VersionService from "../service/VersionService";
|
||||
import { success } from "../utils/response";
|
||||
import { fail, success } from "../utils/response";
|
||||
import VersionDao from "../dao/VersionDao";
|
||||
|
||||
class VersionController extends BaseController {
|
||||
private readonly _versionService: VersionService;
|
||||
private readonly _versionDao: VersionDao;
|
||||
|
||||
constructor(versionService: VersionService) {
|
||||
constructor(versionService: VersionService, versionDao: VersionDao) {
|
||||
super();
|
||||
this._versionService = versionService;
|
||||
this._versionDao = versionDao;
|
||||
}
|
||||
|
||||
getVersions(req: ControllerParam) {
|
||||
@ -22,6 +25,43 @@ class VersionController extends BaseController {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getDownloadedVersions(req: ControllerParam) {
|
||||
this._versionDao.findAll().then(data => {
|
||||
req.event.reply(req.channel, success(data));
|
||||
});
|
||||
}
|
||||
|
||||
downloadFrpVersion(req: ControllerParam) {
|
||||
this._versionService
|
||||
.downloadFrpVersion(req.args.githubReleaseId, progress => {
|
||||
req.event.reply(
|
||||
req.channel,
|
||||
success({
|
||||
percent: progress.percent,
|
||||
githubReleaseId: req.args.githubReleaseId,
|
||||
completed: progress.percent >= 1
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(r => {
|
||||
console.log(2);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(1);
|
||||
});
|
||||
}
|
||||
|
||||
deleteDownloadedVersion(req: ControllerParam) {
|
||||
this._versionService
|
||||
.deleteFrpVersion(req.args.githubReleaseId)
|
||||
.then(() => {
|
||||
req.event.reply(req.channel, success());
|
||||
})
|
||||
.catch(err => {
|
||||
req.event.reply(req.channel, fail());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default VersionController;
|
||||
|
26
electron/core/BeanFactory.ts
Normal file
26
electron/core/BeanFactory.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* todo DI
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public static getBean<T>(name: string): T {
|
||||
return this._beans.get(name);
|
||||
}
|
||||
|
||||
public static hasBean(name: string): boolean {
|
||||
return this._beans.has(name);
|
||||
}
|
||||
|
||||
public static clear(): void {
|
||||
this._beans.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export default BeanFactory;
|
@ -1,13 +1,22 @@
|
||||
import path from "path";
|
||||
import { app } from "electron";
|
||||
import SecureUtils from "../utils/SecureUtils";
|
||||
|
||||
class GlobalConstant {
|
||||
public static FRPC_STORAGE_FOLDER = "";
|
||||
|
||||
public static APP_NAME = "Frpc Desktop";
|
||||
public static ZIP_EXT = ".zip";
|
||||
public static GZ_EXT = ".gz";
|
||||
public static TAR_GZ_EXT = ".tar.gz";
|
||||
|
||||
public static FRP_ARCH_VERSION_MAPPING = {
|
||||
win32_x64: ["window", "amd64"],
|
||||
win32_arm64: ["window", "arm64"],
|
||||
win32_ia32: ["window", "386"],
|
||||
darwin_arm64: ["darwin", "arm64"],
|
||||
darwin_x64: ["darwin", "amd64"],
|
||||
darwin_amd64: ["darwin", "amd64"],
|
||||
linux_x64: ["linux", "amd64"],
|
||||
linux_arm64: ["linux", "arm64"]
|
||||
};
|
||||
|
||||
public static FRPC_PROCESS_STATUS_CHECK_INTERVAL = 3000;
|
||||
// public static APP_DATA_PATH = app.getPath("userData");
|
||||
|
||||
// public static DOWNLOAD_STORAGE_PATH = path.join(
|
||||
|
@ -35,6 +35,18 @@ export const ipcRouters: IpcRouters = {
|
||||
getVersions: {
|
||||
path: "version/getVersions",
|
||||
controller: "versionController.getVersions"
|
||||
},
|
||||
downloadVersion: {
|
||||
path: "version/downloadVersion",
|
||||
controller: "versionController.downloadFrpVersion"
|
||||
},
|
||||
getDownloadedVersions: {
|
||||
path: "version/getDownloadedVersions",
|
||||
controller: "versionController.getDownloadedVersions"
|
||||
},
|
||||
deleteDownloadedVersion: {
|
||||
path: "version/deleteDownloadedVersion",
|
||||
controller: "versionController.deleteDownloadedVersion"
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -68,7 +80,7 @@ class IpcRouterConfigurate {
|
||||
);
|
||||
const logService = new LogService(fileService);
|
||||
const serverController = new ServerController(serverService);
|
||||
const versionController = new VersionController(versionService);
|
||||
const versionController = new VersionController(versionService, versionDao);
|
||||
const logController = new LogController(logService);
|
||||
|
||||
this._beans.set("serverDao", serverDao);
|
||||
|
@ -1,8 +1,7 @@
|
||||
import Datastore from "nedb";
|
||||
import path from "path";
|
||||
import Snowflakify from "snowflakify";
|
||||
import GlobalConstant from "../core/GlobalConstant";
|
||||
import PathUtils from "../utils/PathUtils";
|
||||
import IdUtils from "../utils/IdUtils";
|
||||
|
||||
// interface BaseDaoInterface<T> {
|
||||
// db: Datastore;
|
||||
@ -25,10 +24,7 @@ class BaseDao<T> {
|
||||
protected readonly db: Datastore;
|
||||
|
||||
constructor(dbName: string) {
|
||||
const dbFilename = path.join(
|
||||
PathUtils.getAppData(),
|
||||
`${dbName}-v2.db`
|
||||
);
|
||||
const dbFilename = path.join(PathUtils.getAppData(), `${dbName}-v2.db`);
|
||||
this.db = new Datastore({
|
||||
autoload: true,
|
||||
filename: dbFilename
|
||||
@ -83,10 +79,17 @@ class BaseDao<T> {
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// deleteById(id: string): void {
|
||||
// return null;
|
||||
// }
|
||||
deleteById(id: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.db.remove({ _id: id }, err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
//
|
||||
// findAll(): T[] {
|
||||
// return null;
|
||||
@ -103,6 +106,18 @@ class BaseDao<T> {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
findAll(): Promise<Array<T>> {
|
||||
return new Promise<Array<T>>((resolve, reject) => {
|
||||
this.db.find({}, (err, document) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(document);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseDao;
|
||||
|
@ -18,7 +18,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);
|
||||
|
@ -4,8 +4,9 @@ import fs from "fs";
|
||||
import zlib from "zlib";
|
||||
import admZip from "adm-zip";
|
||||
import GlobalConstant from "../core/GlobalConstant";
|
||||
import { logError, logInfo, LogModule } from "../utils/log";
|
||||
|
||||
// import tar from "tar";
|
||||
const tar = require("tar");
|
||||
|
||||
class FileService {
|
||||
constructor() {}
|
||||
@ -49,37 +50,40 @@ class FileService {
|
||||
// const frpcPath = path.join("frp", path.basename(zipFilePath, zipExt));
|
||||
}
|
||||
|
||||
decompressTarGzFile(tarGzPath: string, targetPath: string) {
|
||||
decompressTarGzFile(tarGzPath: string, targetPath: string, finish: Function) {
|
||||
// const targetFolder = path.join(targetPath, targetPath);
|
||||
const unzip = zlib.createGunzip();
|
||||
const readStream = fs.createReadStream(tarGzPath);
|
||||
// if (!fs.existsSync(unzip)) {
|
||||
// fs.mkdirSync(targetPath, { recursive: true, mode: 0o777 });
|
||||
// }
|
||||
if (!fs.existsSync(targetPath)) {
|
||||
fs.mkdirSync(targetPath, { recursive: true, mode: 0o777 });
|
||||
}
|
||||
|
||||
readStream
|
||||
.pipe(unzip)
|
||||
.on("error", err => {
|
||||
logError(LogModule.APP, `Error during gunzip: ${err.message}`);
|
||||
// logError(LogModule.APP, `Error during gunzip: ${err.message}`);
|
||||
})
|
||||
// .pipe(
|
||||
// tar
|
||||
// .extract({
|
||||
// cwd: targetPath,
|
||||
// filter: filePath => path.basename(filePath) === "frpc"
|
||||
// })
|
||||
// .on("error", err => {
|
||||
// logError(
|
||||
// LogModule.APP,
|
||||
// `Error extracting tar file: ${err.message}`
|
||||
// );
|
||||
// })
|
||||
// )
|
||||
.pipe(
|
||||
tar
|
||||
.extract({
|
||||
cwd: targetPath,
|
||||
strip: 1,
|
||||
filter: filePath => path.basename(filePath) === "frpc"
|
||||
})
|
||||
.on("error", err => {
|
||||
// logError(
|
||||
// LogModule.APP,
|
||||
// `Error extracting tar file: ${err.message}`
|
||||
// );
|
||||
})
|
||||
)
|
||||
.on("finish", () => {
|
||||
const frpcPath = path.join("frp", path.basename(tarGzPath, ".tar.gz"));
|
||||
logInfo(
|
||||
LogModule.APP,
|
||||
`Extraction completed. Extracted directory: ${frpcPath}`
|
||||
);
|
||||
finish();
|
||||
// const frpcPath = path.join("frp", path.basename(tarGzPath, ".tar.gz"));
|
||||
// logInfo(
|
||||
// LogModule.APP,
|
||||
// `Extraction completed. Extracted directory: ${frpcPath}`
|
||||
// );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
74
electron/service/FrpProcessService.ts
Normal file
74
electron/service/FrpProcessService.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import ServerService from "./ServerService";
|
||||
import VersionDao from "../dao/VersionDao";
|
||||
import PathUtils from "../utils/PathUtils";
|
||||
import { frpcProcess } from "../api/frpc";
|
||||
import GlobalConstant from "../core/GlobalConstant";
|
||||
import { Notification } from "electron";
|
||||
|
||||
const { exec, spawn } = require("child_process");
|
||||
|
||||
class FrpProcessService {
|
||||
private readonly _serverService: ServerService;
|
||||
private readonly _versionDao: VersionDao;
|
||||
private _frpcProcess: any;
|
||||
private _FrpcProcessListener: any;
|
||||
|
||||
constructor(serverService: ServerService, versionDao: VersionDao) {
|
||||
this._serverService = serverService;
|
||||
this._versionDao = versionDao;
|
||||
}
|
||||
|
||||
isRunning(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
async startFrpcProcess() {
|
||||
const config = await this._serverService.getServerConfig();
|
||||
const version = await this._versionDao.findByGithubReleaseId(
|
||||
config.frpcVersion
|
||||
);
|
||||
// todo genConfigfile.
|
||||
const configPath = "";
|
||||
const command = `${PathUtils.getFrpcFilename()} -c ${configPath}`;
|
||||
this._frpcProcess = spawn(command, {
|
||||
cwd: version.localPath,
|
||||
shell: true
|
||||
});
|
||||
|
||||
frpcProcess.stdout.on("data", data => {
|
||||
// logDebug(LogModule.FRP_CLIENT, `Frpc process output: ${data}`);
|
||||
});
|
||||
|
||||
frpcProcess.stdout.on("error", data => {
|
||||
// logError(LogModule.FRP_CLIENT, `Frpc process error: ${data}`);
|
||||
// stopFrpcProcess(() => {});
|
||||
this.stopFrpcProcess();
|
||||
});
|
||||
}
|
||||
|
||||
stopFrpcProcess() {}
|
||||
|
||||
watchFrpcProcess(listenerParam: ListenerParam) {
|
||||
this._FrpcProcessListener = setInterval(() => {
|
||||
const running = this.isRunning();
|
||||
// todo return status to view.
|
||||
// logDebug(
|
||||
// LogModule.FRP_CLIENT,
|
||||
// `Monitoring frpc process status: ${status}, Listener ID: ${frpcStatusListener}`
|
||||
// );
|
||||
if (!running) {
|
||||
new Notification({
|
||||
title: GlobalConstant.APP_NAME,
|
||||
body: "Connection lost, please check the logs for details."
|
||||
}).show();
|
||||
// logError(
|
||||
// LogModule.FRP_CLIENT,
|
||||
// "Frpc process status check failed. Connection lost."
|
||||
// );
|
||||
clearInterval(this._FrpcProcessListener);
|
||||
}
|
||||
}, GlobalConstant.FRPC_PROCESS_STATUS_CHECK_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
export default FrpProcessService;
|
@ -8,26 +8,14 @@ class ServerService extends BaseService<FrpcDesktopServer> {
|
||||
this._serverDao = serverDao;
|
||||
}
|
||||
|
||||
saveServerConfig(frpcServer: FrpcDesktopServer): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._serverDao
|
||||
.updateById("1", frpcServer)
|
||||
.then(() => {
|
||||
resolve();
|
||||
})
|
||||
.catch(err => reject(err));
|
||||
});
|
||||
async saveServerConfig(
|
||||
frpcServer: FrpcDesktopServer
|
||||
): Promise<FrpcDesktopServer> {
|
||||
return await this._serverDao.updateById("1", frpcServer);
|
||||
}
|
||||
|
||||
getServerConfig(): Promise<FrpcDesktopServer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._serverDao
|
||||
.findById("1")
|
||||
.then((frpcServer: FrpcDesktopServer) => {
|
||||
resolve(frpcServer);
|
||||
})
|
||||
.catch(err => reject(err));
|
||||
});
|
||||
async getServerConfig(): Promise<FrpcDesktopServer> {
|
||||
return await this._serverDao.findById("1");
|
||||
}
|
||||
|
||||
hasServerConfig(): Promise<boolean> {
|
||||
|
@ -8,23 +8,9 @@ import { BrowserWindow } from "electron";
|
||||
import GlobalConstant from "../core/GlobalConstant";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import FileUtils from "../utils/FileUtils";
|
||||
import SecureUtils from "../utils/SecureUtils";
|
||||
import PathUtils from "../utils/PathUtils";
|
||||
|
||||
/**
|
||||
* arch mapping
|
||||
*/
|
||||
const versionMapping = {
|
||||
win32_x64: ["window", "amd64"],
|
||||
win32_arm64: ["window", "arm64"],
|
||||
win32_ia32: ["window", "386"],
|
||||
darwin_arm64: ["darwin", "arm64"],
|
||||
darwin_x64: ["darwin", "amd64"],
|
||||
darwin_amd64: ["darwin", "amd64"],
|
||||
linux_x64: ["linux", "amd64"],
|
||||
linux_arm64: ["linux", "arm64"]
|
||||
};
|
||||
import FileUtils from "../utils/FileUtils";
|
||||
|
||||
class VersionService extends BaseService<FrpcVersion> {
|
||||
private readonly _versionDao: VersionDao;
|
||||
@ -43,7 +29,7 @@ class VersionService extends BaseService<FrpcVersion> {
|
||||
this._gitHubService = gitHubService;
|
||||
this._fileService = fileService;
|
||||
const nodeVersion = `${process.platform}_${process.arch}`;
|
||||
this._currFrpArch = versionMapping[nodeVersion];
|
||||
this._currFrpArch = GlobalConstant.FRP_ARCH_VERSION_MAPPING[nodeVersion];
|
||||
}
|
||||
|
||||
downloadFrpVersion(githubReleaseId: number, onProgress: Function) {
|
||||
@ -65,6 +51,9 @@ class VersionService extends BaseService<FrpcVersion> {
|
||||
SecureUtils.calculateMD5(version.name)
|
||||
);
|
||||
|
||||
if (fs.existsSync(versionFilePath)) {
|
||||
fs.rmSync(versionFilePath, { recursive: true, force: true });
|
||||
}
|
||||
// const targetPath = path.resolve();
|
||||
download(BrowserWindow.getFocusedWindow(), url, {
|
||||
filename: `${version.assetName}`,
|
||||
@ -73,42 +62,78 @@ class VersionService extends BaseService<FrpcVersion> {
|
||||
onProgress(progress);
|
||||
},
|
||||
onCompleted: () => {
|
||||
if (fs.existsSync(versionFilePath)) {
|
||||
fs.rmSync(versionFilePath, { recursive: true, force: true });
|
||||
}
|
||||
const ext = path.extname(version.assetName);
|
||||
if (ext === GlobalConstant.ZIP_EXT) {
|
||||
this._fileService.decompressZipFile(
|
||||
downloadedFilePath,
|
||||
versionFilePath
|
||||
);
|
||||
// todo delete frps and other file.
|
||||
} else if (
|
||||
ext === GlobalConstant.GZ_EXT &&
|
||||
version.assetName.includes(GlobalConstant.TAR_GZ_EXT)
|
||||
) {
|
||||
this._fileService.decompressTarGzFile(
|
||||
downloadedFilePath,
|
||||
versionFilePath
|
||||
versionFilePath,
|
||||
() => {
|
||||
// rename frpc.
|
||||
const frpcFilePath = path.join(versionFilePath, "frpc");
|
||||
if (fs.existsSync(frpcFilePath)) {
|
||||
const newFrpcFilePath = path.join(
|
||||
versionFilePath,
|
||||
PathUtils.getFrpcFilename()
|
||||
);
|
||||
fs.renameSync(frpcFilePath, newFrpcFilePath);
|
||||
}
|
||||
// delete downloaded file.
|
||||
// todo has bug.
|
||||
const downloadedFile = path.join(
|
||||
PathUtils.getDownloadStoragePath(),
|
||||
version.assetName
|
||||
);
|
||||
if (fs.existsSync(downloadedFile)) {
|
||||
fs.rmSync(downloadedFile, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// todo 2025-02-23 delete downloaded file.
|
||||
version.localPath = versionFilePath;
|
||||
this._versionDao.insert(version).then(data => {
|
||||
resolve(data);
|
||||
});
|
||||
version.downloaded = true;
|
||||
this._versionDao
|
||||
.insert(version)
|
||||
.then(data => {
|
||||
resolve(data);
|
||||
})
|
||||
.catch(err => reject(err));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deleteFrpVersion() {}
|
||||
async deleteFrpVersion(githubReleaseId: number) {
|
||||
if (!githubReleaseId) {
|
||||
return;
|
||||
}
|
||||
const version = await this._versionDao.findByGithubReleaseId(
|
||||
githubReleaseId
|
||||
);
|
||||
if (this.frpcVersionExists(version)) {
|
||||
fs.rmSync(version.localPath, { recursive: true, force: true });
|
||||
await this._versionDao.deleteById(version._id);
|
||||
}
|
||||
}
|
||||
|
||||
getFrpVersionsByGitHub(): Promise<Array<FrpcVersion>> {
|
||||
async getFrpVersionsByGitHub(): Promise<Array<FrpcVersion>> {
|
||||
return new Promise<Array<FrpcVersion>>((resolve, reject) => {
|
||||
this._gitHubService
|
||||
.getGithubRepoAllReleases("fatedier/frp")
|
||||
.then((releases: Array<GithubRelease>) => {
|
||||
.then(async (releases: Array<GithubRelease>) => {
|
||||
const versions: Array<FrpcVersion> =
|
||||
this.githubRelease2FrpcVersion(releases);
|
||||
await this.githubRelease2FrpcVersion(releases);
|
||||
// const versions: Array<FrpcVersion> = (this.versions = versions);
|
||||
this.versions = versions;
|
||||
resolve(versions);
|
||||
})
|
||||
@ -116,11 +141,8 @@ class VersionService extends BaseService<FrpcVersion> {
|
||||
});
|
||||
}
|
||||
|
||||
getFrpVersionByLocalJson(): Promise<Array<FrpcVersion>> {
|
||||
return new Promise<Array<FrpcVersion>>(resolve => {
|
||||
const versions = this.githubRelease2FrpcVersion(frpReleasesJson);
|
||||
resolve(versions);
|
||||
});
|
||||
async getFrpVersionByLocalJson(): Promise<Array<FrpcVersion>> {
|
||||
return this.githubRelease2FrpcVersion(frpReleasesJson);
|
||||
}
|
||||
|
||||
getFrpVersion() {}
|
||||
@ -131,34 +153,24 @@ class VersionService extends BaseService<FrpcVersion> {
|
||||
});
|
||||
}
|
||||
|
||||
private githubRelease2FrpcVersion(
|
||||
private async githubRelease2FrpcVersion(
|
||||
releases: Array<GithubRelease>
|
||||
): Array<FrpcVersion> {
|
||||
): Promise<Array<FrpcVersion>> {
|
||||
const allVersions = await this._versionDao.findAll();
|
||||
return releases
|
||||
.filter(release => {
|
||||
return this.findCurrentArchitectureAsset(release.assets);
|
||||
})
|
||||
.map(m => {
|
||||
const asset = this.findCurrentArchitectureAsset(m.assets);
|
||||
// m.assets.forEach((ma: GithubAsset) => {
|
||||
// // if (asset) {
|
||||
// // const absPath = path.join(
|
||||
// // frpPath,
|
||||
// // asset.name.replace(/(\.tar\.gz|\.zip)$/, "")
|
||||
// // );
|
||||
// // m.absPath = absPath;
|
||||
// // m.download_completed = fs.existsSync(absPath);
|
||||
// m.download_count = download_count;
|
||||
// m.size = formatBytes(ma.size);
|
||||
// // }
|
||||
// });
|
||||
// const asset = getAdaptiveAsset(m.id);
|
||||
const download_count = m.assets.reduce(
|
||||
(sum, item) => sum + item.download_count,
|
||||
0
|
||||
);
|
||||
|
||||
const currVersion = allVersions.find(ff => ff.githubReleaseId === m.id);
|
||||
const v: FrpcVersion = {
|
||||
_id: "",
|
||||
githubReleaseId: m.id,
|
||||
githubAssetId: asset.id,
|
||||
githubCreatedAt: asset.created_at,
|
||||
@ -167,25 +179,24 @@ class VersionService extends BaseService<FrpcVersion> {
|
||||
versionDownloadCount: download_count,
|
||||
assetDownloadCount: asset.download_count,
|
||||
browserDownloadUrl: asset.browser_download_url,
|
||||
downloaded: false,
|
||||
localPath: "",
|
||||
downloaded: this.frpcVersionExists(currVersion),
|
||||
localPath: currVersion && currVersion.localPath,
|
||||
size: FileUtils.formatBytes(asset.size)
|
||||
};
|
||||
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
// private async frpcVersionExists(githubReleaseId: number): boolean {
|
||||
// const version = await this._versionDao.findByGithubReleaseId(
|
||||
// githubReleaseId
|
||||
// );
|
||||
//
|
||||
// if (version) {
|
||||
// return fs.existsSync(version.localPath);
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
private frpcVersionExists(version: FrpcVersion): boolean {
|
||||
// const version = await this._versionDao.findByGithubReleaseId(
|
||||
// githubReleaseId
|
||||
// );
|
||||
|
||||
if (version) {
|
||||
return fs.existsSync(version.localPath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export default VersionService;
|
||||
|
@ -1,39 +1,21 @@
|
||||
class IdUtils {
|
||||
public static genUUID() {
|
||||
if (typeof crypto === "object") {
|
||||
if (typeof crypto.randomUUID === "function") {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
if (
|
||||
typeof crypto.getRandomValues === "function" &&
|
||||
typeof Uint8Array === "function"
|
||||
) {
|
||||
const callback = c => {
|
||||
const num = Number(c);
|
||||
return (
|
||||
num ^
|
||||
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))
|
||||
).toString(16);
|
||||
};
|
||||
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, callback);
|
||||
let uuidValue = "",
|
||||
k,
|
||||
randomValue;
|
||||
for (k = 0; k < 32; k++) {
|
||||
randomValue = (Math.random() * 16) | 0;
|
||||
|
||||
if (k == 8 || k == 12 || k == 16 || k == 20) {
|
||||
uuidValue += "-";
|
||||
}
|
||||
uuidValue += (
|
||||
k == 12 ? 4 : k == 16 ? (randomValue & 3) | 8 : randomValue
|
||||
).toString(16);
|
||||
}
|
||||
let timestamp = new Date().getTime();
|
||||
let perforNow =
|
||||
(typeof performance !== "undefined" &&
|
||||
performance.now &&
|
||||
performance.now() * 1000) ||
|
||||
0;
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
|
||||
let random = Math.random() * 16;
|
||||
if (timestamp > 0) {
|
||||
random = (timestamp + random) % 16 | 0;
|
||||
timestamp = Math.floor(timestamp / 16);
|
||||
} else {
|
||||
random = (perforNow + random) % 16 | 0;
|
||||
perforNow = Math.floor(perforNow / 16);
|
||||
}
|
||||
return (c === "x" ? random : (random & 0x3) | 0x8).toString(16);
|
||||
});
|
||||
return uuidValue;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default IdUtils;
|
||||
|
@ -18,6 +18,10 @@ class PathUtils {
|
||||
);
|
||||
}
|
||||
|
||||
public static getFrpcFilename() {
|
||||
return SecureUtils.calculateMD5("frpc")
|
||||
}
|
||||
|
||||
public static getAppData() {
|
||||
return app.getPath("userData");
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export function success<T>(data: any, message?: string) {
|
||||
export function success<T>(data?: any, message?: string) {
|
||||
const resp: ApiResponse<T> = {
|
||||
success: true,
|
||||
data: data,
|
||||
@ -7,6 +7,15 @@ export function success<T>(data: any, message?: string) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
// export function success(message?: string) {
|
||||
// const resp: ApiResponse<void> = {
|
||||
// success: true,
|
||||
// data: null,
|
||||
// message: message || "successful."
|
||||
// };
|
||||
// return resp;
|
||||
// }
|
||||
|
||||
export function fail(message?: string) {
|
||||
const resp: ApiResponse<any> = {
|
||||
success: false,
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { ipcRenderer } from "electron";
|
||||
|
||||
export const send = (router: IpcRouter) => {
|
||||
export const send = (router: IpcRouter, params?: any) => {
|
||||
console.log(router, "send.router");
|
||||
ipcRenderer.send(router.path);
|
||||
ipcRenderer.send(router.path, params);
|
||||
};
|
||||
|
||||
export const on = (router: IpcRouter, listerHandler: (data: any) => void) => {
|
||||
|
@ -179,7 +179,7 @@ const rules = reactive<FormRules>({
|
||||
]
|
||||
});
|
||||
|
||||
const versions = ref<Array<FrpVersion>>([]);
|
||||
const versions = ref<Array<FrpcVersion>>([]);
|
||||
const copyServerConfigBase64 = ref();
|
||||
const pasteServerConfigBase64 = ref();
|
||||
|
||||
@ -234,13 +234,28 @@ const checkAndResetVersion = () => {
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const handleLoadDownloadedVersion = () => {
|
||||
send(ipcRouters.VERSION.getDownloadedVersions);
|
||||
};
|
||||
|
||||
const handleLoadSavedConfig = () => {
|
||||
send(ipcRouters.SERVER.getServerConfig);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
handleLoadDownloadedVersion();
|
||||
handleLoadSavedConfig();
|
||||
|
||||
on(ipcRouters.SERVER.getServerConfig, data => {
|
||||
console.log("data", data);
|
||||
loading.value--;
|
||||
});
|
||||
|
||||
on(ipcRouters.VERSION.getDownloadedVersions, data => {
|
||||
console.log("versions", data);
|
||||
versions.value = data;
|
||||
// checkAndResetVersion();
|
||||
});
|
||||
// ipcRenderer.send("config.getConfig");
|
||||
// handleLoadVersions();
|
||||
// ipcRenderer.on("Config.getConfig.hook", (event, args) => {
|
||||
@ -541,13 +556,13 @@ onUnmounted(() => {
|
||||
>
|
||||
<el-option
|
||||
v-for="v in versions"
|
||||
:key="v.id"
|
||||
:key="v.githubReleaseId"
|
||||
:label="v.name"
|
||||
:value="v.id"
|
||||
></el-option>
|
||||
:value="v.githubReleaseId"
|
||||
/>
|
||||
</el-select>
|
||||
<div class="w-full flex justify-end">
|
||||
<el-link type="primary" @click="handleLoadVersions">
|
||||
<el-link type="primary" @click="handleLoadDownloadedVersion">
|
||||
<iconify-icon-offline class="mr-1" icon="refresh-rounded" />
|
||||
手动刷新
|
||||
</el-link>
|
||||
|
@ -7,13 +7,13 @@ import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import IconifyIconOffline from "@/components/IconifyIcon/src/iconifyIconOffline";
|
||||
import { on, send } from "@/utils/ipcUtils";
|
||||
import ipcRouter, { ipcRouters } from "../../../electron/core/IpcRouter";
|
||||
import { ipcRouters } from "../../../electron/core/IpcRouter";
|
||||
|
||||
defineComponent({
|
||||
name: "Download"
|
||||
});
|
||||
|
||||
const versions = ref<Array<FrpVersion>>([]);
|
||||
const versions = ref<Array<FrpcVersion>>([]);
|
||||
const loading = ref(1);
|
||||
const downloadPercentage = ref(0);
|
||||
const downloading = ref<Map<number, number>>(new Map<number, number>());
|
||||
@ -28,28 +28,25 @@ const mirrors = ref<Array<GitHubMirror>>([
|
||||
/**
|
||||
* 获取版本
|
||||
*/
|
||||
const handleLoadVersions = () => {
|
||||
ipcRenderer.send("github.getFrpVersions", currMirror.value);
|
||||
const handleLoadAllVersions = () => {
|
||||
send(ipcRouters.VERSION.getVersions);
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载
|
||||
* @param version
|
||||
*/
|
||||
const handleDownload = useDebounceFn((version: FrpVersion) => {
|
||||
// console.log(version, currMirror.value);
|
||||
ipcRenderer.send("github.download", {
|
||||
versionId: version.id,
|
||||
mirror: currMirror.value
|
||||
const handleDownload = useDebounceFn((version: FrpcVersion) => {
|
||||
send(ipcRouters.VERSION.downloadVersion, {
|
||||
githubReleaseId: version.githubReleaseId
|
||||
});
|
||||
downloading.value.set(version.id, 0);
|
||||
}, 300);
|
||||
|
||||
/**
|
||||
* 删除下载
|
||||
* @param version
|
||||
*/
|
||||
const handleDeleteVersion = useDebounceFn((version: FrpVersion) => {
|
||||
const handleDeleteVersion = useDebounceFn((version: FrpcVersion) => {
|
||||
ElMessageBox.alert(
|
||||
`确认要删除 <span class="text-primary font-bold">${version.name} </span> 吗?`,
|
||||
"提示",
|
||||
@ -60,85 +57,86 @@ const handleDeleteVersion = useDebounceFn((version: FrpVersion) => {
|
||||
confirmButtonText: "删除"
|
||||
}
|
||||
).then(() => {
|
||||
ipcRenderer.send("github.deleteVersion", {
|
||||
id: version.id,
|
||||
absPath: version.absPath
|
||||
send(ipcRouters.VERSION.deleteDownloadedVersion, {
|
||||
githubReleaseId: version.githubReleaseId
|
||||
});
|
||||
// ipcRenderer.send("github.deleteVersion", {
|
||||
// id: version.id,
|
||||
// absPath: version.absPath
|
||||
// });
|
||||
});
|
||||
}, 300);
|
||||
|
||||
const handleInitDownloadHook = () => {
|
||||
ipcRenderer.on("Download.frpVersionHook", (event, args) => {
|
||||
loading.value--;
|
||||
versions.value = args.map(m => {
|
||||
m.published_at = moment(m.published_at).format("YYYY-MM-DD");
|
||||
return m as FrpVersion;
|
||||
}) as Array<FrpVersion>;
|
||||
console.log(versions, "versions");
|
||||
});
|
||||
// 进度监听
|
||||
ipcRenderer.on("Download.frpVersionDownloadOnProgress", (event, args) => {
|
||||
const { id, progress } = args;
|
||||
downloading.value.set(
|
||||
id,
|
||||
Number(Number(progress.percent * 100).toFixed(2))
|
||||
);
|
||||
});
|
||||
ipcRenderer.on("Download.frpVersionDownloadOnCompleted", (event, args) => {
|
||||
downloading.value.delete(args);
|
||||
const version: FrpVersion | undefined = versions.value.find(
|
||||
f => f.id === args
|
||||
);
|
||||
if (version) {
|
||||
version.download_completed = true;
|
||||
}
|
||||
});
|
||||
ipcRenderer.on("Download.deleteVersion.hook", (event, args) => {
|
||||
const { err, data } = args;
|
||||
if (!err) {
|
||||
loading.value++;
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "删除成功"
|
||||
});
|
||||
handleLoadVersions();
|
||||
}
|
||||
});
|
||||
ipcRenderer.on("Download.importFrpFile.hook", (event, args) => {
|
||||
const { success, data } = args;
|
||||
console.log(args);
|
||||
|
||||
// if (err) {
|
||||
loading.value++;
|
||||
ElMessage({
|
||||
type: success ? "success" : "error",
|
||||
message: data
|
||||
});
|
||||
handleLoadVersions();
|
||||
// }
|
||||
});
|
||||
};
|
||||
// const handleInitDownloadHook = () => {
|
||||
// ipcRenderer.on("Download.deleteVersion.hook", (event, args) => {
|
||||
// const { err, data } = args;
|
||||
// if (!err) {
|
||||
// loading.value++;
|
||||
// ElMessage({
|
||||
// type: "success",
|
||||
// message: "删除成功"
|
||||
// });
|
||||
// handleLoadVersions();
|
||||
// }
|
||||
// });
|
||||
// ipcRenderer.on("Download.importFrpFile.hook", (event, args) => {
|
||||
// const { success, data } = args;
|
||||
// console.log(args);
|
||||
//
|
||||
// // if (err) {
|
||||
// loading.value++;
|
||||
// ElMessage({
|
||||
// type: success ? "success" : "error",
|
||||
// message: data
|
||||
// });
|
||||
// handleLoadVersions();
|
||||
// // }
|
||||
// });
|
||||
// };
|
||||
|
||||
const handleMirrorChange = () => {
|
||||
handleLoadVersions();
|
||||
handleLoadAllVersions();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
handleLoadAllVersions();
|
||||
|
||||
send(ipcRouters.VERSION.getVersions);
|
||||
|
||||
on(ipcRouters.VERSION.getVersions, (data) => {
|
||||
console.log('versionData', data);
|
||||
// versions.value = args.map(m => {
|
||||
// m.published_at = moment(m.published_at).format("YYYY-MM-DD");
|
||||
// return m as FrpVersion;
|
||||
// }) as Array<FrpVersion>;
|
||||
on(ipcRouters.VERSION.getVersions, data => {
|
||||
console.log("versionData", data);
|
||||
versions.value = data.map(m => {
|
||||
m.githubCreatedAt = moment(m.githubCreatedAt).format("YYYY-MM-DD");
|
||||
return m as FrpcVersion;
|
||||
}) as Array<FrpcVersion>;
|
||||
loading.value--;
|
||||
});
|
||||
|
||||
on(ipcRouters.VERSION.downloadVersion, data => {
|
||||
console.log("downloadData", data);
|
||||
const { githubReleaseId, completed, percent } = data;
|
||||
if (completed) {
|
||||
downloading.value.delete(githubReleaseId);
|
||||
const version: FrpcVersion | undefined = versions.value.find(
|
||||
f => f.githubReleaseId === githubReleaseId
|
||||
);
|
||||
if (version) {
|
||||
version.downloaded = true;
|
||||
}
|
||||
} else {
|
||||
downloading.value.set(
|
||||
githubReleaseId,
|
||||
Number(Number(percent * 100).toFixed(2))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
on(ipcRouters.VERSION.deleteDownloadedVersion, () => {
|
||||
loading.value++;
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "删除成功"
|
||||
});
|
||||
handleLoadAllVersions();
|
||||
});
|
||||
// handleLoadVersions();
|
||||
// handleInitDownloadHook();
|
||||
// ipcRenderer.invoke("process").then((r: any) => {
|
||||
// console.log(r, "rrr");
|
||||
// });
|
||||
});
|
||||
|
||||
const handleImportFrp = () => {
|
||||
@ -211,7 +209,7 @@ onUnmounted(() => {
|
||||
<!-- </el-col>-->
|
||||
<el-col
|
||||
v-for="version in versions"
|
||||
:key="version.id"
|
||||
:key="version.githubAssetId"
|
||||
:lg="6"
|
||||
:md="8"
|
||||
:sm="12"
|
||||
@ -235,19 +233,19 @@ onUnmounted(() => {
|
||||
<span class="text-primary font-bold"
|
||||
>{{
|
||||
// moment(version.published_at).format("YYYY-MM-DD HH:mm:ss")
|
||||
version.download_count
|
||||
version.versionDownloadCount
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-[12px]">
|
||||
发布时间:<span class="text-primary font-bold">{{
|
||||
// moment(version.published_at).format("YYYY-MM-DD HH:mm:ss")
|
||||
version.published_at
|
||||
version.githubCreatedAt
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div v-if="version.download_completed">
|
||||
<div v-if="version.downloaded">
|
||||
<!-- <span class="text-[12px] text-primary font-bold mr-2"-->
|
||||
<!-- >已下载</span-->
|
||||
<!-- >-->
|
||||
@ -276,9 +274,12 @@ onUnmounted(() => {
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div class="w-32" v-if="downloading.has(version.id)">
|
||||
<div
|
||||
class="w-32"
|
||||
v-if="downloading.has(version.githubReleaseId)"
|
||||
>
|
||||
<el-progress
|
||||
:percentage="downloading.get(version.id)"
|
||||
:percentage="downloading.get(version.githubReleaseId)"
|
||||
:text-inside="false"
|
||||
/>
|
||||
</div>
|
||||
|
2
types/core.d.ts
vendored
2
types/core.d.ts
vendored
@ -8,7 +8,7 @@ interface ControllerParam {
|
||||
win: BrowserWindow;
|
||||
channel: string;
|
||||
event: Electron.IpcMainEvent;
|
||||
args: any[];
|
||||
args: any;
|
||||
}
|
||||
|
||||
interface ListenerParam {
|
||||
|
6
types/frpc-desktop.d.ts
vendored
6
types/frpc-desktop.d.ts
vendored
@ -1,5 +1,9 @@
|
||||
type FrpcDesktopProxy = FrpcProxyConfig & {};
|
||||
|
||||
interface BaseEntity {
|
||||
_id: string;
|
||||
};
|
||||
|
||||
interface FrpcSystemConfiguration {
|
||||
launchAtStartup: boolean;
|
||||
silentStartup: boolean;
|
||||
@ -10,7 +14,7 @@ type FrpcDesktopServer = FrpcCommonConfig & {
|
||||
frpcVersion: number;
|
||||
};
|
||||
|
||||
type FrpcVersion = {
|
||||
type FrpcVersion = BaseEntity & {
|
||||
githubReleaseId: number;
|
||||
githubAssetId: number;
|
||||
githubCreatedAt: string;
|
||||
|
Loading…
Reference in New Issue
Block a user