Browse Source

fix:修复前端远程代理字段不一致问题

master
秋田弘 5 months ago
parent
commit
cb9a31c612
  1. 404
      frontend/src/components/setting/FrpcEdit.vue
  2. 38
      frontend/src/stores/proxy.ts

404
frontend/src/components/setting/FrpcEdit.vue

@ -1,18 +1,16 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useProxyStore } from "@/stores/proxy"; import { useProxyStore } from "@/stores/proxy";
import { notifyError, notifySuccess } from "@/util/msg"; import { OpenDirDialog } from "@/util/goutil";
import { ref } from "vue"; import { notifyError, notifySuccess } from "@/util/msg";
import { OpenDirDialog } from "@/util/goutil"; import { ref } from "vue";
const proxyStore = useProxyStore(); const proxyStore = useProxyStore();
const { const { updateProxy } = proxyStore;
updateProxy,
} = proxyStore;
const proxyDialogShow = ref(false); const proxyDialogShow = ref(false);
const pwdRef = ref<any>(null); const pwdRef = ref<any>(null);
// //
const proxyRules = { const proxyRules = {
name: [{ required: true, message: "请输入代理名称", trigger: "blur" }], name: [{ required: true, message: "请输入代理名称", trigger: "blur" }],
// port: [ // port: [
// { required: true, message: "", trigger: "blur" }, // { required: true, message: "", trigger: "blur" },
@ -25,21 +23,22 @@ const proxyRules = {
{ {
pattern: /^[\w-]+(\.[\w-]+)+$/, pattern: /^[\w-]+(\.[\w-]+)+$/,
message: "请输入正确的内网地址", message: "请输入正确的内网地址",
trigger: "blur" trigger: "blur",
} },
], ],
localPort: [ localPort: [
{ required: true, message: "请输入内网端口", trigger: "blur" }, { required: true, message: "请输入内网端口", trigger: "blur" },
{ {
pattern: /^(?:\d{1,5}|\d{1,5}-\d{1,5})(?:,(?:\d{1,5}|\d{1,5}-\d{1,5}))*$/, pattern:
/^(?:\d{1,5}|\d{1,5}-\d{1,5})(?:,(?:\d{1,5}|\d{1,5}-\d{1,5}))*$/,
message: "请输入正确的端口", message: "请输入正确的端口",
trigger: "blur" trigger: "blur",
}, },
] ],
}; };
const addProxy = async () => { const addProxy = async () => {
const res = await proxyStore.createFrpc() const res = await proxyStore.createFrpc();
if (res.code !== 0) { if (res.code !== 0) {
notifyError(res.message); notifyError(res.message);
return; return;
@ -48,18 +47,18 @@ const addProxy = async () => {
proxyStore.resetProxyData(); proxyStore.resetProxyData();
notifySuccess("代理配置已成功创建"); notifySuccess("代理配置已成功创建");
await proxyStore.fetchProxies(); await proxyStore.fetchProxies();
}; };
const update = async () => { const update = async () => {
await updateProxy(proxyStore.proxyData); await updateProxy(proxyStore.proxyData);
notifySuccess("编辑成功"); notifySuccess("编辑成功");
proxyStore.addShow = false; proxyStore.addShow = false;
proxyStore.resetProxyData(); proxyStore.resetProxyData();
proxyStore.isEditor = false; proxyStore.isEditor = false;
await proxyStore.fetchProxies(); await proxyStore.fetchProxies();
}; };
const saveProxy = () => { const saveProxy = () => {
pwdRef.value.validate((valid: boolean) => { pwdRef.value.validate((valid: boolean) => {
if (valid) { if (valid) {
if (proxyStore.isEditor) { if (proxyStore.isEditor) {
@ -71,11 +70,9 @@ const saveProxy = () => {
console.log("表单验证失败"); console.log("表单验证失败");
} }
}); });
}; };
const proxyTypes = ref([ const proxyTypes = ref([
"http", "http",
"https", "https",
"tcp", "tcp",
@ -83,14 +80,14 @@ const proxyTypes = ref([
"stcp", "stcp",
"xtcp", "xtcp",
"sudp", "sudp",
]); ]);
const stcpModels = ref([ const stcpModels = ref([
{ label: "访问者", value: "visitors" }, { label: "访问者", value: "visitors" },
{ label: "被访问者", value: "visited" }, { label: "被访问者", value: "visited" },
]); ]);
const handleSelectFile = (mod: number) => { const handleSelectFile = (mod: number) => {
OpenDirDialog().then((res: string) => { OpenDirDialog().then((res: string) => {
if (mod == 1) { if (mod == 1) {
proxyStore.proxyData.https2httpCaFile = res; proxyStore.proxyData.https2httpCaFile = res;
@ -100,22 +97,34 @@ const handleSelectFile = (mod: number) => {
proxyStore.proxyData.localPath = res; proxyStore.proxyData.localPath = res;
} }
}); });
}; };
</script> </script>
<template> <template>
<el-form :model="proxyStore.proxyData" :rules="proxyRules" ref="pwdRef" label-position="top"> <el-form
:model="proxyStore.proxyData"
:rules="proxyRules"
ref="pwdRef"
label-position="top"
>
<!-- 代理类型选择 --> <!-- 代理类型选择 -->
<el-form-item label="代理类型:" prop="type"> <el-form-item
label="代理类型:"
prop="type"
>
<el-radio-group v-model="proxyStore.proxyData.type"> <el-radio-group v-model="proxyStore.proxyData.type">
<el-radio-button v-for="type in proxyTypes" :key="type" :value="type">{{ type }}</el-radio-button> <el-radio-button
v-for="type in proxyTypes"
:key="type"
:value="type"
>{{ type }}</el-radio-button
>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- HTTP/HTTPS模式 --> <!-- HTTP/HTTPS模式 -->
<template v-if=" <template
v-if="
proxyStore.proxyData.type === 'http' || proxyStore.proxyData.type === 'http' ||
proxyStore.proxyData.type === 'https' || proxyStore.proxyData.type === 'https' ||
proxyStore.proxyData.type === 'tcp' || proxyStore.proxyData.type === 'tcp' ||
@ -123,140 +132,290 @@ const handleSelectFile = (mod: number) => {
proxyStore.proxyData.type === 'stcp' || proxyStore.proxyData.type === 'stcp' ||
proxyStore.proxyData.type === 'xtcp' || proxyStore.proxyData.type === 'xtcp' ||
proxyStore.proxyData.type === 'sudp' proxyStore.proxyData.type === 'sudp'
"> "
<el-form-item label="代理名称:" prop="name"> >
<el-input v-model="proxyStore.proxyData.name" placeholder="代理名称" /> <el-form-item
label="代理名称:"
prop="name"
>
<el-input
v-model="proxyStore.proxyData.name"
placeholder="代理名称"
/>
</el-form-item> </el-form-item>
<el-row v-if=" <el-row
v-if="
proxyStore.proxyData.type === 'http' || proxyStore.proxyData.type === 'http' ||
proxyStore.proxyData.type === 'https' || proxyStore.proxyData.type === 'https' ||
proxyStore.proxyData.type === 'tcp' || proxyStore.proxyData.type === 'tcp' ||
proxyStore.proxyData.type === 'udp' proxyStore.proxyData.type === 'udp'
" :gutter="20"> "
:gutter="20"
>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="内网Ip:" prop="localIp"> <el-form-item
<el-input v-model="proxyStore.proxyData.localIp" placeholder="内网Ip地址" /> label="内网Ip:"
prop="localIp"
>
<el-input
v-model="proxyStore.proxyData.localIp"
placeholder="内网Ip地址"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="端口:" prop="localPort"> <el-form-item
<el-input-number v-model="proxyStore.proxyData.localPort" :min="0" :max="65535" /> label="端口:"
prop="localPort"
>
<el-input-number
v-model="proxyStore.proxyData.localPort"
:min="0"
:max="65535"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-form-item v-if=" <el-form-item
v-if="
proxyStore.proxyData.type === 'http' || proxyStore.proxyData.type === 'http' ||
proxyStore.proxyData.type === 'https' proxyStore.proxyData.type === 'https'
" label="自定义域名" prop="customDomain"> "
<el-row v-for="(_, index) in proxyStore.customDomains" :key="index" :gutter="24"> label="自定义域名:"
prop="customDomain"
>
<el-row
v-for="(_, index) in proxyStore.customDomains"
:key="index"
:gutter="24"
>
<el-col :span="12"> <el-col :span="12">
<el-input v-model="proxyStore.customDomains[index]" placeholder="example.com" /> <el-input
v-model="proxyStore.customDomains[index]"
placeholder="example.com"
/>
</el-col> </el-col>
<el-col :span="5"> <el-col :span="5">
<el-button type="primary" icon="Plus" style="width: 80px" <el-button
@click="proxyStore.addCustomDomain">添加</el-button> type="primary"
icon="Plus"
style="width: 80px"
@click="proxyStore.addCustomDomain"
>添加</el-button
>
</el-col> </el-col>
<el-col :span="5"> <el-col :span="5">
<el-button type="primary" icon="Plus" style="width: 80px" @click=" <el-button
proxyStore.removeCustomDomain(index) type="primary"
">删除</el-button> icon="Plus"
style="width: 80px"
@click="proxyStore.removeCustomDomain(index)"
>删除</el-button
>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item v-if=" <el-form-item
v-if="
proxyStore.proxyData.type === 'http' || proxyStore.proxyData.type === 'http' ||
proxyStore.proxyData.type === 'https' proxyStore.proxyData.type === 'https'
" label="子域名" prop="domain"> "
<el-input v-model="proxyStore.proxyData.domain" placeholder="子域名" /> label="子域名:"
prop="domain"
>
<el-input
v-model="proxyStore.proxyData.subdomain"
placeholder="子域名"
/>
</el-form-item> </el-form-item>
<el-form-item v-if=" <el-form-item
v-if="
proxyStore.proxyData.type === 'tcp' || proxyStore.proxyData.type === 'tcp' ||
proxyStore.proxyData.type === 'udp' proxyStore.proxyData.type === 'udp'
" label="外网端口" prop="remotePort"> "
<el-input-number v-model="proxyStore.proxyData.remotePort" :min="0" :max="65535" /> label="外网端口:"
prop="remotePort"
>
<el-input-number
v-model="proxyStore.proxyData.remotePort"
:min="0"
:max="65535"
/>
</el-form-item> </el-form-item>
<template v-if="proxyStore.proxyData.type === 'http' || <template
v-if="
proxyStore.proxyData.type === 'http' ||
proxyStore.proxyData.type === 'https' || proxyStore.proxyData.type === 'https' ||
proxyStore.proxyData.type === 'tcp'"> proxyStore.proxyData.type === 'tcp'
<el-form-item label="HTTP基本认证:" prop="httpAuth"> "
<el-switch v-model="proxyStore.proxyData.httpAuth" /> >
<el-form-item
label="HTTP基本认证:"
prop="BasicAuth"
>
<el-switch v-model="proxyStore.proxyData.basicAuth" />
</el-form-item> </el-form-item>
<el-form-item v-if="proxyStore.proxyData.httpAuth" label="认证用户名:" prop="authUsername"> <el-form-item
<el-input v-model="proxyStore.proxyData.authUsername" placeholder="username" /> v-if="proxyStore.proxyData.basicAuth"
label="认证用户名:"
prop="httpUser"
>
<el-input
v-model="proxyStore.proxyData.httpUser"
placeholder="username"
/>
</el-form-item> </el-form-item>
<el-form-item v-if="proxyStore.proxyData.httpAuth" label="认证密码:" prop="authPassword"> <el-form-item
<el-input v-model="proxyStore.proxyData.authPassword" type="password" placeholder="password" /> v-if="proxyStore.proxyData.basicAuth"
label="认证密码:"
prop="httpPassword"
>
<el-input
v-model="proxyStore.proxyData.httpPassword"
type="password"
placeholder="password"
/>
</el-form-item> </el-form-item>
</template> </template>
<el-form-item v-if="proxyStore.proxyData.type === 'https'" label="证书文件:" prop="https2httpCaFile"> <el-form-item
<el-input v-model="proxyStore.proxyData.https2httpCaFile" placeholder="点击选择证书文件" v-if="proxyStore.proxyData.type === 'https'"
@click="handleSelectFile(1)" /> label="证书文件:"
prop="https2httpCaFile"
>
<el-input
v-model="proxyStore.proxyData.https2httpCaFile"
placeholder="点击选择证书文件"
@click="handleSelectFile(1)"
/>
</el-form-item> </el-form-item>
<el-form-item v-if="proxyStore.proxyData.type === 'https'" label="密钥文件:" prop="https2httpKeyFile"> <el-form-item
<el-input v-model="proxyStore.proxyData.https2httpKeyFile" placeholder="点击选择密钥文件" v-if="proxyStore.proxyData.type === 'https'"
@click="handleSelectFile(2)" /> label="密钥文件:"
prop="https2httpKeyFile"
>
<el-input
v-model="proxyStore.proxyData.https2httpKeyFile"
placeholder="点击选择密钥文件"
@click="handleSelectFile(2)"
/>
</el-form-item> </el-form-item>
</template> </template>
<template v-if="proxyStore.proxyData.type === 'tcp'"> <template v-if="proxyStore.proxyData.type === 'tcp'">
<el-form-item label="文件访问:" prop="staticFile"> <el-form-item
label="文件访问:"
prop="staticFile"
>
<el-switch v-model="proxyStore.proxyData.staticFile" /> <el-switch v-model="proxyStore.proxyData.staticFile" />
</el-form-item> </el-form-item>
<el-form-item v-if="proxyStore.proxyData.staticFile" label="文件夹路径:" prop="localPath"> <el-form-item
<el-input v-model="proxyStore.proxyData.localPath" placeholder="点击选择文件夹路径" v-if="proxyStore.proxyData.staticFile"
@click="handleSelectFile(3)" /> label="文件夹路径:"
prop="localPath"
>
<el-input
v-model="proxyStore.proxyData.localPath"
placeholder="点击选择文件夹路径"
@click="handleSelectFile(3)"
/>
</el-form-item> </el-form-item>
<el-form-item v-if="proxyStore.proxyData.staticFile" label="URL的前缀:" prop="stripPrefix"> <el-form-item
<el-input v-model="proxyStore.proxyData.stripPrefix" placeholder="URL的前缀" /> v-if="proxyStore.proxyData.staticFile"
label="URL的前缀:"
prop="stripPrefix"
>
<el-input
v-model="proxyStore.proxyData.stripPrefix"
placeholder="URL的前缀"
/>
</el-form-item> </el-form-item>
</template> </template>
<!-- STCP/XTCP/SUDP模式 --> <!-- STCP/XTCP/SUDP模式 -->
<template v-if=" <template
v-if="
proxyStore.proxyData.type === 'stcp' || proxyStore.proxyData.type === 'stcp' ||
proxyStore.proxyData.type === 'xtcp' || proxyStore.proxyData.type === 'xtcp' ||
proxyStore.proxyData.type === 'sudp' proxyStore.proxyData.type === 'sudp'
"> "
>
<el-row :gutter="22"> <el-row :gutter="22">
<el-col :span="14"> <el-col :span="14">
<el-form-item label="STCP模式:" prop="stcpModel"> <el-form-item
<el-radio-group v-model="proxyStore.proxyData.stcpModel"> label="STCP模式:"
<el-radio v-for="model in stcpModels" :key="model.value" :value="model.value">{{ model.label prop="stcpModel"
}}</el-radio> >
<el-radio-group
v-model="proxyStore.proxyData.stcpModel"
>
<el-radio
v-for="model in stcpModels"
:key="model.value"
:value="model.value"
>{{ model.label }}</el-radio
>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<el-form-item label="共享密钥:" prop="secretKey"> <el-form-item
<el-input v-model="proxyStore.proxyData.secretKey" type="password" placeholder="密钥" /> label="共享密钥:"
prop="secretKey"
>
<el-input
v-model="proxyStore.proxyData.secretKey"
type="password"
placeholder="密钥"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 被访问者代理名称 --> <!-- 被访问者代理名称 -->
<el-form-item v-if=" <el-form-item
v-if="
proxyStore.proxyData.type === 'stcp' || proxyStore.proxyData.type === 'stcp' ||
proxyStore.proxyData.type === 'xtcp' || proxyStore.proxyData.type === 'xtcp' ||
proxyStore.proxyData.type === 'sudp' proxyStore.proxyData.type === 'sudp'
" label="被访问者代理名称" prop="visitedName"> "
<el-input v-model="proxyStore.proxyData.visitedName" placeholder="被访问者代理名称" /> label="被访问者代理名称:"
prop="visitedName"
>
<el-input
v-model="proxyStore.proxyData.visitedName"
placeholder="被访问者代理名称"
/>
</el-form-item> </el-form-item>
<template v-if=" <template
v-if="
proxyStore.proxyData.type === 'stcp' || proxyStore.proxyData.type === 'stcp' ||
proxyStore.proxyData.type === 'xtcp' || proxyStore.proxyData.type === 'xtcp' ||
proxyStore.proxyData.type === 'sudp' proxyStore.proxyData.type === 'sudp'
"> "
>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="10"> <el-col :span="10">
<el-form-item label="绑定地址:" prop="bindAddr"> <el-form-item
<el-input v-model="proxyStore.proxyData.bindAddr" placeholder="127.0.0.1" /> label="绑定地址:"
prop="bindAddr"
>
<el-input
v-model="proxyStore.proxyData.bindAddr"
placeholder="127.0.0.1"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<el-form-item label="绑定端口:" prop="bindPort"> <el-form-item
<el-input-number v-model="proxyStore.proxyData.bindPort" :min="1" :max="65535" /> label="绑定端口:"
prop="bindPort"
>
<el-input-number
v-model="proxyStore.proxyData.bindPort"
:min="1"
:max="65535"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -264,21 +423,33 @@ const handleSelectFile = (mod: number) => {
<template v-if="proxyStore.proxyData.type === 'xtcp'"> <template v-if="proxyStore.proxyData.type === 'xtcp'">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="10"> <el-col :span="10">
<el-form-item label="回退代理名称:" prop="fallbackTo"> <el-form-item
<el-input v-model="proxyStore.proxyData.fallbackTo label="回退代理名称:"
" placeholder="回退代理名称" /> prop="fallbackTo"
>
<el-input
v-model="proxyStore.proxyData.fallbackTo"
placeholder="回退代理名称"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<el-form-item label="回退超时毫秒:" prop="fallbackTimeoutMs"> <el-form-item
<el-input-number v-model="proxyStore.proxyData label="回退超时毫秒:"
.fallbackTimeoutMs prop="fallbackTimeoutMs"
" :min="0" /> >
<el-input-number
v-model="proxyStore.proxyData.fallbackTimeoutMs"
:min="0"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 保持隧道开启 --> <!-- 保持隧道开启 -->
<el-form-item label="保持隧道开启:" prop="keepAlive"> <el-form-item
label="保持隧道开启:"
prop="keepAlive"
>
<el-switch v-model="proxyStore.proxyData.keepAlive" /> <el-switch v-model="proxyStore.proxyData.keepAlive" />
</el-form-item> </el-form-item>
</template> </template>
@ -286,10 +457,19 @@ const handleSelectFile = (mod: number) => {
<!-- 保存和取消按钮 --> <!-- 保存和取消按钮 -->
<el-row justify="start"> <el-row justify="start">
<el-button type="primary" @click="saveProxy" style="width: 100px"> <el-button
{{ proxyStore.isEditor ? '编辑' : '保存' }} type="primary"
@click="saveProxy"
style="width: 100px"
>
{{ proxyStore.isEditor ? "编辑" : "保存" }}
</el-button> </el-button>
<el-button type="primary" style="width: 100px" @click="proxyDialogShow = false">取消</el-button> <el-button
type="primary"
style="width: 100px"
@click="proxyDialogShow = false"
>取消</el-button
>
</el-row> </el-row>
</el-form> </el-form>
</template> </template>

38
frontend/src/stores/proxy.ts

@ -1,19 +1,19 @@
import { getSystemKey } from "@/system/config";
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { ref } from 'vue'; import { ref } from 'vue';
import { getSystemKey } from "@/system/config"
export interface ProxyItem { export interface ProxyItem {
id?:number; id?: number;
port: number; port: number;
domain: string; subdomain: string;
type: string; type: string;
name: string; name: string;
localPort: number; localPort: number;
localIp: string; localIp: string;
serverAddr: string; serverAddr: string;
serverPort: number; serverPort: number;
httpAuth: boolean; basicAuth: boolean;
authUsername: string; httpUser: string;
authPassword: string; httpPassword: string;
https2http: boolean; https2http: boolean;
https2httpCaFile: string; https2httpCaFile: string;
https2httpKeyFile: string; https2httpKeyFile: string;
@ -27,8 +27,8 @@ export interface ProxyItem {
fallbackTimeoutMs?: number; fallbackTimeoutMs?: number;
keepAlive?: boolean; keepAlive?: boolean;
customDomains?: string; customDomains?: string;
staticFile?:boolean; staticFile?: boolean;
localPath?:string; localPath?: string;
stripPrefix?: string; stripPrefix?: string;
} }
@ -52,18 +52,18 @@ export const useProxyStore = defineStore('proxyStore', () => {
}) })
function createNewProxyData(): ProxyItem { function createNewProxyData(): ProxyItem {
return { return {
id:0, id: 0,
type: "http", type: "http",
name: "", name: "",
port: 8000, port: 8000,
domain: "", subdomain: "",
localPort: 56780, localPort: 56780,
localIp: "127.0.0.1", localIp: "127.0.0.1",
serverAddr: "", serverAddr: "",
serverPort: 0, serverPort: 0,
httpAuth: false, basicAuth: false,
authUsername: "", httpUser: "",
authPassword: "", httpPassword: "",
https2http: false, https2http: false,
https2httpCaFile: "", https2httpCaFile: "",
https2httpKeyFile: "", https2httpKeyFile: "",
@ -77,9 +77,9 @@ export const useProxyStore = defineStore('proxyStore', () => {
fallbackTimeoutMs: 500, fallbackTimeoutMs: 500,
keepAlive: false, keepAlive: false,
customDomains: "", customDomains: "",
staticFile:false, staticFile: false,
localPath:"", localPath: "",
stripPrefix:"" stripPrefix: ""
}; };
} }
@ -99,8 +99,8 @@ export const useProxyStore = defineStore('proxyStore', () => {
const createFrpc = async () => { const createFrpc = async () => {
const url = `${apiUrl}/proxy/frpc/create`; const url = `${apiUrl}/proxy/frpc/create`;
const postData:any = toRaw(proxyData.value) const postData: any = toRaw(proxyData.value)
if(customDomains.value.length > 0){ if (customDomains.value.length > 0) {
postData.customDomains = customDomains.value.join(','); postData.customDomains = customDomains.value.join(',');
} }
postData.LocalPort = parseInt(postData.localPort) postData.LocalPort = parseInt(postData.localPort)
@ -166,7 +166,7 @@ export const useProxyStore = defineStore('proxyStore', () => {
const updateProxy = async (proxy: ProxyItem) => { const updateProxy = async (proxy: ProxyItem) => {
const url = `${apiUrl}/proxy/frpc/update`; const url = `${apiUrl}/proxy/frpc/update`;
if(customDomains.value.length > 0){ if (customDomains.value.length > 0) {
proxy.customDomains = customDomains.value.join(','); proxy.customDomains = customDomains.value.join(',');
} }
fetch(url, { fetch(url, {

Loading…
Cancel
Save