mirror of
https://github.com/MetaCubeX/metacubexd.git
synced 2024-12-26 19:24:12 +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 = '') => {
|
||||
const t = type.toLowerCase()
|
||||
|
||||
if (t === 'shadowsocks') {
|
||||
return 'ss'
|
||||
}
|
||||
|
||||
if (t === 'shadowsocksr') {
|
||||
return 'ssr'
|
||||
if (t.includes('shadowsocks')) {
|
||||
return type.replace('shadowsocks', 'ss') // for both ss and ssr
|
||||
}
|
||||
|
||||
if (t === 'hysteria') {
|
||||
|
@ -42,7 +42,7 @@ export default {
|
||||
off: 'Off',
|
||||
proxiesPreviewType: 'Proxies preview type',
|
||||
urlForLatencyTest: 'URL for latency test',
|
||||
autoCloseConns: 'Automatically close all connections',
|
||||
autoCloseConns: 'Automatically close connections when proxy is selected',
|
||||
useTwemoji: 'Use Twemoji Mozilla Font',
|
||||
autoSwitchTheme: 'Automatically switch theme',
|
||||
favDayTheme: 'Favorite light theme',
|
||||
|
@ -42,7 +42,7 @@ export default {
|
||||
off: '关闭',
|
||||
proxiesPreviewType: '节点组预览样式',
|
||||
urlForLatencyTest: '测速链接',
|
||||
autoCloseConns: '切换代理时自动断开全部连接',
|
||||
autoCloseConns: '切换代理时自动断开连接',
|
||||
useTwemoji: '使用 Twemoji Mozilla 字体',
|
||||
autoSwitchTheme: '自动切换主题',
|
||||
favDayTheme: '浅色主题偏好',
|
||||
|
@ -25,8 +25,7 @@ import {
|
||||
} from '@tanstack/solid-table'
|
||||
import byteSize from 'byte-size'
|
||||
import dayjs from 'dayjs'
|
||||
import { differenceWith } from 'lodash'
|
||||
import { For, createEffect, createMemo, createSignal } from 'solid-js'
|
||||
import { For, createMemo, createSignal } from 'solid-js'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Button, ConnectionsTableOrderingModal } from '~/components'
|
||||
import {
|
||||
@ -38,8 +37,8 @@ import { formatTimeFromNow } from '~/helpers'
|
||||
import {
|
||||
tableSize,
|
||||
tableSizeClassName,
|
||||
useConnections,
|
||||
useRequest,
|
||||
useWsRequest,
|
||||
} from '~/signals'
|
||||
import type { Connection } from '~/types'
|
||||
|
||||
@ -62,63 +61,12 @@ export default () => {
|
||||
|
||||
const [search, setSearch] = createSignal('')
|
||||
const [activeTab, setActiveTab] = createSignal(ActiveTab.activeConnections)
|
||||
|
||||
const connections = useWsRequest<{ connections: Connection[] }>('connections')
|
||||
|
||||
const [closedConnectionsWithSpeed, setClosedConnectionsWithSpeed] =
|
||||
createSignal<ConnectionWithSpeed[]>([], { equals: () => paused() })
|
||||
|
||||
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 {
|
||||
activeConnectionsWithSpeed,
|
||||
closedConnectionsWithSpeed,
|
||||
paused,
|
||||
setPaused,
|
||||
} = useConnections()
|
||||
const onCloseConnection = (id: string) => request.delete(`connections/${id}`)
|
||||
|
||||
const [columnVisibility, setColumnVisibility] = makePersisted(
|
||||
|
@ -13,8 +13,7 @@ import {
|
||||
createSignal,
|
||||
} from 'solid-js'
|
||||
import { CHART_MAX_XAXIS, DEFAULT_CHART_OPTIONS } from '~/constants'
|
||||
import { useWsRequest } from '~/signals'
|
||||
import type { Connection } from '~/types'
|
||||
import { connections, useWsRequest } from '~/signals'
|
||||
|
||||
const TrafficWidget: ParentComponent<{ label: JSX.Element }> = (props) => (
|
||||
<div class="stat flex-1 place-items-center">
|
||||
@ -84,12 +83,6 @@ export default () => {
|
||||
{ name: t('memory'), data: memories() },
|
||||
])
|
||||
|
||||
const connection = useWsRequest<{
|
||||
downloadTotal: number
|
||||
uploadTotal: number
|
||||
connections: Connection[]
|
||||
}>('connections')
|
||||
|
||||
return (
|
||||
<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">
|
||||
@ -102,15 +95,15 @@ export default () => {
|
||||
</TrafficWidget>
|
||||
|
||||
<TrafficWidget label={t('uploadTotal')}>
|
||||
{byteSize(connection()?.uploadTotal || 0).toString()}
|
||||
{byteSize(connections()?.uploadTotal || 0).toString()}
|
||||
</TrafficWidget>
|
||||
|
||||
<TrafficWidget label={t('downloadTotal')}>
|
||||
{byteSize(connection()?.downloadTotal || 0).toString()}
|
||||
{byteSize(connections()?.downloadTotal || 0).toString()}
|
||||
</TrafficWidget>
|
||||
|
||||
<TrafficWidget label={t('activeConnections')}>
|
||||
{connection()?.connections.length || 0}
|
||||
{connections()?.connections.length || 0}
|
||||
</TrafficWidget>
|
||||
|
||||
<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 './connections'
|
||||
export * from './proxies'
|
||||
export * from './request'
|
||||
export * from './rules'
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
useRequest,
|
||||
} from '~/signals'
|
||||
import type { Proxy, ProxyNode, ProxyProvider } from '~/types'
|
||||
import { useConnections } from './connections'
|
||||
|
||||
type ProxyInfo = {
|
||||
name: string
|
||||
@ -46,6 +47,7 @@ const setProxiesInfo = (proxies: (Proxy | ProxyNode)[]) => {
|
||||
|
||||
export const useProxies = () => {
|
||||
const request = useRequest()
|
||||
const { activeConnectionsWithSpeed } = useConnections()
|
||||
|
||||
const updateProxies = async () => {
|
||||
const [{ providers }, { proxies }] = await Promise.all([
|
||||
@ -80,7 +82,13 @@ export const useProxies = () => {
|
||||
const proxyGroupList = proxies().slice()
|
||||
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}`, {
|
||||
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 = {
|
||||
type: string
|
||||
payload: string
|
||||
|
Loading…
x
Reference in New Issue
Block a user