feat(ui): add all proxies tab to frps dashboard (#5321)
This commit is contained in:
committed by
GitHub
Unverified
parent
a88e0e9a49
commit
e20f974d61
+112
-34
@@ -77,8 +77,9 @@
|
|||||||
<div v-if="filteredProxies.length > 0" class="proxies-list">
|
<div v-if="filteredProxies.length > 0" class="proxies-list">
|
||||||
<ProxyCard
|
<ProxyCard
|
||||||
v-for="proxy in filteredProxies"
|
v-for="proxy in filteredProxies"
|
||||||
:key="proxy.name"
|
:key="`${proxy.type}:${proxy.name}`"
|
||||||
:proxy="proxy"
|
:proxy="proxy"
|
||||||
|
:show-type="activeType === 'all'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="!loading" class="empty-state">
|
<div v-else-if="!loading" class="empty-state">
|
||||||
@@ -129,6 +130,7 @@ const route = useRoute()
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const proxyTypes = [
|
const proxyTypes = [
|
||||||
|
{ label: 'All', value: 'all' },
|
||||||
{ label: 'TCP', value: 'tcp' },
|
{ label: 'TCP', value: 'tcp' },
|
||||||
{ label: 'UDP', value: 'udp' },
|
{ label: 'UDP', value: 'udp' },
|
||||||
{ label: 'HTTP', value: 'http' },
|
{ label: 'HTTP', value: 'http' },
|
||||||
@@ -200,15 +202,48 @@ const filteredProxies = computed(() => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter by search text
|
// Filter by search text across multiple fields
|
||||||
if (searchText.value) {
|
if (searchText.value) {
|
||||||
const search = searchText.value.toLowerCase()
|
const search = searchText.value.toLowerCase()
|
||||||
result = result.filter((p) => p.name.toLowerCase().includes(search))
|
result = result.filter((p) => {
|
||||||
|
const fields: unknown[] = [
|
||||||
|
p.name,
|
||||||
|
p.type,
|
||||||
|
p.clientID,
|
||||||
|
p.user,
|
||||||
|
p.addr,
|
||||||
|
p.port,
|
||||||
|
p.customDomains,
|
||||||
|
p.subdomain,
|
||||||
|
]
|
||||||
|
return fields.some((v) => matchesSearch(v, search))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Normalize a field of unknown shape (string / number / array / null) to a
|
||||||
|
// lowercase string for case-insensitive substring matching. Arrays are joined
|
||||||
|
// so e.g. customDomains: ["A.com","B.com"] is searchable as one blob.
|
||||||
|
const matchesSearch = (value: unknown, needle: string): boolean => {
|
||||||
|
if (value === null || value === undefined) return false
|
||||||
|
let str: string
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
str = value
|
||||||
|
.filter((v) => v !== null && v !== undefined)
|
||||||
|
.map((v) => String(v))
|
||||||
|
.join(' ')
|
||||||
|
} else if (typeof value === 'number') {
|
||||||
|
if (value === 0) return false
|
||||||
|
str = String(value)
|
||||||
|
} else {
|
||||||
|
str = String(value)
|
||||||
|
}
|
||||||
|
if (!str) return false
|
||||||
|
return str.toLowerCase().includes(needle)
|
||||||
|
}
|
||||||
|
|
||||||
const onClientFilterChange = (key: string) => {
|
const onClientFilterChange = (key: string) => {
|
||||||
if (key) {
|
if (key) {
|
||||||
const client = clientOptions.value.find((c) => c.key === key)
|
const client = clientOptions.value.find((c) => c.key === key)
|
||||||
@@ -249,45 +284,88 @@ const fetchServerInfo = async () => {
|
|||||||
return serverInfo
|
return serverInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const convertProxies = async (
|
||||||
|
type: string,
|
||||||
|
json: any,
|
||||||
|
): Promise<BaseProxy[]> => {
|
||||||
|
if (type === 'tcp') {
|
||||||
|
return json.proxies.map((p: any) => new TCPProxy(p))
|
||||||
|
}
|
||||||
|
if (type === 'udp') {
|
||||||
|
return json.proxies.map((p: any) => new UDPProxy(p))
|
||||||
|
}
|
||||||
|
if (type === 'http') {
|
||||||
|
const info = await fetchServerInfo()
|
||||||
|
if (info && info.vhostHTTPPort) {
|
||||||
|
return json.proxies.map(
|
||||||
|
(p: any) => new HTTPProxy(p, info.vhostHTTPPort, info.subdomainHost),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (type === 'https') {
|
||||||
|
const info = await fetchServerInfo()
|
||||||
|
if (info && info.vhostHTTPSPort) {
|
||||||
|
return json.proxies.map(
|
||||||
|
(p: any) => new HTTPSProxy(p, info.vhostHTTPSPort, info.subdomainHost),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (type === 'tcpmux') {
|
||||||
|
const info = await fetchServerInfo()
|
||||||
|
if (info && info.tcpmuxHTTPConnectPort) {
|
||||||
|
return json.proxies.map(
|
||||||
|
(p: any) =>
|
||||||
|
new TCPMuxProxy(p, info.tcpmuxHTTPConnectPort, info.subdomainHost),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (type === 'stcp') {
|
||||||
|
return json.proxies.map((p: any) => new STCPProxy(p))
|
||||||
|
}
|
||||||
|
if (type === 'sudp') {
|
||||||
|
return json.proxies.map((p: any) => new SUDPProxy(p))
|
||||||
|
}
|
||||||
|
// Fallback for types without a dedicated class (e.g. xtcp). Matches the
|
||||||
|
// pattern in ProxyDetail.vue so the type tag and meta render correctly.
|
||||||
|
return json.proxies.map((p: any) => {
|
||||||
|
const bp = new BaseProxy(p)
|
||||||
|
bp.type = type
|
||||||
|
return bp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const allProxyTypes = [
|
||||||
|
'tcp',
|
||||||
|
'udp',
|
||||||
|
'http',
|
||||||
|
'https',
|
||||||
|
'tcpmux',
|
||||||
|
'stcp',
|
||||||
|
'xtcp',
|
||||||
|
'sudp',
|
||||||
|
]
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
proxies.value = []
|
proxies.value = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const type = activeType.value
|
const type = activeType.value
|
||||||
const json = await getProxiesByType(type)
|
|
||||||
|
|
||||||
if (type === 'tcp') {
|
if (type === 'all') {
|
||||||
proxies.value = json.proxies.map((p: any) => new TCPProxy(p))
|
const results = await Promise.all(
|
||||||
} else if (type === 'udp') {
|
allProxyTypes.map(async (t) => {
|
||||||
proxies.value = json.proxies.map((p: any) => new UDPProxy(p))
|
const json = await getProxiesByType(t)
|
||||||
} else if (type === 'http') {
|
return convertProxies(t, json)
|
||||||
const info = await fetchServerInfo()
|
}),
|
||||||
if (info && info.vhostHTTPPort) {
|
|
||||||
proxies.value = json.proxies.map(
|
|
||||||
(p: any) => new HTTPProxy(p, info.vhostHTTPPort, info.subdomainHost),
|
|
||||||
)
|
)
|
||||||
}
|
proxies.value = results.flat()
|
||||||
} else if (type === 'https') {
|
} else {
|
||||||
const info = await fetchServerInfo()
|
const json = await getProxiesByType(type)
|
||||||
if (info && info.vhostHTTPSPort) {
|
proxies.value = await convertProxies(type, json)
|
||||||
proxies.value = json.proxies.map(
|
|
||||||
(p: any) =>
|
|
||||||
new HTTPSProxy(p, info.vhostHTTPSPort, info.subdomainHost),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (type === 'tcpmux') {
|
|
||||||
const info = await fetchServerInfo()
|
|
||||||
if (info && info.tcpmuxHTTPConnectPort) {
|
|
||||||
proxies.value = json.proxies.map(
|
|
||||||
(p: any) =>
|
|
||||||
new TCPMuxProxy(p, info.tcpmuxHTTPConnectPort, info.subdomainHost),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (type === 'stcp') {
|
|
||||||
proxies.value = json.proxies.map((p: any) => new STCPProxy(p))
|
|
||||||
} else if (type === 'sudp') {
|
|
||||||
proxies.value = json.proxies.map((p: any) => new SUDPProxy(p))
|
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
ElMessage({
|
ElMessage({
|
||||||
|
|||||||
Reference in New Issue
Block a user