mirror of
https://github.com/MetaCubeX/metacubexd.git
synced 2024-11-23 21:35:36 +08:00
feat(proxies): auto close connections when proxy selected
This commit is contained in:
parent
59a7273665
commit
15d20cde4a
@ -9,12 +9,8 @@ export const formatTimeFromNow = (time: number | string) => {
|
|||||||
export const formatProxyType = (type = '') => {
|
export const formatProxyType = (type = '') => {
|
||||||
const t = type.toLowerCase()
|
const t = type.toLowerCase()
|
||||||
|
|
||||||
if (t === 'shadowsocks') {
|
if (t.includes('shadowsocks')) {
|
||||||
return 'ss'
|
return type.replace('shadowsocks', 'ss') // for both ss and ssr
|
||||||
}
|
|
||||||
|
|
||||||
if (t === 'shadowsocksr') {
|
|
||||||
return 'ssr'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t === 'hysteria') {
|
if (t === 'hysteria') {
|
||||||
|
@ -42,7 +42,7 @@ export default {
|
|||||||
off: 'Off',
|
off: 'Off',
|
||||||
proxiesPreviewType: 'Proxies preview type',
|
proxiesPreviewType: 'Proxies preview type',
|
||||||
urlForLatencyTest: 'URL for latency test',
|
urlForLatencyTest: 'URL for latency test',
|
||||||
autoCloseConns: 'Automatically close all connections',
|
autoCloseConns: 'Automatically close connections when proxy is selected',
|
||||||
useTwemoji: 'Use Twemoji Mozilla Font',
|
useTwemoji: 'Use Twemoji Mozilla Font',
|
||||||
autoSwitchTheme: 'Automatically switch theme',
|
autoSwitchTheme: 'Automatically switch theme',
|
||||||
favDayTheme: 'Favorite light theme',
|
favDayTheme: 'Favorite light theme',
|
||||||
|
@ -42,7 +42,7 @@ export default {
|
|||||||
off: '关闭',
|
off: '关闭',
|
||||||
proxiesPreviewType: '节点组预览样式',
|
proxiesPreviewType: '节点组预览样式',
|
||||||
urlForLatencyTest: '测速链接',
|
urlForLatencyTest: '测速链接',
|
||||||
autoCloseConns: '切换代理时自动断开全部连接',
|
autoCloseConns: '切换代理时自动断开连接',
|
||||||
useTwemoji: '使用 Twemoji Mozilla 字体',
|
useTwemoji: '使用 Twemoji Mozilla 字体',
|
||||||
autoSwitchTheme: '自动切换主题',
|
autoSwitchTheme: '自动切换主题',
|
||||||
favDayTheme: '浅色主题偏好',
|
favDayTheme: '浅色主题偏好',
|
||||||
|
@ -25,8 +25,7 @@ import {
|
|||||||
} from '@tanstack/solid-table'
|
} from '@tanstack/solid-table'
|
||||||
import byteSize from 'byte-size'
|
import byteSize from 'byte-size'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { differenceWith } from 'lodash'
|
import { For, createMemo, createSignal } from 'solid-js'
|
||||||
import { For, createEffect, createMemo, createSignal } from 'solid-js'
|
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
import { Button, ConnectionsTableOrderingModal } from '~/components'
|
import { Button, ConnectionsTableOrderingModal } from '~/components'
|
||||||
import {
|
import {
|
||||||
@ -38,8 +37,8 @@ import { formatTimeFromNow } from '~/helpers'
|
|||||||
import {
|
import {
|
||||||
tableSize,
|
tableSize,
|
||||||
tableSizeClassName,
|
tableSizeClassName,
|
||||||
|
useConnections,
|
||||||
useRequest,
|
useRequest,
|
||||||
useWsRequest,
|
|
||||||
} from '~/signals'
|
} from '~/signals'
|
||||||
import type { Connection } from '~/types'
|
import type { Connection } from '~/types'
|
||||||
|
|
||||||
@ -62,63 +61,12 @@ export default () => {
|
|||||||
|
|
||||||
const [search, setSearch] = createSignal('')
|
const [search, setSearch] = createSignal('')
|
||||||
const [activeTab, setActiveTab] = createSignal(ActiveTab.activeConnections)
|
const [activeTab, setActiveTab] = createSignal(ActiveTab.activeConnections)
|
||||||
|
const {
|
||||||
const connections = useWsRequest<{ connections: Connection[] }>('connections')
|
activeConnectionsWithSpeed,
|
||||||
|
closedConnectionsWithSpeed,
|
||||||
const [closedConnectionsWithSpeed, setClosedConnectionsWithSpeed] =
|
paused,
|
||||||
createSignal<ConnectionWithSpeed[]>([], { equals: () => paused() })
|
setPaused,
|
||||||
|
} = useConnections()
|
||||||
const [activeConnectionsWithSpeed, setActiveConnectionsWithSpeed] =
|
|
||||||
createSignal<ConnectionWithSpeed[]>([], { equals: () => paused() })
|
|
||||||
|
|
||||||
const [paused, setPaused] = createSignal(false)
|
|
||||||
|
|
||||||
const updateConnectionsWithSpeed =
|
|
||||||
(connections: Connection[]) => (prevConnections: ConnectionWithSpeed[]) => {
|
|
||||||
const prevMap = new Map<string, Connection>()
|
|
||||||
prevConnections.forEach((prev) => prevMap.set(prev.id, prev))
|
|
||||||
|
|
||||||
const connectionWithSpeed = connections.map((connection) => {
|
|
||||||
const prevConn = prevMap.get(connection.id)
|
|
||||||
|
|
||||||
if (!prevConn) {
|
|
||||||
return { ...connection, downloadSpeed: 0, uploadSpeed: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...connection,
|
|
||||||
downloadSpeed:
|
|
||||||
connection.download - (prevConn.download ?? connection.download),
|
|
||||||
uploadSpeed:
|
|
||||||
connection.upload - (prevConn.upload ?? connection.upload),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const closedConnections = differenceWith(
|
|
||||||
prevConnections,
|
|
||||||
connectionWithSpeed,
|
|
||||||
(a, b) => a.id === b.id,
|
|
||||||
)
|
|
||||||
|
|
||||||
setClosedConnectionsWithSpeed((prev) =>
|
|
||||||
[...prev, ...closedConnections].slice(-1000),
|
|
||||||
)
|
|
||||||
|
|
||||||
return connectionWithSpeed.slice(-200)
|
|
||||||
}
|
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
const connection = connections()?.connections
|
|
||||||
|
|
||||||
if (!connection) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const updater = updateConnectionsWithSpeed(connection)
|
|
||||||
|
|
||||||
setActiveConnectionsWithSpeed(updater)
|
|
||||||
})
|
|
||||||
|
|
||||||
const onCloseConnection = (id: string) => request.delete(`connections/${id}`)
|
const onCloseConnection = (id: string) => request.delete(`connections/${id}`)
|
||||||
|
|
||||||
const [columnVisibility, setColumnVisibility] = makePersisted(
|
const [columnVisibility, setColumnVisibility] = makePersisted(
|
||||||
|
@ -13,8 +13,7 @@ import {
|
|||||||
createSignal,
|
createSignal,
|
||||||
} from 'solid-js'
|
} from 'solid-js'
|
||||||
import { CHART_MAX_XAXIS, DEFAULT_CHART_OPTIONS } from '~/constants'
|
import { CHART_MAX_XAXIS, DEFAULT_CHART_OPTIONS } from '~/constants'
|
||||||
import { useWsRequest } from '~/signals'
|
import { connections, useWsRequest } from '~/signals'
|
||||||
import type { Connection } from '~/types'
|
|
||||||
|
|
||||||
const TrafficWidget: ParentComponent<{ label: JSX.Element }> = (props) => (
|
const TrafficWidget: ParentComponent<{ label: JSX.Element }> = (props) => (
|
||||||
<div class="stat flex-1 place-items-center">
|
<div class="stat flex-1 place-items-center">
|
||||||
@ -84,12 +83,6 @@ export default () => {
|
|||||||
{ name: t('memory'), data: memories() },
|
{ name: t('memory'), data: memories() },
|
||||||
])
|
])
|
||||||
|
|
||||||
const connection = useWsRequest<{
|
|
||||||
downloadTotal: number
|
|
||||||
uploadTotal: number
|
|
||||||
connections: Connection[]
|
|
||||||
}>('connections')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="stats stats-vertical w-full grid-cols-2 bg-primary shadow lg:stats-horizontal lg:flex">
|
<div class="stats stats-vertical w-full grid-cols-2 bg-primary shadow lg:stats-horizontal lg:flex">
|
||||||
@ -102,15 +95,15 @@ export default () => {
|
|||||||
</TrafficWidget>
|
</TrafficWidget>
|
||||||
|
|
||||||
<TrafficWidget label={t('uploadTotal')}>
|
<TrafficWidget label={t('uploadTotal')}>
|
||||||
{byteSize(connection()?.uploadTotal || 0).toString()}
|
{byteSize(connections()?.uploadTotal || 0).toString()}
|
||||||
</TrafficWidget>
|
</TrafficWidget>
|
||||||
|
|
||||||
<TrafficWidget label={t('downloadTotal')}>
|
<TrafficWidget label={t('downloadTotal')}>
|
||||||
{byteSize(connection()?.downloadTotal || 0).toString()}
|
{byteSize(connections()?.downloadTotal || 0).toString()}
|
||||||
</TrafficWidget>
|
</TrafficWidget>
|
||||||
|
|
||||||
<TrafficWidget label={t('activeConnections')}>
|
<TrafficWidget label={t('activeConnections')}>
|
||||||
{connection()?.connections.length || 0}
|
{connections()?.connections.length || 0}
|
||||||
</TrafficWidget>
|
</TrafficWidget>
|
||||||
|
|
||||||
<TrafficWidget label={t('memoryUsage')}>
|
<TrafficWidget label={t('memoryUsage')}>
|
||||||
|
113
src/signals/connections.ts
Normal file
113
src/signals/connections.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { differenceWith, unionWith } from 'lodash'
|
||||||
|
import {
|
||||||
|
createEffect,
|
||||||
|
createMemo,
|
||||||
|
createResource,
|
||||||
|
createSignal,
|
||||||
|
untrack,
|
||||||
|
} from 'solid-js'
|
||||||
|
import { Connection, ConnectionWithSpeed } from '~/types'
|
||||||
|
import { selectedEndpoint, useWsRequest } from './request'
|
||||||
|
|
||||||
|
type WsMsg = {
|
||||||
|
connections: Connection[]
|
||||||
|
uploadTotal: number
|
||||||
|
downloadTotal: number
|
||||||
|
} | null
|
||||||
|
|
||||||
|
// we make allconnections global so we can keep track of connections when user in proxy page
|
||||||
|
// when user selects proxy and close some connections they can back and check connections
|
||||||
|
// they closed
|
||||||
|
const [allConnectionsWithSpeed, setAllConnectionsWithSpeed] = createSignal<
|
||||||
|
ConnectionWithSpeed[]
|
||||||
|
>([])
|
||||||
|
|
||||||
|
const [latestConnectionWsMessage] = createResource(async () => {
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
createEffect(() => {
|
||||||
|
if (selectedEndpoint()) {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return useWsRequest<WsMsg>('connections')
|
||||||
|
})
|
||||||
|
|
||||||
|
export const connections = createMemo(() => latestConnectionWsMessage()?.())
|
||||||
|
|
||||||
|
export const useConnections = () => {
|
||||||
|
const [closedConnectionsWithSpeed, setClosedConnectionsWithSpeed] =
|
||||||
|
createSignal<ConnectionWithSpeed[]>([])
|
||||||
|
const [activeConnectionsWithSpeed, setActiveConnectionsWithSpeed] =
|
||||||
|
createSignal<ConnectionWithSpeed[]>([])
|
||||||
|
const [paused, setPaused] = createSignal(false)
|
||||||
|
|
||||||
|
const updateConnectionsWithSpeed = (connections: Connection[]) => {
|
||||||
|
const prevActiveConnections = activeConnectionsWithSpeed()
|
||||||
|
const prevMap = new Map<string, Connection>()
|
||||||
|
prevActiveConnections.forEach((prev) => prevMap.set(prev.id, prev))
|
||||||
|
|
||||||
|
const activeConnnections: ConnectionWithSpeed[] = connections.map(
|
||||||
|
(connection) => {
|
||||||
|
const prevConn = prevMap.get(connection.id)
|
||||||
|
|
||||||
|
if (!prevConn) {
|
||||||
|
return { ...connection, downloadSpeed: 0, uploadSpeed: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...connection,
|
||||||
|
downloadSpeed:
|
||||||
|
connection.download - (prevConn.download ?? connection.download),
|
||||||
|
uploadSpeed:
|
||||||
|
connection.upload - (prevConn.upload ?? connection.upload),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const allConnections = unionWith(
|
||||||
|
allConnectionsWithSpeed(),
|
||||||
|
activeConnnections,
|
||||||
|
(a, b) => a.id === b.id,
|
||||||
|
)
|
||||||
|
const closedConnections = differenceWith(
|
||||||
|
allConnections,
|
||||||
|
activeConnnections,
|
||||||
|
(a, b) => a.id === b.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
activeConns: activeConnnections.slice(-200),
|
||||||
|
closedConns: closedConnections.slice(-200),
|
||||||
|
allConns: allConnections.slice(-400),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const connection = connections()?.connections
|
||||||
|
|
||||||
|
if (!connection) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
untrack(() => {
|
||||||
|
const { activeConns, closedConns, allConns } =
|
||||||
|
updateConnectionsWithSpeed(connection)
|
||||||
|
|
||||||
|
if (!paused()) {
|
||||||
|
setActiveConnectionsWithSpeed(activeConns)
|
||||||
|
setClosedConnectionsWithSpeed(closedConns)
|
||||||
|
}
|
||||||
|
|
||||||
|
setAllConnectionsWithSpeed(allConns)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
closedConnectionsWithSpeed,
|
||||||
|
activeConnectionsWithSpeed,
|
||||||
|
paused,
|
||||||
|
setPaused,
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
export * from './config'
|
export * from './config'
|
||||||
|
export * from './connections'
|
||||||
export * from './proxies'
|
export * from './proxies'
|
||||||
export * from './request'
|
export * from './request'
|
||||||
export * from './rules'
|
export * from './rules'
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
useRequest,
|
useRequest,
|
||||||
} from '~/signals'
|
} from '~/signals'
|
||||||
import type { Proxy, ProxyNode, ProxyProvider } from '~/types'
|
import type { Proxy, ProxyNode, ProxyProvider } from '~/types'
|
||||||
|
import { useConnections } from './connections'
|
||||||
|
|
||||||
type ProxyInfo = {
|
type ProxyInfo = {
|
||||||
name: string
|
name: string
|
||||||
@ -46,6 +47,7 @@ const setProxiesInfo = (proxies: (Proxy | ProxyNode)[]) => {
|
|||||||
|
|
||||||
export const useProxies = () => {
|
export const useProxies = () => {
|
||||||
const request = useRequest()
|
const request = useRequest()
|
||||||
|
const { activeConnectionsWithSpeed } = useConnections()
|
||||||
|
|
||||||
const updateProxies = async () => {
|
const updateProxies = async () => {
|
||||||
const [{ providers }, { proxies }] = await Promise.all([
|
const [{ providers }, { proxies }] = await Promise.all([
|
||||||
@ -80,7 +82,13 @@ export const useProxies = () => {
|
|||||||
const proxyGroupList = proxies().slice()
|
const proxyGroupList = proxies().slice()
|
||||||
const proxyGroup = proxyGroupList.find((i) => i.name === proxy.name)!
|
const proxyGroup = proxyGroupList.find((i) => i.name === proxy.name)!
|
||||||
|
|
||||||
if (autoCloseConns()) request.delete('connections')
|
if (autoCloseConns()) {
|
||||||
|
activeConnectionsWithSpeed().forEach(({ id, chains }) => {
|
||||||
|
if (chains.includes(proxy.name)) {
|
||||||
|
request.delete(`connections/${id}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
await request.put(`proxies/${proxy.name}`, {
|
await request.put(`proxies/${proxy.name}`, {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
5
src/types/index.d.ts
vendored
5
src/types/index.d.ts
vendored
@ -100,6 +100,11 @@ export type Connection = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ConnectionWithSpeed = Connection & {
|
||||||
|
downloadSpeed: number
|
||||||
|
uploadSpeed: number
|
||||||
|
}
|
||||||
|
|
||||||
export type Log = {
|
export type Log = {
|
||||||
type: string
|
type: string
|
||||||
payload: string
|
payload: string
|
||||||
|
Loading…
Reference in New Issue
Block a user