🏗️ lower electron version
This commit is contained in:
parent
183a86d10b
commit
3ef92a8af9
@ -1,53 +0,0 @@
|
||||
import { app, ipcMain, shell } from "electron";
|
||||
import { logInfo, logError, LogModule } from "../utils/log";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
export const initLoggerApi = () => {
|
||||
const logPath = path.join(app.getPath("userData"), "frpc.log");
|
||||
|
||||
const readLogger = (callback: (content: string) => void) => {
|
||||
fs.readFile(logPath, "utf-8", (error, data) => {
|
||||
if (!error) {
|
||||
logInfo(LogModule.APP, "Log file read successfully.");
|
||||
callback(data);
|
||||
} else {
|
||||
logError(LogModule.APP, `Error reading log file: ${error.message}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ipcMain.on("logger.getLog", async (event, args) => {
|
||||
logInfo(LogModule.APP, "Received request to get log.");
|
||||
readLogger(content => {
|
||||
event.reply("Logger.getLog.hook", content);
|
||||
logInfo(LogModule.APP, "Log data sent to client.");
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on("logger.update", (event, args) => {
|
||||
logInfo(LogModule.APP, "Watching log file for changes.");
|
||||
fs.watch(logPath, (eventType, filename) => {
|
||||
if (eventType === "change") {
|
||||
logInfo(LogModule.APP, "Log file changed, reading new content.");
|
||||
readLogger(content => {
|
||||
event.reply("Logger.update.hook", content);
|
||||
logInfo(LogModule.APP, "Updated log data sent to client.");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on("logger.openLog", (event, args) => {
|
||||
logInfo(LogModule.APP, "Attempting to open log file.");
|
||||
shell.openPath(logPath).then((errorMessage) => {
|
||||
if (errorMessage) {
|
||||
logError(LogModule.APP, `Failed to open Logger: ${errorMessage}`);
|
||||
event.reply("Logger.openLog.hook", false);
|
||||
} else {
|
||||
logInfo(LogModule.APP, "Logger opened successfully.");
|
||||
event.reply("Logger.openLog.hook", true);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
21
electron/controller/BaseController.ts
Normal file
21
electron/controller/BaseController.ts
Normal file
@ -0,0 +1,21 @@
|
||||
class BaseController {
|
||||
// success<T>(data: any, message?: string) {
|
||||
// const resp: ApiResponse<T> = {
|
||||
// success: true,
|
||||
// data: data,
|
||||
// message: message || "successful."
|
||||
// };
|
||||
// return resp;
|
||||
// }
|
||||
//
|
||||
// fail(message?: string) {
|
||||
// const resp: ApiResponse<any> = {
|
||||
// success: false,
|
||||
// data: null,
|
||||
// message: message || "internal error."
|
||||
// };
|
||||
// return resp;
|
||||
// }
|
||||
}
|
||||
|
||||
export default BaseController;
|
5
electron/controller/LaunchController.ts
Normal file
5
electron/controller/LaunchController.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import BaseController from "./BaseController";
|
||||
|
||||
class LaunchController extends BaseController {
|
||||
|
||||
}
|
39
electron/controller/LogController.ts
Normal file
39
electron/controller/LogController.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import BaseController from "./BaseController";
|
||||
import LogService from "../service/LogService";
|
||||
import { fail, success } from "../utils/response";
|
||||
|
||||
class LogController extends BaseController {
|
||||
private readonly _logService: LogService;
|
||||
|
||||
constructor(logService: LogService) {
|
||||
super();
|
||||
this._logService = logService;
|
||||
console.log("logService2", this._logService);
|
||||
}
|
||||
|
||||
getFrpLogContent(req: ControllerRequest) {
|
||||
console.log("logService3", this._logService);
|
||||
this._logService.getFrpLogContent().then(data => {
|
||||
req.event.reply(req.reply, success(data));
|
||||
});
|
||||
}
|
||||
|
||||
// watchFrpcLogContent(req: ControllerRequest) {
|
||||
// this._logService.watchFrpcLog().then(data => {
|
||||
// console.log('reply watch', data);
|
||||
// req.event.reply(req.reply, this.success(data));
|
||||
// });
|
||||
// }
|
||||
|
||||
openFrpcLogFile(req: ControllerRequest) {
|
||||
this._logService.openFrpcLogFile().then(data => {
|
||||
if (data) {
|
||||
success(null);
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default LogController;
|
7
electron/controller/ProxyController.ts
Normal file
7
electron/controller/ProxyController.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import BaseController from "./BaseController";
|
||||
|
||||
class ProxyController extends BaseController {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
18
electron/controller/ServerController.ts
Normal file
18
electron/controller/ServerController.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import BaseController from "./BaseController";
|
||||
import ServerService from "../service/ServerService";
|
||||
import IpcMainEvent = Electron.IpcMainEvent;
|
||||
|
||||
class ServerController extends BaseController {
|
||||
serverService: ServerService;
|
||||
|
||||
constructor(serverService: ServerService) {
|
||||
super();
|
||||
this.serverService = serverService;
|
||||
}
|
||||
|
||||
saveConfig(event: IpcMainEvent, ...args: any[]) {
|
||||
console.log("test", args);
|
||||
}
|
||||
}
|
||||
|
||||
export default ServerController;
|
75
electron/core/IpcRouter.ts
Normal file
75
electron/core/IpcRouter.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import ServerController from "../controller/ServerController";
|
||||
import ServerDao from "../dao/ServerDao";
|
||||
import ServerService from "../service/ServerService";
|
||||
import LogService from "../service/LogService";
|
||||
import { BrowserWindow, ipcMain } from "electron";
|
||||
import LogController from "../controller/LogController";
|
||||
import FileService from "../service/FileService";
|
||||
|
||||
type IpcRouter = {
|
||||
path: string;
|
||||
reply: string;
|
||||
controller: any;
|
||||
instance: any;
|
||||
};
|
||||
|
||||
class IpcRouterConfigurate {
|
||||
ipcRouters: Array<IpcRouter>;
|
||||
private readonly _win: BrowserWindow;
|
||||
|
||||
constructor(win: BrowserWindow) {
|
||||
this._win = win;
|
||||
const serverDao = new ServerDao();
|
||||
const fileService = new FileService();
|
||||
const serverService = new ServerService(serverDao);
|
||||
const logService = new LogService(fileService);
|
||||
const serverController = new ServerController(serverService);
|
||||
const logController = new LogController(logService);
|
||||
|
||||
logService.watchFrpcLog(win);
|
||||
|
||||
this.ipcRouters = [
|
||||
{
|
||||
path: "server/test",
|
||||
reply: "server/test.hook",
|
||||
controller: serverController.saveConfig,
|
||||
instance: serverController
|
||||
},
|
||||
{
|
||||
path: "log/getFrpLogContent",
|
||||
reply: "log/getFrpLogContent.hook",
|
||||
controller: logController.getFrpLogContent,
|
||||
instance: logController
|
||||
},
|
||||
// {
|
||||
// path: "log/watchFrpcLogContent",
|
||||
// reply: "log/watchFrpcLogContent.hook",
|
||||
// controller: logController.watchFrpcLogContent,
|
||||
// instance: logController
|
||||
// },
|
||||
{
|
||||
path: "log/openFrpcLogFile",
|
||||
reply: "log/openFrpcLogFile.hook",
|
||||
controller: logController.openFrpcLogFile,
|
||||
instance: logController
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
init() {
|
||||
this.ipcRouters.forEach(router => {
|
||||
ipcMain.on(router.path, (event, args) => {
|
||||
const req: ControllerRequest = {
|
||||
win: this._win,
|
||||
reply: router.reply,
|
||||
event: event,
|
||||
args: args
|
||||
};
|
||||
router.controller.call(router.instance, req);
|
||||
});
|
||||
});
|
||||
console.log("ipcRouter init success.");
|
||||
}
|
||||
}
|
||||
|
||||
export default IpcRouterConfigurate;
|
48
electron/dao/BaseDao.ts
Normal file
48
electron/dao/BaseDao.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import Datastore from "nedb";
|
||||
import path from "path";
|
||||
import { app } from "electron";
|
||||
|
||||
interface BaseDaoInterface<T> {
|
||||
db: Datastore;
|
||||
|
||||
insert(t: T): Promise<T>;
|
||||
|
||||
updateById(t: T): T;
|
||||
|
||||
deleteById(id: string): void;
|
||||
|
||||
findAll(): T[];
|
||||
}
|
||||
|
||||
class BaseDao<T> implements BaseDaoInterface<T> {
|
||||
db: Datastore;
|
||||
|
||||
constructor(dbName: string) {
|
||||
const dbFilename = path.join(app.getPath("userData"), `${dbName}-v2.db`);
|
||||
this.db = new Datastore({
|
||||
autoload: true,
|
||||
filename: dbFilename
|
||||
});
|
||||
// todo log
|
||||
}
|
||||
|
||||
async insert(t: T): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
resolve(t);
|
||||
});
|
||||
}
|
||||
|
||||
updateById(t: T): T {
|
||||
return null;
|
||||
}
|
||||
|
||||
deleteById(id: string): void {
|
||||
return null;
|
||||
}
|
||||
|
||||
findAll(): T[] {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseDao;
|
9
electron/dao/ServerDao.ts
Normal file
9
electron/dao/ServerDao.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import BaseDao from "./BaseDao";
|
||||
|
||||
class ServerDao extends BaseDao<FrpcDesktopServer> {
|
||||
constructor() {
|
||||
super("config");
|
||||
}
|
||||
}
|
||||
|
||||
export default ServerDao
|
11
electron/electron-env.d.ts
vendored
11
electron/electron-env.d.ts
vendored
@ -1,11 +0,0 @@
|
||||
/// <reference types="vite-plugin-electron/electron-env" />
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
VSCODE_DEBUG?: 'true'
|
||||
DIST_ELECTRON: string
|
||||
DIST: string
|
||||
/** /dist/ or /public/ */
|
||||
VITE_PUBLIC: string
|
||||
}
|
||||
}
|
@ -18,13 +18,13 @@ import {
|
||||
startFrpWorkerProcess,
|
||||
stopFrpcProcess
|
||||
} from "../api/frpc";
|
||||
import { initLoggerApi } from "../api/logger";
|
||||
import { initFileApi } from "../api/file";
|
||||
import { getConfig } from "../storage/config";
|
||||
import { initCommonApi } from "../api/common";
|
||||
import { initLocalApi } from "../api/local";
|
||||
import { initLog, logError, logInfo, LogModule } from "../utils/log";
|
||||
import { maskSensitiveData } from "../utils/desensitize";
|
||||
import IpcRouterConfigurate from "../core/IpcRouter";
|
||||
|
||||
process.env.DIST_ELECTRON = join(__dirname, "..");
|
||||
process.env.DIST = join(process.env.DIST_ELECTRON, "../dist");
|
||||
@ -192,6 +192,8 @@ app.whenReady().then(() => {
|
||||
startFrpWorkerProcess(config);
|
||||
}
|
||||
}
|
||||
const ipcRouterConfig = new IpcRouterConfigurate(win);
|
||||
ipcRouterConfig.init();
|
||||
// Initialize APIs
|
||||
try {
|
||||
initGitHubApi(win);
|
||||
@ -206,8 +208,7 @@ app.whenReady().then(() => {
|
||||
initFrpcApi();
|
||||
logInfo(LogModule.APP, `FRPC API initialized.`);
|
||||
|
||||
initLoggerApi();
|
||||
logInfo(LogModule.APP, `Logger API initialized.`);
|
||||
// logInfo(LogModule.APP, `Logger API initialized.`);
|
||||
|
||||
initFileApi();
|
||||
logInfo(LogModule.APP, `File API initialized.`);
|
||||
|
15
electron/service/BaseService.ts
Normal file
15
electron/service/BaseService.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import BaseDao from "../dao/BaseDao";
|
||||
|
||||
interface BaseServiceInterface<T> {
|
||||
dao: BaseDao<T>;
|
||||
}
|
||||
|
||||
class BaseService<T> implements BaseServiceInterface<T> {
|
||||
dao: BaseDao<T>;
|
||||
|
||||
constructor(dao: BaseDao<T>) {
|
||||
this.dao = dao;
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseService;
|
24
electron/service/FileService.ts
Normal file
24
electron/service/FileService.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { shell } from "electron";
|
||||
|
||||
class FileService {
|
||||
constructor() {}
|
||||
|
||||
openFile(filePath: string) {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
shell
|
||||
.openPath(filePath)
|
||||
.then(errorMessage => {
|
||||
if (errorMessage) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default FileService;
|
59
electron/service/LogService.ts
Normal file
59
electron/service/LogService.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { app, BrowserWindow } from "electron";
|
||||
import FileService from "./FileService";
|
||||
import { success } from "../utils/response";
|
||||
|
||||
class LogService {
|
||||
private readonly _fileService: FileService;
|
||||
private readonly _logPath: string = path.join(
|
||||
app.getPath("userData"),
|
||||
"frpc.log"
|
||||
);
|
||||
|
||||
constructor(fileService: FileService) {
|
||||
this._fileService = fileService;
|
||||
}
|
||||
|
||||
getFrpLogContent(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(this._logPath, "utf-8", (error, data) => {
|
||||
if (!error) {
|
||||
resolve(data);
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
watchFrpcLog(win: BrowserWindow) {
|
||||
fs.watch(this._logPath, (eventType, filename) => {
|
||||
if (eventType === "change") {
|
||||
win.webContents.send(
|
||||
"log/watchFrpcLogContent.hook",
|
||||
success(true)
|
||||
)
|
||||
} else {
|
||||
}
|
||||
});
|
||||
// return new Promise<boolean>((resolve, reject) => {
|
||||
//
|
||||
// });
|
||||
}
|
||||
|
||||
openFrpcLogFile(): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
this._fileService
|
||||
.openFile(this._logPath)
|
||||
.then(result => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default LogService;
|
12
electron/service/ServerService.ts
Normal file
12
electron/service/ServerService.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import BaseService from "./BaseService";
|
||||
import ServerDao from "../dao/ServerDao";
|
||||
|
||||
class ServerService extends BaseService<FrpcDesktopServer> {
|
||||
constructor(serverDao: ServerDao) {
|
||||
super(serverDao);
|
||||
}
|
||||
|
||||
saveServerConfig(frpcServer: FrpcDesktopServer) {}
|
||||
}
|
||||
|
||||
export default ServerService;
|
17
electron/utils/response.ts
Normal file
17
electron/utils/response.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export function success<T>(data: any, message?: string) {
|
||||
const resp: ApiResponse<T> = {
|
||||
success: true,
|
||||
data: data,
|
||||
message: message || "successful."
|
||||
};
|
||||
return resp;
|
||||
}
|
||||
|
||||
export function fail(message?: string) {
|
||||
const resp: ApiResponse<any> = {
|
||||
success: false,
|
||||
data: null,
|
||||
message: message || "internal error."
|
||||
};
|
||||
return resp;
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, defineComponent, onMounted, onUnmounted, ref } from "vue";
|
||||
import { defineComponent, onMounted, onUnmounted, ref } from "vue";
|
||||
import Breadcrumb from "@/layout/compoenets/Breadcrumb.vue";
|
||||
import { ipcRenderer } from "electron";
|
||||
import IconifyIconOffline from "@/components/IconifyIcon/src/iconifyIconOffline";
|
||||
import { useDebounce, useDebounceFn } from "@vueuse/core";
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
defineComponent({
|
||||
@ -12,7 +12,7 @@ defineComponent({
|
||||
|
||||
const loggerContent = ref('<div class="text-white">暂无日志</div>');
|
||||
|
||||
const handleLog2Html = (logContent: string) => {
|
||||
const formatLogContent = (logContent: string) => {
|
||||
const logs = logContent
|
||||
.split("\n")
|
||||
.filter(f => f)
|
||||
@ -31,20 +31,16 @@ const handleLog2Html = (logContent: string) => {
|
||||
});
|
||||
return logs.reverse().join("");
|
||||
};
|
||||
|
||||
const refreshStatus = ref(false);
|
||||
|
||||
const logLoading = ref(true);
|
||||
// const isWatch = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
console.log('logger mounted')
|
||||
ipcRenderer.send("logger.getLog");
|
||||
ipcRenderer.on("Logger.getLog.hook", (event, args) => {
|
||||
// console.log("日志", args, args.indexOf("\n"));
|
||||
// const logs = args.split("\n");
|
||||
// console.log(logs, "2");
|
||||
if (args) {
|
||||
loggerContent.value = handleLog2Html(args);
|
||||
ipcRenderer.send("log/getFrpLogContent");
|
||||
ipcRenderer.on("log/getFrpLogContent.hook", (event, args) => {
|
||||
const { success, data } = args;
|
||||
if (success) {
|
||||
loggerContent.value = formatLogContent(data);
|
||||
}
|
||||
logLoading.value = false;
|
||||
if (refreshStatus.value) {
|
||||
@ -54,18 +50,36 @@ onMounted(() => {
|
||||
message: "刷新成功"
|
||||
});
|
||||
} else {
|
||||
ipcRenderer.send("logger.update");
|
||||
// if (!isWatch.value) {
|
||||
// // ipcRenderer.send("log/watchFrpcLogContent");
|
||||
// isWatch.value = true;
|
||||
// }
|
||||
}
|
||||
});
|
||||
ipcRenderer.on("Logger.update.hook", (event, args) => {
|
||||
console.log("logger update hook", 1);
|
||||
if (args) {
|
||||
loggerContent.value = handleLog2Html(args);
|
||||
ipcRenderer.on("log/watchFrpcLogContent.hook", (event, args) => {
|
||||
console.log(event,'eevent');
|
||||
console.log("watchFrpcLogContent", args);
|
||||
const { success, data } = args;
|
||||
if (success && data) {
|
||||
ipcRenderer.send("log/getFrpLogContent");
|
||||
}
|
||||
// if (args) {
|
||||
// loggerContent.value = formatLogContent(args);
|
||||
// }
|
||||
});
|
||||
|
||||
ipcRenderer.on("Logger.openLog.hook", (event, args) => {
|
||||
if (args) {
|
||||
// ipcRenderer.on("log/watchFrpcLogContent.hook", (event, args) => {
|
||||
// console.log("watchFrpcLogContent", args);
|
||||
// const { success, data } = args;
|
||||
// if (success && data) {
|
||||
// ipcRenderer.send("log/getFrpLogContent");
|
||||
// }
|
||||
// // if (args) {
|
||||
// // loggerContent.value = formatLogContent(args);
|
||||
// // }
|
||||
// });
|
||||
ipcRenderer.on("log/openFrpcLogFile.hook", (event, args) => {
|
||||
const { success } = args;
|
||||
if (success) {
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "打开日志成功"
|
||||
@ -75,7 +89,7 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
const openLocalLog = useDebounceFn(() => {
|
||||
ipcRenderer.send("logger.openLog");
|
||||
ipcRenderer.send("log/openFrpcLogFile");
|
||||
}, 1000);
|
||||
|
||||
const refreshLog = useDebounceFn(() => {
|
||||
@ -86,13 +100,12 @@ const refreshLog = useDebounceFn(() => {
|
||||
// });
|
||||
refreshStatus.value = true;
|
||||
logLoading.value = true;
|
||||
ipcRenderer.send("logger.getLog");
|
||||
ipcRenderer.send("log/getFrpLogContent");
|
||||
}, 300);
|
||||
|
||||
onUnmounted(() => {
|
||||
console.log('logger unmounted')
|
||||
ipcRenderer.removeAllListeners("Logger.getLog.hook");
|
||||
ipcRenderer.removeAllListeners("Logger.openLog.hook");
|
||||
ipcRenderer.removeAllListeners("log/getFrpLogContent.hook");
|
||||
ipcRenderer.removeAllListeners("log/openFrpcLogFile.hook");
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
|
12
types/core.d.ts
vendored
Normal file
12
types/core.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
interface ApiResponse<T> {
|
||||
success: boolean;
|
||||
data: T;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface ControllerRequest {
|
||||
win: BrowserWindow;
|
||||
reply: string;
|
||||
event: Electron.IpcMainEvent;
|
||||
args: any[];
|
||||
}
|
46
types/frp.d.ts
vendored
Normal file
46
types/frp.d.ts
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
type LogConfig = {
|
||||
to: string;
|
||||
level: string;
|
||||
maxDays: number;
|
||||
disablePrintColor: boolean;
|
||||
};
|
||||
|
||||
type AuthConfig = {
|
||||
method: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
type WebServerConfig = {
|
||||
addr: string;
|
||||
port: number;
|
||||
user: string;
|
||||
password: string;
|
||||
pprofEnable: boolean;
|
||||
};
|
||||
|
||||
type TransportConfig = {
|
||||
poolCount: number;
|
||||
protocol: string;
|
||||
connectServerLocalIP: string;
|
||||
};
|
||||
|
||||
interface FrpcCommonConfig {
|
||||
user: string;
|
||||
serverAddr: string;
|
||||
serverPort: number;
|
||||
loginFailExit: boolean;
|
||||
log: LogConfig;
|
||||
auth: AuthConfig;
|
||||
webServer: WebServerConfig;
|
||||
transport: TransportConfig;
|
||||
udpPacketSize: number;
|
||||
// metadatas: MetadataConfig;
|
||||
}
|
||||
|
||||
interface FrpcProxyConfig {
|
||||
name: string;
|
||||
type: string;
|
||||
localIP: string;
|
||||
localPort: number;
|
||||
remotePort: number;
|
||||
}
|
3
types/frpc-desktop.d.ts
vendored
Normal file
3
types/frpc-desktop.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
type FrpcDesktopProxy = FrpcProxyConfig & {};
|
||||
|
||||
type FrpcDesktopServer = FrpcCommonConfig & {};
|
Loading…
Reference in New Issue
Block a user