Skip to content

Commit

Permalink
feat(proxies): auto close connections when proxy selected
Browse files Browse the repository at this point in the history
  • Loading branch information
Zephyruso authored and Zephyruso committed Sep 8, 2023
1 parent 59a7273 commit 15d20cd
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 80 deletions.
8 changes: 2 additions & 6 deletions src/helpers/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default {
off: '关闭',
proxiesPreviewType: '节点组预览样式',
urlForLatencyTest: '测速链接',
autoCloseConns: '切换代理时自动断开全部连接',
autoCloseConns: '切换代理时自动断开连接',
useTwemoji: '使用 Twemoji Mozilla 字体',
autoSwitchTheme: '自动切换主题',
favDayTheme: '浅色主题偏好',
Expand Down
68 changes: 8 additions & 60 deletions src/pages/Connections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -38,8 +37,8 @@ import { formatTimeFromNow } from '~/helpers'
import {
tableSize,
tableSizeClassName,
useConnections,
useRequest,
useWsRequest,
} from '~/signals'
import type { Connection } from '~/types'

Expand All @@ -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(
Expand Down
15 changes: 4 additions & 11 deletions src/pages/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down Expand Up @@ -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">
Expand All @@ -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')}>
Expand Down
113 changes: 113 additions & 0 deletions src/signals/connections.ts
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions src/signals/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './config'
export * from './connections'
export * from './proxies'
export * from './request'
export * from './rules'
Expand Down
10 changes: 9 additions & 1 deletion src/signals/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
useRequest,
} from '~/signals'
import type { Proxy, ProxyNode, ProxyProvider } from '~/types'
import { useConnections } from './connections'

type ProxyInfo = {
name: string
Expand Down Expand Up @@ -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([
Expand Down Expand Up @@ -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({
Expand Down
5 changes: 5 additions & 0 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ export type Connection = {
}
}

export type ConnectionWithSpeed = Connection & {
downloadSpeed: number
uploadSpeed: number
}

export type Log = {
type: string
payload: string
Expand Down

0 comments on commit 15d20cd

Please sign in to comment.