🚧 类型统一

This commit is contained in:
刘嘉伟 2024-08-16 23:59:40 +08:00
parent 989f9b8a37
commit 3961475421
11 changed files with 921 additions and 595 deletions

View File

@ -74,7 +74,7 @@ const unZip = (zipPath: string, targetPath: string) => {
export const initGitHubApi = () => { export const initGitHubApi = () => {
// 版本 // 版本
let versions = []; let versions: FrpVersion[] = [];
const getVersion = versionId => { const getVersion = versionId => {
return versions.find(f => f.id === versionId); return versions.find(f => f.id === versionId);

View File

@ -2,45 +2,22 @@ import Datastore from "nedb";
import path from "path"; import path from "path";
import { app } from "electron"; import { app } from "electron";
const log = require('electron-log'); const log = require("electron-log");
const configDB = new Datastore({ const configDB = new Datastore({
autoload: true, autoload: true,
filename: path.join(app.getPath("userData"), "config.db") filename: path.join(app.getPath("userData"), "config.db")
}); });
export type Config = {
currentVersion: any;
serverAddr: string;
serverPort: number;
authMethod: string;
authToken: string;
logLevel: string;
logMaxDays: number;
tlsConfigEnable: boolean;
tlsConfigCertFile: string;
tlsConfigKeyFile: string;
tlsConfigTrustedCaFile: string;
tlsConfigServerName: string;
proxyConfigEnable: boolean;
proxyConfigProxyUrl: string;
systemSelfStart: boolean;
systemStartupConnect: boolean;
user: string;
metaToken: string;
transportHeartbeatInterval: number;
transportHeartbeatTimeout: number;
};
/** /**
* *
*/ */
export const saveConfig = ( export const saveConfig = (
document: Config, document: FrpConfig,
cb?: (err: Error | null, numberOfUpdated: number, upsert: boolean) => void cb?: (err: Error | null, numberOfUpdated: number, upsert: boolean) => void
) => { ) => {
document["_id"] = "1"; document["_id"] = "1";
log.debug(`保存日志 ${JSON.stringify(document)}`) log.debug(`保存日志 ${JSON.stringify(document)}`);
configDB.update({ _id: "1" }, document, { upsert: true }, cb); configDB.update({ _id: "1" }, document, { upsert: true }, cb);
}; };
@ -49,7 +26,7 @@ export const saveConfig = (
* @param cb * @param cb
*/ */
export const getConfig = ( export const getConfig = (
cb: (err: Error | null, document: Config) => void cb: (err: Error | null, document: FrpConfig) => void
) => { ) => {
configDB.findOne({ _id: "1" }, cb); configDB.findOne({ _id: "1" }, cb);
}; };

View File

@ -1,22 +1,14 @@
import Datastore from "nedb"; import Datastore from "nedb";
import path from "path"; import path from "path";
import { app } from "electron"; import { app } from "electron";
const log = require('electron-log');
const log = require("electron-log");
const proxyDB = new Datastore({ const proxyDB = new Datastore({
autoload: true, autoload: true,
filename: path.join(app.getPath("userData"), "proxy.db") filename: path.join(app.getPath("userData"), "proxy.db")
}); });
export type Proxy = {
_id: string;
name: string;
type: string;
localIp: string;
localPort: number;
remotePort: number;
customDomains: string[];
};
/** /**
* *
* @param proxy * @param proxy

View File

@ -9,12 +9,12 @@ const versionDB = new Datastore({
}); });
/** /**
* *
* @param proxy * @param version
* @param cb * @param cb
*/ */
export const insertVersion = ( export const insertVersion = (
version: any, version: FrpVersion,
cb?: (err: Error | null, document: any) => void cb?: (err: Error | null, document: any) => void
) => { ) => {
log.debug(`新增版本:${JSON.stringify(version)}`); log.debug(`新增版本:${JSON.stringify(version)}`);
@ -26,14 +26,14 @@ export const insertVersion = (
* @param cb * @param cb
*/ */
export const listVersion = ( export const listVersion = (
callback: (err: Error | null, documents: any[]) => void callback: (err: Error | null, documents: FrpVersion[]) => void
) => { ) => {
versionDB.find({}, callback); versionDB.find({}, callback);
}; };
export const getVersionById = ( export const getVersionById = (
id: string, id: string,
callback: (err: Error | null, document: any) => void callback: (err: Error | null, document: FrpVersion) => void
) => { ) => {
versionDB.findOne({id: id}, callback); versionDB.findOne({id: id}, callback);
}; };

View File

@ -1,9 +0,0 @@
declare module 'element-plus/dist/locale/zh-cn.mjs' {
const zhLocale: any;
export default zhLocale;
}
declare module 'element-plus/dist/locale/en.mjs' {
const enLocale: any;
export default enLocale;
}

View File

@ -7,51 +7,22 @@ import {useDebounceFn} from "@vueuse/core";
import { clone } from "@/utils/clone"; import { clone } from "@/utils/clone";
import { Base64 } from "js-base64"; import { Base64 } from "js-base64";
defineComponent({ defineComponent({
name: "Config" name: "Config"
}); });
type Config = { type ShareLinkConfig = {
currentVersion: string;
serverAddr: string; serverAddr: string;
serverPort: number; serverPort: number;
authMethod: string; authMethod: string;
authToken: string; authToken: string;
logLevel: string;
logMaxDays: number;
tlsConfigEnable: boolean;
tlsConfigCertFile: string;
tlsConfigKeyFile: string;
tlsConfigTrustedCaFile: string;
tlsConfigServerName: string;
proxyConfigEnable: boolean;
proxyConfigProxyUrl: string;
systemSelfStart: boolean;
systemStartupConnect: boolean;
user: string;
metaToken: string;
transportHeartbeatInterval: number; transportHeartbeatInterval: number;
transportHeartbeatTimeout: number; transportHeartbeatTimeout: number;
user: string;
metaToken: string;
}; };
type Version = { const defaultFormData = ref<FrpConfig>({
id: string;
name: string;
}
type ShareLinkConfig = {
serverAddr: string,
serverPort: number,
authMethod: string,
authToken: string,
transportHeartbeatInterval: number,
transportHeartbeatTimeout: number,
user: string,
metaToken: string,
}
const defaultFormData = ref<Config>({
currentVersion: "", currentVersion: "",
serverAddr: "", serverAddr: "",
serverPort: 7000, serverPort: 7000,
@ -71,11 +42,10 @@ const defaultFormData = ref<Config>({
user: "", user: "",
metaToken: "", metaToken: "",
transportHeartbeatInterval: 30, transportHeartbeatInterval: 30,
transportHeartbeatTimeout: 90, transportHeartbeatTimeout: 90
}); });
const formData = ref<FrpConfig>(defaultFormData.value);
const formData = ref<Config>(defaultFormData.value);
const loading = ref(1); const loading = ref(1);
@ -92,22 +62,32 @@ const rules = reactive<FormRules>({
serverPort: [ serverPort: [
{ required: true, message: "请输入服务器端口", trigger: "blur" } { required: true, message: "请输入服务器端口", trigger: "blur" }
], ],
user: [ user: [{ required: true, message: "请输入用户", trigger: "blur" }],
{required: true, message: "请输入用户", trigger: "blur"} metaToken: [{ required: true, message: "请输入多用户令牌", trigger: "blur" }],
],
metaToken: [
{required: true, message: "请输入多用户令牌", trigger: "blur"}
],
authMethod: [{ required: true, message: "请选择验证方式", trigger: "blur" }], authMethod: [{ required: true, message: "请选择验证方式", trigger: "blur" }],
authToken: [{ required: true, message: "请输入 Token 值 ", trigger: "blur" }], authToken: [{ required: true, message: "请输入 Token 值 ", trigger: "blur" }],
logLevel: [{ required: true, message: "请选择日志级别 ", trigger: "blur" }], logLevel: [{ required: true, message: "请选择日志级别 ", trigger: "blur" }],
logMaxDays: [{required: true, message: "请输入日志保留天数 ", trigger: "blur"}], logMaxDays: [
tlsConfigEnable: [{required: true, message: "请选择 TLS 状态", trigger: "change"}], { required: true, message: "请输入日志保留天数 ", trigger: "blur" }
tlsConfigCertFile: [{required: true, message: "请选择 TLS 证书文件", trigger: "change"}], ],
tlsConfigKeyFile: [{required: true, message: "请选择 TLS 密钥文件", trigger: "change"}], tlsConfigEnable: [
tlsConfigTrustedCaFile: [{required: true, message: "请选择 CA 证书文件", trigger: "change"}], { required: true, message: "请选择 TLS 状态", trigger: "change" }
tlsConfigServerName: [{required: true, message: "请输入 TLS Server 名称", trigger: "blur"}], ],
proxyConfigEnable: [{required: true, message: "请选择代理状态", trigger: "change"}], tlsConfigCertFile: [
{ required: true, message: "请选择 TLS 证书文件", trigger: "change" }
],
tlsConfigKeyFile: [
{ required: true, message: "请选择 TLS 密钥文件", trigger: "change" }
],
tlsConfigTrustedCaFile: [
{ required: true, message: "请选择 CA 证书文件", trigger: "change" }
],
tlsConfigServerName: [
{ required: true, message: "请输入 TLS Server 名称", trigger: "blur" }
],
proxyConfigEnable: [
{ required: true, message: "请选择代理状态", trigger: "change" }
],
proxyConfigProxyUrl: [ proxyConfigProxyUrl: [
{ required: true, message: "请输入代理地址", trigger: "change" }, { required: true, message: "请输入代理地址", trigger: "change" },
{ {
@ -117,18 +97,17 @@ const rules = reactive<FormRules>({
} }
], ],
systemSelfStart: [ systemSelfStart: [
{required: true, message: "请选择是否开机自启", trigger: "change"}, { required: true, message: "请选择是否开机自启", trigger: "change" }
], ],
transportHeartbeatInterval: [ transportHeartbeatInterval: [
{required: true, message: "心跳间隔时间不能为空", trigger: "change"}, { required: true, message: "心跳间隔时间不能为空", trigger: "change" }
], ],
transportHeartbeatTimeout: [ transportHeartbeatTimeout: [
{required: true, message: "心跳超时时间不能为空", trigger: "change"}, { required: true, message: "心跳超时时间不能为空", trigger: "change" }
], ]
}); });
const versions = ref<Array<Version>>([]); const versions = ref<Array<FrpVersion>>([]);
const copyServerConfigBase64 = ref(); const copyServerConfigBase64 = ref();
const pasteServerConfigBase64 = ref(); const pasteServerConfigBase64 = ref();
@ -137,7 +116,7 @@ const protocol = ref("frp://");
const visibles = reactive({ const visibles = reactive({
copyServerConfig: false, copyServerConfig: false,
pasteServerConfig: false, pasteServerConfig: false
}); });
const handleSubmit = useDebounceFn(() => { const handleSubmit = useDebounceFn(() => {
@ -156,22 +135,29 @@ const handleLoadVersions = () => {
}; };
const handleAuthMethodChange = e => { const handleAuthMethodChange = e => {
if (e === 'multiuser') { if (e === "multiuser") {
ElMessageBox.alert("多用户插件需要 Frp版本 >= <span class=\"font-black text-[#5A3DAA]\">v0.31.0</span> 请自行选择正确版本", "提示", { ElMessageBox.alert(
'多用户插件需要 Frp版本 >= <span class="font-black text-[#5A3DAA]">v0.31.0</span> 请自行选择正确版本',
"提示",
{
// if you want to disable its autofocus // if you want to disable its autofocus
autofocus: false, autofocus: false,
confirmButtonText: "知道了", confirmButtonText: "知道了",
dangerouslyUseHTMLString: true, dangerouslyUseHTMLString: true
})
} }
);
} }
};
const checkAndResetVersion = () => { const checkAndResetVersion = () => {
const currentVersion = formData.value.currentVersion; const currentVersion = formData.value.currentVersion;
if (currentVersion && !versions.value.some(item => item.id === currentVersion)) { if (
currentVersion &&
!versions.value.some(item => item.id === currentVersion)
) {
formData.value.currentVersion = ""; formData.value.currentVersion = "";
} }
} };
onMounted(() => { onMounted(() => {
ipcRenderer.send("config.getConfig"); ipcRenderer.send("config.getConfig");
@ -180,12 +166,14 @@ onMounted(() => {
const { err, data } = args; const { err, data } = args;
if (!err) { if (!err) {
if (data) { if (data) {
console.log('data', data) console.log("data", data);
if (!data.transportHeartbeatInterval) { if (!data.transportHeartbeatInterval) {
data.transportHeartbeatInterval = defaultFormData.value.transportHeartbeatInterval data.transportHeartbeatInterval =
defaultFormData.value.transportHeartbeatInterval;
} }
if (!data.transportHeartbeatTimeout) { if (!data.transportHeartbeatTimeout) {
data.transportHeartbeatTimeout = defaultFormData.value.transportHeartbeatTimeout data.transportHeartbeatTimeout =
defaultFormData.value.transportHeartbeatTimeout;
} }
formData.value = data; formData.value = data;
} }
@ -213,19 +201,18 @@ const handleSelectFile = (type: number, ext: string[]) => {
ipcRenderer.invoke("file.selectFile", ext).then(r => { ipcRenderer.invoke("file.selectFile", ext).then(r => {
switch (type) { switch (type) {
case 1: case 1:
formData.value.tlsConfigCertFile = r[0] formData.value.tlsConfigCertFile = r[0];
break; break;
case 2: case 2:
formData.value.tlsConfigKeyFile = r[0] formData.value.tlsConfigKeyFile = r[0];
break; break;
case 3: case 3:
formData.value.tlsConfigTrustedCaFile = r[0] formData.value.tlsConfigTrustedCaFile = r[0];
break; break;
} }
console.log(r) console.log(r);
});
}) };
}
/** /**
* 分享配置 * 分享配置
@ -239,14 +226,11 @@ const handleCopyServerConfig2Base64 = useDebounceFn(() => {
transportHeartbeatInterval: formData.value.transportHeartbeatInterval, transportHeartbeatInterval: formData.value.transportHeartbeatInterval,
transportHeartbeatTimeout: formData.value.transportHeartbeatTimeout, transportHeartbeatTimeout: formData.value.transportHeartbeatTimeout,
user: formData.value.user, user: formData.value.user,
metaToken: formData.value.metaToken, metaToken: formData.value.metaToken
}; };
const base64str = Base64.encode( const base64str = Base64.encode(JSON.stringify(shareConfig));
JSON.stringify(shareConfig)
)
copyServerConfigBase64.value = protocol.value + base64str; copyServerConfigBase64.value = protocol.value + base64str;
visibles.copyServerConfig = true; visibles.copyServerConfig = true;
}, 300); }, 300);
/** /**
@ -262,45 +246,45 @@ const handlePasteServerConfigBase64 = useDebounceFn(() => {
type: "warning", type: "warning",
message: "链接不正确 请输入正确的链接" message: "链接不正确 请输入正确的链接"
}); });
} };
if (!pasteServerConfigBase64.value.startsWith(protocol.value)) { if (!pasteServerConfigBase64.value.startsWith(protocol.value)) {
tips() tips();
return return;
} }
const ciphertext = pasteServerConfigBase64.value.replace("frp://", "") const ciphertext = pasteServerConfigBase64.value.replace("frp://", "");
const plaintext = Base64.decode(ciphertext) const plaintext = Base64.decode(ciphertext);
console.log('plain', plaintext) console.log("plain", plaintext);
let serverConfig: ShareLinkConfig = null; let serverConfig: ShareLinkConfig = null;
try { try {
serverConfig = JSON.parse(plaintext) serverConfig = JSON.parse(plaintext);
} catch { } catch {
tips() tips();
return return;
} }
if (!serverConfig && !serverConfig.serverAddr) { if (!serverConfig && !serverConfig.serverAddr) {
tips() tips();
return return;
} }
if (!serverConfig && !serverConfig.serverPort) { if (!serverConfig && !serverConfig.serverPort) {
tips() tips();
return return;
} }
formData.value.serverAddr = serverConfig.serverAddr formData.value.serverAddr = serverConfig.serverAddr;
formData.value.serverPort = serverConfig.serverPort formData.value.serverPort = serverConfig.serverPort;
formData.value.authMethod = serverConfig.authMethod formData.value.authMethod = serverConfig.authMethod;
formData.value.authToken = serverConfig.authToken formData.value.authToken = serverConfig.authToken;
formData.value.transportHeartbeatInterval = serverConfig.transportHeartbeatInterval formData.value.transportHeartbeatInterval =
formData.value.transportHeartbeatTimeout = serverConfig.transportHeartbeatTimeout serverConfig.transportHeartbeatInterval;
formData.value.user = serverConfig.user formData.value.transportHeartbeatTimeout =
formData.value.metaToken = serverConfig.metaToken serverConfig.transportHeartbeatTimeout;
formData.value.user = serverConfig.user;
formData.value.metaToken = serverConfig.metaToken;
handleSubmit(); handleSubmit();
pasteServerConfigBase64.value = ""; pasteServerConfigBase64.value = "";
visibles.pasteServerConfig = false; visibles.pasteServerConfig = false;
}, 300);
}, 300)
onUnmounted(() => { onUnmounted(() => {
ipcRenderer.removeAllListeners("Config.getConfig.hook"); ipcRenderer.removeAllListeners("Config.getConfig.hook");
@ -312,9 +296,7 @@ onUnmounted(() => {
<div class="main"> <div class="main">
<breadcrumb /> <breadcrumb />
<div class="app-container-breadcrumb pr-2" v-loading="loading > 0"> <div class="app-container-breadcrumb pr-2" v-loading="loading > 0">
<div <div class="w-full bg-white p-4 rounded drop-shadow-lg">
class="w-full bg-white p-4 rounded drop-shadow-lg"
>
<el-form <el-form
:model="formData" :model="formData"
:rules="rules" :rules="rules"
@ -327,7 +309,6 @@ onUnmounted(() => {
<div class="h2 flex justify-between"> <div class="h2 flex justify-between">
<div>版本选择</div> <div>版本选择</div>
</div> </div>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="Frp版本" prop="currentVersion"> <el-form-item label="Frp版本" prop="currentVersion">
@ -348,7 +329,11 @@ onUnmounted(() => {
<iconify-icon-offline class="mr-1" icon="refresh-rounded" /> <iconify-icon-offline class="mr-1" icon="refresh-rounded" />
手动刷新 手动刷新
</el-link> </el-link>
<el-link class="ml-2" type="primary" @click="$router.replace({ name: 'Download' })"> <el-link
class="ml-2"
type="primary"
@click="$router.replace({ name: 'Download' })"
>
<IconifyIconOffline class="mr-1" icon="download" /> <IconifyIconOffline class="mr-1" icon="download" />
点击这里去下载 点击这里去下载
</el-link> </el-link>
@ -359,28 +344,36 @@ onUnmounted(() => {
<div class="h2 flex justify-between"> <div class="h2 flex justify-between">
<div>服务器配置</div> <div>服务器配置</div>
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<IconifyIconOffline @click="handleCopyServerConfig2Base64" <IconifyIconOffline
class="mr-2 cursor-pointer text-xl font-bold" icon="content-copy"/> @click="handleCopyServerConfig2Base64"
<IconifyIconOffline @click="handlePasteServerConfig4Base64" class="mr-2 cursor-pointer text-xl font-bold"
class="mr-2 cursor-pointer text-xl font-bold" icon="content-paste-go"/> icon="content-copy"
/>
<IconifyIconOffline
@click="handlePasteServerConfig4Base64"
class="mr-2 cursor-pointer text-xl font-bold"
icon="content-paste-go"
/>
</div> </div>
</div> </div>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="服务器地址:" prop="serverAddr"> <el-form-item label="服务器地址:" prop="serverAddr">
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover placement="top" trigger="hover">
placement="top"
trigger="hover"
>
<template #default> <template #default>
Frps服务端地址 <br/> 支持 <span class="font-black text-[#5A3DAA]">域名</span><span Frps服务端地址 <br />
class="font-black text-[#5A3DAA]">IP</span> 支持
<span class="font-black text-[#5A3DAA]">域名</span
><span class="font-black text-[#5A3DAA]">IP</span>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
@ -408,17 +401,19 @@ onUnmounted(() => {
<el-form-item label="验证方式:" prop="authMethod"> <el-form-item label="验证方式:" prop="authMethod">
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover width="200" placement="top" trigger="hover">
width="200"
placement="top"
trigger="hover"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">auth.method</span> 对应参数<span class="font-black text-[#5A3DAA]"
>auth.method</span
>
</template> </template>
<template #reference> <template #reference>
<!-- <IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/>--> <!-- <IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/>-->
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
@ -440,16 +435,18 @@ onUnmounted(() => {
<el-form-item label="令牌:" prop="authToken"> <el-form-item label="令牌:" prop="authToken">
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover placement="top" trigger="hover" width="200">
placement="top"
trigger="hover"
width="200"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">auth.token</span> 对应参数<span class="font-black text-[#5A3DAA]"
>auth.token</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
@ -466,40 +463,42 @@ onUnmounted(() => {
<el-form-item label="用户:" prop="user"> <el-form-item label="用户:" prop="user">
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover placement="top" trigger="hover">
placement="top"
trigger="hover"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">user</span> 对应参数<span class="font-black text-[#5A3DAA]"
>user</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
用户 用户
</template> </template>
<el-input <el-input placeholder="请输入用户" v-model="formData.user" />
placeholder="请输入用户"
v-model="formData.user"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="formData.authMethod === 'multiuser'"> <el-col :span="12" v-if="formData.authMethod === 'multiuser'">
<el-form-item label="用户令牌:" prop="metaToken"> <el-form-item label="用户令牌:" prop="metaToken">
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover width="200" placement="top" trigger="hover">
width="200"
placement="top"
trigger="hover"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">metadatas.token</span> 对应参数<span class="font-black text-[#5A3DAA]"
>metadatas.token</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
@ -513,27 +512,38 @@ onUnmounted(() => {
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="心跳间隔:" prop="transportHeartbeatInterval"> <el-form-item
label="心跳间隔:"
prop="transportHeartbeatInterval"
>
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover width="300" placement="top" trigger="hover">
width="300"
placement="top"
trigger="hover"
>
<template #default> <template #default>
多长向服务端发发送一次心跳包 单位 <span class="font-black text-[#5A3DAA]"></span> <br/> 多长向服务端发发送一次心跳包 单位
对应参数<span class="font-black text-[#5A3DAA]">transport.heartbeatInterval</span> <span class="font-black text-[#5A3DAA]"></span> <br />
对应参数<span class="font-black text-[#5A3DAA]"
>transport.heartbeatInterval</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
心跳间隔 心跳间隔
</template> </template>
<el-input-number class="w-full" v-model="formData.transportHeartbeatInterval" :min="1" :max="10" <el-input-number
controls-position="right"/> class="w-full"
v-model="formData.transportHeartbeatInterval"
:min="1"
:max="10"
controls-position="right"
/>
<!-- <el-input--> <!-- <el-input-->
<!-- placeholder="请输入心跳间隔"--> <!-- placeholder="请输入心跳间隔"-->
<!-- type="number"--> <!-- type="number"-->
@ -548,24 +558,32 @@ onUnmounted(() => {
<el-form-item label="心跳超时:" prop="transportHeartbeatTimeout"> <el-form-item label="心跳超时:" prop="transportHeartbeatTimeout">
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover width="300" placement="top" trigger="hover">
width="300"
placement="top"
trigger="hover"
>
<template #default> <template #default>
心跳超时时间 单位 <span class="font-black text-[#5A3DAA]"></span> <br/> 心跳超时时间 单位
对应参数<span class="font-black text-[#5A3DAA]">transport.heartbeatTimeout</span> <span class="font-black text-[#5A3DAA]"></span> <br />
对应参数<span class="font-black text-[#5A3DAA]"
>transport.heartbeatTimeout</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
心跳超时 心跳超时
</template> </template>
<el-input-number class="w-full" v-model="formData.transportHeartbeatTimeout" :min="1" :max="10" <el-input-number
controls-position="right"/> class="w-full"
v-model="formData.transportHeartbeatTimeout"
:min="1"
:max="10"
controls-position="right"
/>
<!-- <el-input--> <!-- <el-input-->
<!-- placeholder="请输入心跳超时时间"--> <!-- placeholder="请输入心跳超时时间"-->
<!-- :min="0"--> <!-- :min="0"-->
@ -581,27 +599,35 @@ onUnmounted(() => {
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="是否启用TSL" prop="tlsConfigEnable"> <el-form-item label="是否启用TSL" prop="tlsConfigEnable">
<el-switch active-text="" <el-switch
active-text="开"
inline-prompt inline-prompt
inactive-text="关" inactive-text="关"
v-model="formData.tlsConfigEnable"/> v-model="formData.tlsConfigEnable"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<template v-if="formData.tlsConfigEnable"> <template v-if="formData.tlsConfigEnable">
<el-col :span="24"> <el-col :span="24">
<el-form-item label="TLS证书文件" prop="tlsConfigCertFile" label-width="180"> <el-form-item
label="TLS证书文件"
prop="tlsConfigCertFile"
label-width="180"
>
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover width="260" placement="top" trigger="hover">
width="260"
placement="top"
trigger="hover"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">transport.tls.certFile</span> 对应参数<span class="font-black text-[#5A3DAA]"
>transport.tls.certFile</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
@ -613,23 +639,34 @@ onUnmounted(() => {
placeholder="请选择TLS证书文件" placeholder="请选择TLS证书文件"
readonly readonly
/> />
<el-button class="ml-2" type="primary" @click="handleSelectFile(1, ['crt'])">选择</el-button> <el-button
class="ml-2"
type="primary"
@click="handleSelectFile(1, ['crt'])"
>选择</el-button
>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="TLS密钥文件" prop="tlsConfigKeyFile" label-width="180"> <el-form-item
label="TLS密钥文件"
prop="tlsConfigKeyFile"
label-width="180"
>
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover width="260" placement="top" trigger="hover">
width="260"
placement="top"
trigger="hover"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">transport.tls.keyFile</span> 对应参数<span class="font-black text-[#5A3DAA]"
>transport.tls.keyFile</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
@ -641,23 +678,34 @@ onUnmounted(() => {
placeholder="请选择 TLS 密钥文件" placeholder="请选择 TLS 密钥文件"
readonly readonly
/> />
<el-button class="ml-2" type="primary" @click="handleSelectFile(2, ['key'])">选择</el-button> <el-button
class="ml-2"
type="primary"
@click="handleSelectFile(2, ['key'])"
>选择</el-button
>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="CA证书文件" prop="tlsConfigTrustedCaFile" label-width="180"> <el-form-item
label="CA证书文件"
prop="tlsConfigTrustedCaFile"
label-width="180"
>
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover width="310" placement="top" trigger="hover">
width="310"
placement="top"
trigger="hover"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">transport.tls.trustedCaFile</span> 对应参数<span class="font-black text-[#5A3DAA]"
>transport.tls.trustedCaFile</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
@ -669,23 +717,34 @@ onUnmounted(() => {
placeholder="请选择CA证书文件" placeholder="请选择CA证书文件"
readonly readonly
/> />
<el-button class="ml-2" type="primary" @click="handleSelectFile(3, ['crt'])">选择</el-button> <el-button
class="ml-2"
type="primary"
@click="handleSelectFile(3, ['crt'])"
>选择</el-button
>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="TLS Server 名称:" prop="tlsConfigServerName" label-width="180"> <el-form-item
label="TLS Server 名称:"
prop="tlsConfigServerName"
label-width="180"
>
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover width="300" placement="top" trigger="hover">
width="300"
placement="top"
trigger="hover"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">transport.tls.serverName</span> 对应参数<span class="font-black text-[#5A3DAA]"
>transport.tls.serverName</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
@ -703,10 +762,12 @@ onUnmounted(() => {
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="是否启动代理:" prop="proxyConfigEnable"> <el-form-item label="是否启动代理:" prop="proxyConfigEnable">
<el-switch active-text="" <el-switch
active-text="开"
inline-prompt inline-prompt
inactive-text="关" inactive-text="关"
v-model="formData.proxyConfigEnable"/> v-model="formData.proxyConfigEnable"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<template v-if="formData.proxyConfigEnable"> <template v-if="formData.proxyConfigEnable">
@ -714,23 +775,27 @@ onUnmounted(() => {
<el-form-item label="代理地址:" prop="proxyConfigProxyUrl"> <el-form-item label="代理地址:" prop="proxyConfigProxyUrl">
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover width="300" placement="top" trigger="hover">
width="300"
placement="top"
trigger="hover"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">transport.proxyURL</span> 对应参数<span class="font-black text-[#5A3DAA]"
>transport.proxyURL</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
代理地址 代理地址
</template> </template>
<el-input v-model="formData.proxyConfigProxyUrl" <el-input
placeholder="http://user:pwd@192.168.1.128:8080"/> v-model="formData.proxyConfigProxyUrl"
placeholder="http://user:pwd@192.168.1.128:8080"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</template> </template>
@ -750,7 +815,11 @@ onUnmounted(() => {
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="日志保留天数:" prop="logMaxDays"> <el-form-item label="日志保留天数:" prop="logMaxDays">
<el-input-number class="!w-full" controls-position="right" v-model="formData.logMaxDays"/> <el-input-number
class="!w-full"
controls-position="right"
v-model="formData.logMaxDays"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
@ -760,49 +829,59 @@ onUnmounted(() => {
<el-form-item label="开机自启:" prop="systemSelfStart"> <el-form-item label="开机自启:" prop="systemSelfStart">
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover placement="top" trigger="hover">
placement="top"
trigger="hover"
>
<template #default> <template #default>
开机自动启动 <br/><span class="font-black text-[#5A3DAA]">Frpc Desktop</span> 开机自动启动 <br /><span
class="font-black text-[#5A3DAA]"
>Frpc Desktop</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
开机自启 开机自启
</template> </template>
<el-switch active-text="" <el-switch
active-text="开"
inline-prompt inline-prompt
inactive-text="关" inactive-text="关"
v-model="formData.systemSelfStart"/> v-model="formData.systemSelfStart"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="自动连接:" prop="systemStartupConnect"> <el-form-item label="自动连接:" prop="systemStartupConnect">
<template #label> <template #label>
<div class="h-full flex items-center mr-1"> <div class="h-full flex items-center mr-1">
<el-popover <el-popover placement="top" trigger="hover">
placement="top"
trigger="hover"
>
<template #default> <template #default>
启动软件后是否<span class="font-black text-[#5A3DAA]">自动连接</span>服务器 启动软件后是否<span class="font-black text-[#5A3DAA]"
>自动连接</span
>服务器
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
自动连接 自动连接
</template> </template>
<el-switch active-text="" <el-switch
active-text="开"
inline-prompt inline-prompt
inactive-text="关" inactive-text="关"
v-model="formData.systemStartupConnect"/> v-model="formData.systemStartupConnect"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
@ -818,47 +897,73 @@ onUnmounted(() => {
</div> </div>
</div> </div>
<el-dialog v-model="visibles.copyServerConfig" title="复制链接" width="500" top="5%"> <el-dialog
<el-alert class="mb-4" title="生成内容包含服务器密钥等内容 请妥善保管 且链接仅在Frpc-Desktop中可用" type="warning" v-model="visibles.copyServerConfig"
:closable="false"/> title="复制链接"
<el-input class="h-30" v-model="copyServerConfigBase64" type="textarea" :rows="8"></el-input> width="500"
top="5%"
>
<el-alert
class="mb-4"
title="生成内容包含服务器密钥等内容 请妥善保管 且链接仅在Frpc-Desktop中可用"
type="warning"
:closable="false"
/>
<el-input
class="h-30"
v-model="copyServerConfigBase64"
type="textarea"
:rows="8"
></el-input>
</el-dialog> </el-dialog>
<el-dialog v-model="visibles.pasteServerConfig" title="导入链接" width="500" top="5%"> <el-dialog
<el-input class="h-30" v-model="visibles.pasteServerConfig"
title="导入链接"
width="500"
top="5%"
>
<el-input
class="h-30"
v-model="pasteServerConfigBase64" v-model="pasteServerConfigBase64"
type="textarea" placeholder="frp://......" type="textarea"
:rows="8"></el-input> placeholder="frp://......"
:rows="8"
></el-input>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button
<el-button plain type="primary" @click="handlePasteServerConfigBase64"> plain
<IconifyIconOffline class="cursor-pointer mr-2" icon="label-important-rounded"/> type="primary"
@click="handlePasteServerConfigBase64"
>
<IconifyIconOffline
class="cursor-pointer mr-2"
icon="label-important-rounded"
/>
</el-button> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.h2 { .h2 {
color: #5A3DAA; color: #5a3daa;
font-size: 16px; font-size: 16px;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-weight: 700; font-weight: 700;
padding: 6px 10px 6px 15px; padding: 6px 10px 6px 15px;
border-left: 5px solid #5A3DAA; border-left: 5px solid #5a3daa;
border-radius: 4px; border-radius: 4px;
background-color: #EEEBF6; background-color: #eeebf6;
margin-bottom: 18px; margin-bottom: 18px;
} }
.button-input { .button-input {
width: calc(100% - 68px); width: calc(100% - 68px);
} }
</style> </style>

View File

@ -11,20 +11,7 @@ defineComponent({
name: "Download" name: "Download"
}); });
type Asset = { const versions = ref<Array<FrpVersion>>([]);
name: string
}
type Version = {
id: string;
name: string;
published_at: string;
download_completed: boolean;
absPath: string;
assets: Asset[]
};
const versions = ref<Array<Version>>([]);
const loading = ref(1); const loading = ref(1);
const downloadPercentage = ref(0); const downloadPercentage = ref(0);
const downloading = ref<Map<string, number>>(new Map<string, number>()); const downloading = ref<Map<string, number>>(new Map<string, number>());
@ -40,7 +27,7 @@ const handleLoadVersions = () => {
* 下载 * 下载
* @param version * @param version
*/ */
const handleDownload = useDebounceFn((version: Version) => { const handleDownload = useDebounceFn((version: FrpVersion) => {
ipcRenderer.send("github.download", version.id); ipcRenderer.send("github.download", version.id);
downloading.value.set(version.id, 0); downloading.value.set(version.id, 0);
}, 300); }, 300);
@ -49,7 +36,7 @@ const handleDownload = useDebounceFn((version: Version) => {
* 删除下载 * 删除下载
* @param version * @param version
*/ */
const handleDeleteVersion = useDebounceFn((version: Version) => { const handleDeleteVersion = useDebounceFn((version: FrpVersion) => {
ipcRenderer.send("github.deleteVersion", { ipcRenderer.send("github.deleteVersion", {
id: version.id, id: version.id,
absPath: version.absPath absPath: version.absPath
@ -61,8 +48,8 @@ const handleInitDownloadHook = () => {
loading.value--; loading.value--;
versions.value = args.map(m => { versions.value = args.map(m => {
m.published_at = moment(m.published_at).format("YYYY-MM-DD HH:mm:ss") m.published_at = moment(m.published_at).format("YYYY-MM-DD HH:mm:ss")
return m as Version; return m as FrpVersion;
}) as Array<Version>; }) as Array<FrpVersion>;
console.log(versions, 'versions') console.log(versions, 'versions')
}); });
// //
@ -75,7 +62,7 @@ const handleInitDownloadHook = () => {
}); });
ipcRenderer.on("Download.frpVersionDownloadOnCompleted", (event, args) => { ipcRenderer.on("Download.frpVersionDownloadOnCompleted", (event, args) => {
downloading.value.delete(args); downloading.value.delete(args);
const version: Version | undefined = versions.value.find( const version: FrpVersion | undefined = versions.value.find(
f => f.id === args f => f.id === args
); );
if (version) { if (version) {

View File

@ -7,27 +7,10 @@ import {clone} from "@/utils/clone";
import { useDebounceFn } from "@vueuse/core"; import { useDebounceFn } from "@vueuse/core";
import IconifyIconOffline from "@/components/IconifyIcon/src/iconifyIconOffline"; import IconifyIconOffline from "@/components/IconifyIcon/src/iconifyIconOffline";
defineComponent({ defineComponent({
name: "Proxy" name: "Proxy"
}); });
type Proxy = {
_id: string;
name: string;
type: string;
localIp: string;
localPort: number;
remotePort: number;
customDomains: string[];
};
type LocalPort = {
protocol: string;
ip: string;
port: number;
}
/** /**
* 代理列表 * 代理列表
*/ */
@ -52,25 +35,48 @@ const edit = ref({
visible: false visible: false
}); });
/** const defaultForm = ref<Proxy>({
* 表单内容
*/
const editForm = ref<Proxy>({
_id: "", _id: "",
name: "", name: "",
type: "http", type: "http",
localIp: "", localIp: "",
localPort: 8080, localPort: 8080,
remotePort: 8080, remotePort: 8080,
customDomains: [""] customDomains: [""],
stcpModel: "visitor",
serverName: "",
secretKey: "",
bindAddr: "",
bindPort: null
}); });
/**
* 表单内容
*/
const editForm = ref<Proxy>(defaultForm.value);
/**
* 代理类型
*/
const proxyTypes = ref(["http", "https", "tcp", "udp", "stcp"]);
const stcpModels = ref([
{
label: "访问者",
value: "visitor"
},
{
label: "被访问者",
value: "visited"
}
]);
/** /**
* 表单校验 * 表单校验
*/ */
const editFormRules = reactive<FormRules>({ const editFormRules = reactive<FormRules>({
name: [ name: [
{required: true, message: "请输入名称", trigger: "blur"}, { required: true, message: "请输入名称", trigger: "blur" }
// { // {
// pattern: /^[a-zA-Z]+$/, // pattern: /^[a-zA-Z]+$/,
// message: "", // message: "",
@ -87,7 +93,23 @@ const editFormRules = reactive<FormRules>({
} }
], ],
localPort: [{ required: true, message: "请输入本地端口", trigger: "blur" }], localPort: [{ required: true, message: "请输入本地端口", trigger: "blur" }],
remotePort: [{required: true, message: "请输入远程端口", trigger: "blur"}] remotePort: [{ required: true, message: "请输入远程端口", trigger: "blur" }],
stcpModel: [{ required: true, message: "请选择stcp模式", trigger: "blur" }],
secretKey: [
{ required: true, message: "请输入stcp共享密钥", trigger: "blur" }
],
serverName: [
{ required: true, message: "请输入stcp被访问者代理名称", trigger: "blur" }
],
bindAddr: [
{ required: true, message: "请输入绑定的地址", trigger: "blur" },
{
pattern: /^[\w-]+(\.[\w-]+)+$/,
message: "请输入正确的内网地址",
trigger: "blur"
}
],
bindPort: [{ required: true, message: "请输入绑定的端口", trigger: "blur" }]
}); });
/** /**
@ -147,15 +169,7 @@ const handleDeleteProxy = (proxy: Proxy) => {
* 重置表单 * 重置表单
*/ */
const handleResetForm = () => { const handleResetForm = () => {
editForm.value = { editForm.value = defaultForm.value;
_id: "",
name: "",
type: "http",
localIp: "",
localPort: 0,
remotePort: 0,
customDomains: [""]
};
}; };
/** /**
@ -186,8 +200,8 @@ const handleInitHook = () => {
ipcRenderer.on("local.getLocalPorts.hook", (event, args) => { ipcRenderer.on("local.getLocalPorts.hook", (event, args) => {
loading.value.localPorts--; loading.value.localPorts--;
localPorts.value = args.data; localPorts.value = args.data;
console.log('本地端口', localPorts.value) console.log("本地端口", localPorts.value);
}) });
// ipcRenderer.on("Proxy.updateProxy.hook", (event, args) => { // ipcRenderer.on("Proxy.updateProxy.hook", (event, args) => {
// loading.value.form--; // loading.value.form--;
// const { err } = args; // const { err } = args;
@ -236,40 +250,38 @@ const handleOpenUpdate = (proxy: Proxy) => {
const handleLoadLocalPorts = () => { const handleLoadLocalPorts = () => {
loading.value.localPorts = 1; loading.value.localPorts = 1;
ipcRenderer.send("local.getLocalPorts") ipcRenderer.send("local.getLocalPorts");
} };
const handleSelectLocalPort = useDebounceFn((port: number) => { const handleSelectLocalPort = useDebounceFn((port: number) => {
editForm.value.localPort = port; editForm.value.localPort = port;
handleCloseLocalPortDialog(); handleCloseLocalPortDialog();
}) });
const handleCloseLocalPortDialog = () => { const handleCloseLocalPortDialog = () => {
listPortsVisible.value = false; listPortsVisible.value = false;
} };
const handleOpenLocalPortDialog = () => { const handleOpenLocalPortDialog = () => {
listPortsVisible.value = true; listPortsVisible.value = true;
handleLoadLocalPorts(); handleLoadLocalPorts();
}; };
interface RestaurantItem { interface RestaurantItem {
value: string value: string;
} }
const commonIp = ref<Array<RestaurantItem>>([]) const commonIp = ref<Array<RestaurantItem>>([]);
const handleIpFetchSuggestions = (queryString: string, cb: any) => { const handleIpFetchSuggestions = (queryString: string, cb: any) => {
const results = queryString const results = queryString
? commonIp.value.filter(f => { ? commonIp.value.filter(f => {
return f.value.toLowerCase().indexOf(queryString.toLowerCase()) !== -1 return f.value.toLowerCase().indexOf(queryString.toLowerCase()) !== -1;
}) })
: commonIp.value : commonIp.value;
console.log(results, 'results') console.log(results, "results");
cb(commonIp.value) cb(commonIp.value);
} };
onMounted(() => { onMounted(() => {
handleInitHook(); handleInitHook();
@ -295,7 +307,7 @@ onUnmounted(() => {
<IconifyIconOffline icon="add" /> <IconifyIconOffline icon="add" />
</div> </div>
</breadcrumb> </breadcrumb>
<div class="app-container-breadcrumb" v-loading="loading.list > 0"> <div class="app-container-breadcrumb pr-2" v-loading="loading.list > 0">
<template v-if="proxys && proxys.length > 0"> <template v-if="proxys && proxys.length > 0">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col <el-col
@ -330,7 +342,8 @@ onUnmounted(() => {
</div> </div>
<div> <div>
<el-dropdown size="small"> <el-dropdown size="small">
<a href="javascript:void(0)" <a
href="javascript:void(0)"
class="text-xl text-[#ADADAD] hover:text-[#5A3DAA]" class="text-xl text-[#ADADAD] hover:text-[#5A3DAA]"
> >
<IconifyIconOffline icon="more-vert" /> <IconifyIconOffline icon="more-vert" />
@ -386,7 +399,7 @@ onUnmounted(() => {
<el-dialog <el-dialog
v-model="edit.visible" v-model="edit.visible"
:title="edit.title" :title="edit.title"
width="500" class="sm:w-[500px] md:w-[600px] lg:w-[800px]"
top="5%" top="5%"
> >
<el-form <el-form
@ -400,19 +413,77 @@ onUnmounted(() => {
<el-col :span="24"> <el-col :span="24">
<el-form-item label="代理类型:" prop="type"> <el-form-item label="代理类型:" prop="type">
<el-radio-group v-model="editForm.type"> <el-radio-group v-model="editForm.type">
<el-radio label="http" model-value="http"/> <el-radio-button
<el-radio label="https" model-value="https"/> v-for="p in proxyTypes"
<el-radio label="tcp" model-value="tcp"/> :key="p"
<el-radio label="udp" model-value="udp"/> :label="p"
<!-- <el-radio label="stcp" model-value="stcp" />--> :value="p"
/>
</el-radio-group>
</el-form-item>
</el-col>
<template v-if="editForm.type === 'stcp'">
<el-col :span="12">
<el-form-item label="stcp模式" prop="stcpModel">
<el-radio-group v-model="editForm.stcpModel">
<el-radio
v-for="p in stcpModels"
:key="p.value"
:label="p.label"
:value="p.value"
/>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="代理名称:" prop="name"> <el-form-item label="共享密钥:" prop="secretKey">
<el-input v-model="editForm.name" placeholder="代理名称" clearable/> <template #label>
<div class="inline-block">
<div class="flex items-center">
<div class="mr-1">
<el-popover placement="top" trigger="hover" width="300">
<template #default>
对应参数<span class="font-black text-[#5A3DAA]"
>secretKey</span
>
只有访问者与被访问者共享密钥一致的用户才能访问该服务
</template>
<template #reference>
<IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template>
</el-popover>
</div>
共享密钥
</div>
</div>
</template>
<el-input
type="password"
v-model="editForm.secretKey"
placeholder="密钥"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</template>
<el-col :span="editForm.stcpModel === 'visitor' ? 12 : 24">
<el-form-item label="代理名称:" prop="name">
<el-input
v-model="editForm.name"
placeholder="代理名称"
clearable
/>
</el-form-item>
</el-col>
<template
v-if="
editForm.type !== 'stcp' ||
(editForm.type === 'stcp' && editForm.stcpModel === 'visited')
"
>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="内网地址:" prop="localIp"> <el-form-item label="内网地址:" prop="localIp">
<!-- <el-autocomplete--> <!-- <el-autocomplete-->
@ -421,7 +492,11 @@ onUnmounted(() => {
<!-- clearable--> <!-- clearable-->
<!-- placeholder="127.0.0.1"--> <!-- placeholder="127.0.0.1"-->
<!-- />--> <!-- />-->
<el-input v-model="editForm.localIp" placeholder="127.0.0.1" clearable/> <el-input
v-model="editForm.localIp"
placeholder="127.0.0.1"
clearable
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -434,12 +509,21 @@ onUnmounted(() => {
v-model="editForm.localPort" v-model="editForm.localPort"
controls-position="right" controls-position="right"
/> />
<el-button class="ml-[10px]" plain type="primary" @click="handleOpenLocalPortDialog"> <el-button
<IconifyIconOffline class="cursor-pointer mr-2" icon="bring-your-own-ip-rounded"/> class="ml-[10px]"
plain
type="primary"
@click="handleOpenLocalPortDialog"
>
<IconifyIconOffline
class="cursor-pointer mr-2"
icon="bring-your-own-ip-rounded"
/>
本地端口 本地端口
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-col> </el-col>
</template>
<template v-if="editForm.type === 'tcp' || editForm.type === 'udp'"> <template v-if="editForm.type === 'tcp' || editForm.type === 'udp'">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="外网端口:" prop="remotePort"> <el-form-item label="外网端口:" prop="remotePort">
@ -480,15 +564,18 @@ onUnmounted(() => {
<div class="inline-block"> <div class="inline-block">
<div class="flex items-center"> <div class="flex items-center">
<div class="mr-1"> <div class="mr-1">
<el-popover <el-popover placement="top" trigger="hover">
placement="top"
trigger="hover"
>
<template #default> <template #default>
对应参数<span class="font-black text-[#5A3DAA]">customDomains</span> 对应参数<span class="font-black text-[#5A3DAA]"
>customDomains</span
>
</template> </template>
<template #reference> <template #reference>
<IconifyIconOffline class="text-base" color="#5A3DAA" icon="info"/> <IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template> </template>
</el-popover> </el-popover>
</div> </div>
@ -537,16 +624,117 @@ onUnmounted(() => {
</el-form-item> </el-form-item>
</el-col> </el-col>
</template> </template>
<template
v-if="editForm.type === 'stcp' && editForm.stcpModel === 'visitor'"
>
<el-col :span="12">
<el-form-item label="被访问者代理名称:" prop="serverName">
<el-input
type="text"
v-model="editForm.serverName"
placeholder="stcp被访问者代理名称"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="绑定地址:" prop="bindAddr">
<template #label>
<div class="inline-block">
<div class="flex items-center">
<div class="mr-1">
<el-popover placement="top" trigger="hover" width="300">
<template #default>
对应参数<span class="font-black text-[#5A3DAA]"
>bindAddr</span
>
要将被访问者的服务绑定到本地哪个<span
class="font-black text-[#5A3DAA]"
>IP</span
>
<br />
仅本机访问<span class="font-black text-[#5A3DAA]"
>127.0.0.1</span
>
<br />
支持局域网其他设备访问<span
class="font-black text-[#5A3DAA]"
>0.0.0.0</span
>
<br />
</template>
<template #reference>
<IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template>
</el-popover>
</div>
绑定地址
</div>
</div>
</template>
<el-input
type="text"
v-model="editForm.bindAddr"
placeholder="127.0.0.1"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="绑定端口:" prop="bindPort">
<template #label>
<div class="inline-block">
<div class="flex items-center">
<div class="mr-1">
<el-popover placement="top" trigger="hover" width="300">
<template #default>
对应参数<span class="font-black text-[#5A3DAA]"
>bindAddr</span
>
要将被访问者的服务绑定到本地哪个<span
class="font-black text-[#5A3DAA]"
>端口</span
>
<br />
请自行确保端口未被占用
</template>
<template #reference>
<IconifyIconOffline
class="text-base"
color="#5A3DAA"
icon="info"
/>
</template>
</el-popover>
</div>
绑定端口
</div>
</div>
</template>
<el-input-number
v-model="editForm.bindPort"
placeholder="8080"
/>
</el-form-item>
</el-col>
</template>
<el-col :span="24"> <el-col :span="24">
<el-form-item> <el-form-item>
<div class="w-full flex justify-end"> <div class="w-full flex justify-end">
<el-button @click="edit.visible = false"> <el-button @click="edit.visible = false">
<iconify-icon-offline class="cursor-pointer mr-2" icon="cancel-presentation"/> <iconify-icon-offline
class="cursor-pointer mr-2"
icon="cancel-presentation"
/>
</el-button> </el-button>
<el-button plain type="primary" @click="handleSubmit" <el-button plain type="primary" @click="handleSubmit">
> <IconifyIconOffline
<IconifyIconOffline class="cursor-pointer mr-2" icon="save-rounded"/> class="cursor-pointer mr-2"
icon="save-rounded"
/>
</el-button> </el-button>
</div> </div>
@ -556,24 +744,30 @@ onUnmounted(() => {
</el-form> </el-form>
</el-dialog> </el-dialog>
<el-dialog v-model="listPortsVisible" <el-dialog v-model="listPortsVisible" title="本地端口" width="600" top="5%">
title="本地端口" <el-table
width="600" :data="localPorts"
top="5%"> stripe
<el-table :data="localPorts" stripe
v-loading="loading.localPorts" v-loading="loading.localPorts"
border height="400"> border
height="400"
>
<el-table-column label="协议" :width="60" prop="protocol" /> <el-table-column label="协议" :width="60" prop="protocol" />
<el-table-column label="IP" prop="ip" /> <el-table-column label="IP" prop="ip" />
<el-table-column label="端口" :width="80" prop="port" /> <el-table-column label="端口" :width="80" prop="port" />
<el-table-column label="操作" :width="80"> <el-table-column label="操作" :width="80">
<template #default="scope"> <template #default="scope">
<el-button type="text" @click="handleSelectLocalPort(scope.row.port)"> <el-button
<IconifyIconOffline class="cursor-pointer mr-2" icon="gesture-select"/> type="text"
@click="handleSelectLocalPort(scope.row.port)"
>
<IconifyIconOffline
class="cursor-pointer mr-2"
icon="gesture-select"
/>
选择 选择
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-dialog> </el-dialog>

80
types/global.d.ts vendored Normal file
View File

@ -0,0 +1,80 @@
declare module 'element-plus/dist/locale/zh-cn.mjs' {
const zhLocale: any;
export default zhLocale;
}
declare module 'element-plus/dist/locale/en.mjs' {
const enLocale: any;
export default enLocale;
}
declare global {
/**
*
*/
type Proxy = {
_id: string;
name: string;
type: string;
localIp: string;
localPort: number;
remotePort: number;
customDomains: string[];
stcpModel: string;
serverName: string;
secretKey: string;
bindAddr: string;
bindPort: string;
};
/**
*
*/
type LocalPort = {
protocol: string;
ip: string;
port: number;
}
/**
*
*/
type FrpVersion = {
id: string;
name: string;
published_at: string;
download_completed: boolean;
absPath: string;
assets: Asset[]
};
/**
*
*/
type FrpConfig = {
currentVersion: string;
serverAddr: string;
serverPort: number;
authMethod: string;
authToken: string;
logLevel: string;
logMaxDays: number;
tlsConfigEnable: boolean;
tlsConfigCertFile: string;
tlsConfigKeyFile: string;
tlsConfigTrustedCaFile: string;
tlsConfigServerName: string;
proxyConfigEnable: boolean;
proxyConfigProxyUrl: string;
systemSelfStart: boolean;
systemStartupConnect: boolean;
user: string;
metaToken: string;
transportHeartbeatInterval: number;
transportHeartbeatTimeout: number;
};
}
export {};