mirror of
https://github.com/MetaCubeX/metacubexd.git
synced 2024-11-10 05:15:35 +08:00
feat: quick filter for connections (#856)
* feat: quick filter for connections * feat: make persisted for collapse map status * fix: config error when tun is disabled in sing-box
This commit is contained in:
parent
b419bed6b1
commit
916a661011
@ -31,8 +31,10 @@ import {
|
|||||||
allConnections,
|
allConnections,
|
||||||
clientSourceIPTags,
|
clientSourceIPTags,
|
||||||
connectionsTableSize,
|
connectionsTableSize,
|
||||||
|
quickFilterRegex,
|
||||||
setClientSourceIPTags,
|
setClientSourceIPTags,
|
||||||
setConnectionsTableSize,
|
setConnectionsTableSize,
|
||||||
|
setQuickFilterRegex,
|
||||||
} from '~/signals'
|
} from '~/signals'
|
||||||
import {
|
import {
|
||||||
ConnectionsTableColumnOrder,
|
ConnectionsTableColumnOrder,
|
||||||
@ -205,6 +207,16 @@ export const ConnectionsSettingsModal = (props: {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
|
<div>
|
||||||
|
<ConfigTitle withDivider>{t('quickFilter')}</ConfigTitle>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
onInput={(e) => setQuickFilterRegex(e.target.value)}
|
||||||
|
value={quickFilterRegex()}
|
||||||
|
></input>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ConfigTitle withDivider>{t('tableSize')}</ConfigTitle>
|
<ConfigTitle withDivider>{t('tableSize')}</ConfigTitle>
|
||||||
|
|
||||||
|
@ -112,4 +112,5 @@ export default {
|
|||||||
en: 'English',
|
en: 'English',
|
||||||
zh: 'Chinese',
|
zh: 'Chinese',
|
||||||
port: '{{ name }} Port',
|
port: '{{ name }} Port',
|
||||||
|
quickFilter: 'Quick Filter',
|
||||||
}
|
}
|
||||||
|
@ -114,4 +114,5 @@ export default {
|
|||||||
en: '英文',
|
en: '英文',
|
||||||
zh: '中文',
|
zh: '中文',
|
||||||
port: '{{ name }} 端口',
|
port: '{{ name }} 端口',
|
||||||
|
quickFilter: '快速过滤',
|
||||||
} satisfies Dict
|
} satisfies Dict
|
||||||
|
@ -244,7 +244,7 @@ const ConfigForm: Component<{ backendVersion: Accessor<string> }> = ({
|
|||||||
id="enable-tun-device"
|
id="enable-tun-device"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="toggle"
|
class="toggle"
|
||||||
checked={configsData()?.tun.enable}
|
checked={configsData()?.tun?.enable}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
void updateBackendConfigAPI(
|
void updateBackendConfigAPI(
|
||||||
'tun',
|
'tun',
|
||||||
@ -263,7 +263,7 @@ const ConfigForm: Component<{ backendVersion: Accessor<string> }> = ({
|
|||||||
<select
|
<select
|
||||||
id="tun-ip-stack"
|
id="tun-ip-stack"
|
||||||
class="select select-bordered flex-1"
|
class="select select-bordered flex-1"
|
||||||
value={configsData()?.tun.stack}
|
value={configsData()?.tun?.stack}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
void updateBackendConfigAPI(
|
void updateBackendConfigAPI(
|
||||||
'tun',
|
'tun',
|
||||||
@ -287,7 +287,7 @@ const ConfigForm: Component<{ backendVersion: Accessor<string> }> = ({
|
|||||||
<input
|
<input
|
||||||
id="device-name"
|
id="device-name"
|
||||||
class="input input-bordered min-w-0"
|
class="input input-bordered min-w-0"
|
||||||
value={configsData()?.tun.device}
|
value={configsData()?.tun?.device}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
void updateBackendConfigAPI(
|
void updateBackendConfigAPI(
|
||||||
'tun',
|
'tun',
|
||||||
|
@ -45,6 +45,7 @@ import {
|
|||||||
connectionsTableSize,
|
connectionsTableSize,
|
||||||
endpoint,
|
endpoint,
|
||||||
formatTimeFromNow,
|
formatTimeFromNow,
|
||||||
|
quickFilterRegex,
|
||||||
setConnectionsTableColumnOrder,
|
setConnectionsTableColumnOrder,
|
||||||
setConnectionsTableColumnVisibility,
|
setConnectionsTableColumnVisibility,
|
||||||
tableSizeClassName,
|
tableSizeClassName,
|
||||||
@ -89,7 +90,13 @@ export default () => {
|
|||||||
useConnections()
|
useConnections()
|
||||||
|
|
||||||
const [globalFilter, setGlobalFilter] = createSignal('')
|
const [globalFilter, setGlobalFilter] = createSignal('')
|
||||||
|
const [enableQuickFilter, setEnableQuickFilter] = makePersisted(
|
||||||
|
createSignal(false),
|
||||||
|
{
|
||||||
|
name: 'enableQuickFilter',
|
||||||
|
storage: localStorage,
|
||||||
|
},
|
||||||
|
)
|
||||||
const [selectedConnectionID, setSelectedConnectionID] = createSignal<string>()
|
const [selectedConnectionID, setSelectedConnectionID] = createSignal<string>()
|
||||||
|
|
||||||
const columns: ColumnDef<Connection>[] = [
|
const columns: ColumnDef<Connection>[] = [
|
||||||
@ -283,9 +290,24 @@ export default () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
get data() {
|
get data() {
|
||||||
return activeTab() === ActiveTab.activeConnections
|
const connections =
|
||||||
? activeConnections()
|
activeTab() === ActiveTab.activeConnections
|
||||||
: closedConnections()
|
? activeConnections()
|
||||||
|
: closedConnections()
|
||||||
|
|
||||||
|
connections.sort((a, b) => {
|
||||||
|
return a.id.localeCompare(b.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!enableQuickFilter()) {
|
||||||
|
return connections
|
||||||
|
}
|
||||||
|
|
||||||
|
const reg = new RegExp(quickFilterRegex(), 'i')
|
||||||
|
|
||||||
|
return connections.filter(
|
||||||
|
(connection) => !reg.test(connection.chains.join('')),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
sortDescFirst: true,
|
sortDescFirst: true,
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
@ -353,6 +375,16 @@ export default () => {
|
|||||||
</Index>
|
</Index>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="mr-2 hidden lg:inline-block">{t('quickFilter')}:</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle"
|
||||||
|
checked={enableQuickFilter()}
|
||||||
|
onChange={(e) => setEnableQuickFilter(e.target.checked)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
class="select select-bordered select-primary select-sm w-full max-w-full flex-1 sm:select-md"
|
class="select select-bordered select-primary select-sm w-full max-w-full flex-1 sm:select-md"
|
||||||
onChange={(e) => setSourceIPFilter(e.target.value)}
|
onChange={(e) => setSourceIPFilter(e.target.value)}
|
||||||
|
@ -87,8 +87,8 @@ export default () => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2 lg:h-full">
|
||||||
<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 flex-shrink-0 grid-cols-2 bg-gradient-to-br from-primary to-secondary shadow lg:stats-horizontal lg:flex">
|
||||||
<TrafficWidget label={t('upload')}>
|
<TrafficWidget label={t('upload')}>
|
||||||
{byteSize(traffic()?.up || 0).toString()}/s
|
{byteSize(traffic()?.up || 0).toString()}/s
|
||||||
</TrafficWidget>
|
</TrafficWidget>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { makePersisted } from '@solid-primitives/storage'
|
||||||
import {
|
import {
|
||||||
IconBrandSpeedtest,
|
IconBrandSpeedtest,
|
||||||
IconReload,
|
IconReload,
|
||||||
@ -55,14 +56,24 @@ export default () => {
|
|||||||
updateAllProvider,
|
updateAllProvider,
|
||||||
proxyGroupLatencyTest,
|
proxyGroupLatencyTest,
|
||||||
proxyProviderLatencyTest,
|
proxyProviderLatencyTest,
|
||||||
collapsedMap,
|
|
||||||
setCollapsedMap,
|
|
||||||
proxyGroupLatencyTestingMap,
|
proxyGroupLatencyTestingMap,
|
||||||
proxyProviderLatencyTestingMap,
|
proxyProviderLatencyTestingMap,
|
||||||
isAllProviderUpdating,
|
isAllProviderUpdating,
|
||||||
updatingMap,
|
updatingMap,
|
||||||
} = useProxies()
|
} = useProxies()
|
||||||
|
|
||||||
|
const [collapsedMap, setCollapsedMap] = makePersisted(
|
||||||
|
createSignal<Record<string, boolean>>({}),
|
||||||
|
{
|
||||||
|
name: 'collapsedMap',
|
||||||
|
storage: localStorage,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const setCollapsedMapByKey = (key: string, value: boolean) => {
|
||||||
|
setCollapsedMap((prev) => ({ ...prev, [key]: value }))
|
||||||
|
}
|
||||||
|
|
||||||
onMount(fetchProxies)
|
onMount(fetchProxies)
|
||||||
|
|
||||||
const onProxyGroupLatencyTestClick = async (
|
const onProxyGroupLatencyTestClick = async (
|
||||||
@ -221,7 +232,9 @@ export default () => {
|
|||||||
<Collapse
|
<Collapse
|
||||||
isOpen={collapsedMap()[proxyGroup.name]}
|
isOpen={collapsedMap()[proxyGroup.name]}
|
||||||
title={title}
|
title={title}
|
||||||
onCollapse={(val) => setCollapsedMap(proxyGroup.name, val)}
|
onCollapse={(val) =>
|
||||||
|
setCollapsedMapByKey(proxyGroup.name, val)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<For each={sortedProxyNames()}>
|
<For each={sortedProxyNames()}>
|
||||||
{(proxyName) => (
|
{(proxyName) => (
|
||||||
@ -330,7 +343,7 @@ export default () => {
|
|||||||
isOpen={collapsedMap()[proxyProvider.name]}
|
isOpen={collapsedMap()[proxyProvider.name]}
|
||||||
title={title}
|
title={title}
|
||||||
onCollapse={(val) =>
|
onCollapse={(val) =>
|
||||||
setCollapsedMap(proxyProvider.name, val)
|
setCollapsedMapByKey(proxyProvider.name, val)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<For each={sortedProxyNames()}>
|
<For each={sortedProxyNames()}>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { makePersisted } from '@solid-primitives/storage'
|
||||||
import { differenceWith, isNumber, unionWith } from 'lodash'
|
import { differenceWith, isNumber, unionWith } from 'lodash'
|
||||||
import { CONNECTIONS_TABLE_MAX_CLOSED_ROWS } from '~/constants'
|
import { CONNECTIONS_TABLE_MAX_CLOSED_ROWS } from '~/constants'
|
||||||
import { Connection, ConnectionRawMessage } from '~/types'
|
import { Connection, ConnectionRawMessage } from '~/types'
|
||||||
@ -8,6 +9,14 @@ export type WsMsg = {
|
|||||||
downloadTotal: number
|
downloadTotal: number
|
||||||
} | null
|
} | null
|
||||||
|
|
||||||
|
export const [quickFilterRegex, setQuickFilterRegex] = makePersisted(
|
||||||
|
createSignal<string>('Direct|direct|dns-out'),
|
||||||
|
{
|
||||||
|
name: 'quickFilterRegex',
|
||||||
|
storage: localStorage,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
// we make connections global, so we can keep track of connections when user in proxy page
|
// we make connections 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
|
// when user selects proxy and close some connections they can back and check connections
|
||||||
// they closed
|
// they closed
|
||||||
|
@ -32,7 +32,6 @@ type ProxyInfo = {
|
|||||||
export type ProxyWithProvider = Proxy & { provider?: string }
|
export type ProxyWithProvider = Proxy & { provider?: string }
|
||||||
export type ProxyNodeWithProvider = ProxyNode & { provider?: string }
|
export type ProxyNodeWithProvider = ProxyNode & { provider?: string }
|
||||||
|
|
||||||
const { map: collapsedMap, set: setCollapsedMap } = useStringBooleanMap()
|
|
||||||
const {
|
const {
|
||||||
map: proxyLatencyTestingMap,
|
map: proxyLatencyTestingMap,
|
||||||
setWithCallback: setProxyLatencyTestingMap,
|
setWithCallback: setProxyLatencyTestingMap,
|
||||||
@ -322,8 +321,6 @@ export const useProxies = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
collapsedMap,
|
|
||||||
setCollapsedMap,
|
|
||||||
proxyIPv6SupportMap,
|
proxyIPv6SupportMap,
|
||||||
proxyLatencyTestingMap,
|
proxyLatencyTestingMap,
|
||||||
proxyGroupLatencyTestingMap,
|
proxyGroupLatencyTestingMap,
|
||||||
|
Loading…
Reference in New Issue
Block a user