mirror of
https://github.com/MetaCubeX/metacubexd.git
synced 2024-12-26 07:24:10 +08:00
feat(connections): tag client source ip with name, closes #181
This commit is contained in:
parent
b2aa3585c5
commit
e538f9d057
@ -20,6 +20,7 @@
|
||||
"@solid-primitives/clipboard": "^1.5.7",
|
||||
"@solid-primitives/event-listener": "^2.3.0",
|
||||
"@solid-primitives/i18n": "^1.4.1",
|
||||
"@solid-primitives/keyed": "^1.2.0",
|
||||
"@solid-primitives/media": "^2.2.5",
|
||||
"@solid-primitives/resize-observer": "^2.0.22",
|
||||
"@solid-primitives/storage": "^2.1.1",
|
||||
|
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@ -26,6 +26,9 @@ dependencies:
|
||||
'@solid-primitives/i18n':
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1(solid-js@1.7.11)
|
||||
'@solid-primitives/keyed':
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0(solid-js@1.7.11)
|
||||
'@solid-primitives/media':
|
||||
specifier: ^2.2.5
|
||||
version: 2.2.5(solid-js@1.7.11)
|
||||
@ -2044,6 +2047,14 @@ packages:
|
||||
solid-js: 1.7.11
|
||||
dev: false
|
||||
|
||||
/@solid-primitives/keyed@1.2.0(solid-js@1.7.11):
|
||||
resolution: {integrity: sha512-0DuTeJdxWjCTu73XnDZs24JzfXckBnpvCfQ6Mf/kTPKkMZJh7tjkBnZEk48ckrE9xmwat9stIdfrBmZctsepIw==}
|
||||
peerDependencies:
|
||||
solid-js: ^1.6.12
|
||||
dependencies:
|
||||
solid-js: 1.7.11
|
||||
dev: false
|
||||
|
||||
/@solid-primitives/media@2.2.5(solid-js@1.7.11):
|
||||
resolution: {integrity: sha512-wTESNFteSwOZsNIBPLMIVLuOHIIzt2AIZdaCYYxfsJIr/xjDqSomlmdFlAmxfJD3ondO7fwtWfc0rcmAvjoPCA==}
|
||||
peerDependencies:
|
||||
@ -2259,10 +2270,6 @@ packages:
|
||||
resolution: {integrity: sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g==}
|
||||
dev: false
|
||||
|
||||
/@types/node@20.6.0:
|
||||
resolution: {integrity: sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==}
|
||||
dev: false
|
||||
|
||||
/@types/node@20.6.1:
|
||||
resolution: {integrity: sha512-4LcJvuXQlv4lTHnxwyHQZ3uR9Zw2j7m1C9DfuwoTFQQP4Pmu04O6IfLYgMmHoOCt0nosItLLZAH+sOrRE0Bo8g==}
|
||||
dev: false
|
||||
@ -2274,7 +2281,7 @@ packages:
|
||||
/@types/resolve@1.17.1:
|
||||
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
|
||||
dependencies:
|
||||
'@types/node': 20.6.0
|
||||
'@types/node': 20.6.1
|
||||
dev: false
|
||||
|
||||
/@types/semver@7.5.1:
|
||||
@ -4140,7 +4147,7 @@ packages:
|
||||
resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
dependencies:
|
||||
'@types/node': 20.6.0
|
||||
'@types/node': 20.6.1
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 7.2.0
|
||||
dev: false
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { createForm } from '@felte/solid'
|
||||
import { validator } from '@felte/validator-zod'
|
||||
import { useI18n } from '@solid-primitives/i18n'
|
||||
import { IconX } from '@tabler/icons-solidjs'
|
||||
import type {
|
||||
DragEventHandler,
|
||||
Draggable,
|
||||
@ -13,7 +16,9 @@ import {
|
||||
createSortable,
|
||||
useDragDropContext,
|
||||
} from '@thisbeyond/solid-dnd'
|
||||
import { Component, For, Show, createSignal } from 'solid-js'
|
||||
import { uniq } from 'lodash'
|
||||
import { Component, For, Index, Show, createSignal } from 'solid-js'
|
||||
import { z } from 'zod'
|
||||
import { Button, ConfigTitle } from '~/components'
|
||||
import {
|
||||
CONNECTIONS_TABLE_ACCESSOR_KEY,
|
||||
@ -22,16 +27,87 @@ import {
|
||||
MODAL,
|
||||
TAILWINDCSS_SIZE,
|
||||
} from '~/constants'
|
||||
import { connectionsTableSize, setConnectionsTableSize } from '~/signals'
|
||||
import {
|
||||
allConnections,
|
||||
clientSourceIPTags,
|
||||
connectionsTableSize,
|
||||
setClientSourceIPTags,
|
||||
setConnectionsTableSize,
|
||||
} from '~/signals'
|
||||
import {
|
||||
ConnectionsTableColumnOrder,
|
||||
ConnectionsTableColumnVisibility,
|
||||
} from '~/types'
|
||||
|
||||
type ColumnVisibility = Partial<Record<CONNECTIONS_TABLE_ACCESSOR_KEY, boolean>>
|
||||
type ColumnOrder = CONNECTIONS_TABLE_ACCESSOR_KEY[]
|
||||
const TagClientSourceIPWithNameForm: Component = () => {
|
||||
const schema = z.object({
|
||||
tagName: z.string().nonempty(),
|
||||
sourceIP: z.string().nonempty(),
|
||||
})
|
||||
|
||||
const [t] = useI18n()
|
||||
|
||||
const { form } = createForm<z.infer<typeof schema>>({
|
||||
extend: validator({ schema }),
|
||||
onSubmit: ({ tagName, sourceIP }) =>
|
||||
setClientSourceIPTags((tags) => {
|
||||
if (
|
||||
tags.some(
|
||||
(tag) => tag.tagName === tagName || tag.sourceIP === sourceIP,
|
||||
)
|
||||
) {
|
||||
return tags
|
||||
}
|
||||
|
||||
return [...tags, { tagName, sourceIP }]
|
||||
}),
|
||||
})
|
||||
|
||||
return (
|
||||
<form use:form={form}>
|
||||
<div class="join flex">
|
||||
<select name="sourceIP" class="select join-item select-bordered">
|
||||
<option />
|
||||
|
||||
<Index
|
||||
each={uniq(
|
||||
allConnections().map(({ metadata: { sourceIP } }) => sourceIP),
|
||||
)
|
||||
.sort()
|
||||
.filter(
|
||||
(sourceIP) =>
|
||||
!clientSourceIPTags().some(
|
||||
({ sourceIP: tagSourceIP }) => tagSourceIP === sourceIP,
|
||||
),
|
||||
)}
|
||||
>
|
||||
{(sourceIP) => (
|
||||
<option class="badge" value={sourceIP()}>
|
||||
{sourceIP()}
|
||||
</option>
|
||||
)}
|
||||
</Index>
|
||||
</select>
|
||||
|
||||
<input
|
||||
name="tagName"
|
||||
class="input join-item input-bordered min-w-0 flex-1"
|
||||
placeholder="name"
|
||||
/>
|
||||
|
||||
<Button type="submit" class="join-item">
|
||||
{t('tag')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
export const ConnectionsSettingsModal = (props: {
|
||||
order: ColumnOrder
|
||||
visible: ColumnVisibility
|
||||
onOrderChange: (value: ColumnOrder) => void
|
||||
onVisibleChange: (value: ColumnVisibility) => void
|
||||
order: ConnectionsTableColumnOrder
|
||||
visible: ConnectionsTableColumnVisibility
|
||||
onOrderChange: (value: ConnectionsTableColumnOrder) => void
|
||||
onVisibleChange: (value: ConnectionsTableColumnVisibility) => void
|
||||
}) => {
|
||||
const [t] = useI18n()
|
||||
const [activeKey, setActiveKey] =
|
||||
@ -123,6 +199,39 @@ export const ConnectionsSettingsModal = (props: {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ConfigTitle withDivider>
|
||||
{t('tagClientSourceIPWithName')}
|
||||
</ConfigTitle>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<TagClientSourceIPWithNameForm />
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<For each={clientSourceIPTags()}>
|
||||
{({ tagName, sourceIP }) => (
|
||||
<div class="badge badge-primary badge-lg items-center gap-2">
|
||||
<span>
|
||||
{tagName} - {sourceIP}
|
||||
</span>
|
||||
|
||||
<Button
|
||||
class="btn-circle btn-ghost btn-xs"
|
||||
onClick={() =>
|
||||
setClientSourceIPTags((tags) =>
|
||||
tags.filter((tag) => tag.tagName !== tagName),
|
||||
)
|
||||
}
|
||||
>
|
||||
<IconX size={12} />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ConfigTitle withDivider>{t('sort')}</ConfigTitle>
|
||||
|
||||
|
@ -15,7 +15,7 @@ export const ConnectionsTableDetailsModal: Component<{
|
||||
<pre>
|
||||
<code>
|
||||
{JSON.stringify(
|
||||
allConnections.find(
|
||||
allConnections().find(
|
||||
({ id }) => id === props.selectedConnectionID,
|
||||
),
|
||||
null,
|
||||
|
@ -89,4 +89,6 @@ export default {
|
||||
hideUnAvailableProxies: 'Hide UnAvailable Proxies',
|
||||
reloadConfigFile: 'Reload Config File',
|
||||
flushFakeIPData: 'Flush Fake-IP Data',
|
||||
tagClientSourceIPWithName: 'Tag Client Source IP With Name',
|
||||
tag: 'Tag',
|
||||
}
|
||||
|
@ -89,4 +89,6 @@ export default {
|
||||
hideUnAvailableProxies: '隐藏不可用节点',
|
||||
reloadConfigFile: '重新加载配置文件',
|
||||
flushFakeIPData: '清空 Fake-IP 数据',
|
||||
tagClientSourceIPWithName: '为客户端源 IP 地址添加名称标记',
|
||||
tag: '标记',
|
||||
}
|
||||
|
@ -36,23 +36,20 @@ import {
|
||||
ConnectionsSettingsModal,
|
||||
ConnectionsTableDetailsModal,
|
||||
} from '~/components'
|
||||
import {
|
||||
CONNECTIONS_TABLE_ACCESSOR_KEY,
|
||||
CONNECTIONS_TABLE_INITIAL_COLUMN_ORDER,
|
||||
CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY,
|
||||
MODAL,
|
||||
} from '~/constants'
|
||||
import { CONNECTIONS_TABLE_ACCESSOR_KEY, MODAL } from '~/constants'
|
||||
import { formatTimeFromNow } from '~/helpers'
|
||||
import {
|
||||
clientSourceIPTags,
|
||||
connectionsTableColumnOrder,
|
||||
connectionsTableColumnVisibility,
|
||||
connectionsTableSize,
|
||||
setConnectionsTableColumnOrder,
|
||||
setConnectionsTableColumnVisibility,
|
||||
tableSizeClassName,
|
||||
useConnections,
|
||||
} from '~/signals'
|
||||
import type { Connection } from '~/types'
|
||||
|
||||
type ColumnVisibility = Partial<Record<CONNECTIONS_TABLE_ACCESSOR_KEY, boolean>>
|
||||
type ColumnOrder = CONNECTIONS_TABLE_ACCESSOR_KEY[]
|
||||
|
||||
enum ActiveTab {
|
||||
activeConnections,
|
||||
closedConnections,
|
||||
@ -79,20 +76,6 @@ export default () => {
|
||||
useConnections()
|
||||
|
||||
const [globalFilter, setGlobalFilter] = createSignal('')
|
||||
const [columnVisibility, setColumnVisibility] = makePersisted(
|
||||
createSignal<ColumnVisibility>(CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY),
|
||||
{
|
||||
name: 'columnVisibility',
|
||||
storage: localStorage,
|
||||
},
|
||||
)
|
||||
const [columnOrder, setColumnOrder] = makePersisted(
|
||||
createSignal<ColumnOrder>(CONNECTIONS_TABLE_INITIAL_COLUMN_ORDER),
|
||||
{
|
||||
name: 'columnOrder',
|
||||
storage: localStorage,
|
||||
},
|
||||
)
|
||||
|
||||
const [selectedConnectionID, setSelectedConnectionID] = createSignal<string>()
|
||||
|
||||
@ -227,7 +210,13 @@ export default () => {
|
||||
{
|
||||
header: () => t('sourceIP'),
|
||||
accessorKey: CONNECTIONS_TABLE_ACCESSOR_KEY.SourceIP,
|
||||
accessorFn: (original) => original.metadata.sourceIP,
|
||||
accessorFn: (original) => {
|
||||
const tag = clientSourceIPTags().find(
|
||||
(tag) => tag.sourceIP === original.metadata.sourceIP,
|
||||
)
|
||||
|
||||
return tag ? tag.tagName : original.metadata.sourceIP
|
||||
},
|
||||
},
|
||||
{
|
||||
header: () => t('sourcePort'),
|
||||
@ -258,7 +247,7 @@ export default () => {
|
||||
},
|
||||
state: {
|
||||
get columnOrder() {
|
||||
return columnOrder()
|
||||
return connectionsTableColumnOrder()
|
||||
},
|
||||
get grouping() {
|
||||
return grouping()
|
||||
@ -267,7 +256,7 @@ export default () => {
|
||||
return sorting()
|
||||
},
|
||||
get columnVisibility() {
|
||||
return columnVisibility()
|
||||
return connectionsTableColumnVisibility()
|
||||
},
|
||||
get globalFilter() {
|
||||
return globalFilter()
|
||||
@ -494,11 +483,11 @@ export default () => {
|
||||
</div>
|
||||
|
||||
<ConnectionsSettingsModal
|
||||
order={columnOrder()}
|
||||
visible={columnVisibility()}
|
||||
onOrderChange={(data: ColumnOrder) => setColumnOrder(data)}
|
||||
onVisibleChange={(data: ColumnVisibility) =>
|
||||
setColumnVisibility({ ...data })
|
||||
order={connectionsTableColumnOrder()}
|
||||
visible={connectionsTableColumnVisibility()}
|
||||
onOrderChange={(data) => setConnectionsTableColumnOrder(data)}
|
||||
onVisibleChange={(data) =>
|
||||
setConnectionsTableColumnVisibility({ ...data })
|
||||
}
|
||||
/>
|
||||
|
||||
|
@ -2,6 +2,8 @@ import { usePrefersDark } from '@solid-primitives/media'
|
||||
import { makePersisted } from '@solid-primitives/storage'
|
||||
import { createEffect, createSignal } from 'solid-js'
|
||||
import {
|
||||
CONNECTIONS_TABLE_INITIAL_COLUMN_ORDER,
|
||||
CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY,
|
||||
LATENCY_QUALITY_MAP_HTTP,
|
||||
LATENCY_QUALITY_MAP_HTTPS,
|
||||
LOG_LEVEL,
|
||||
@ -10,6 +12,10 @@ import {
|
||||
TAILWINDCSS_SIZE,
|
||||
} from '~/constants'
|
||||
import { setCurTheme } from '~/signals'
|
||||
import {
|
||||
ConnectionsTableColumnOrder,
|
||||
ConnectionsTableColumnVisibility,
|
||||
} from '~/types'
|
||||
|
||||
export const [proxiesPreviewType, setProxiesPreviewType] = makePersisted(
|
||||
createSignal(PROXIES_PREVIEW_TYPE.Auto),
|
||||
@ -54,6 +60,35 @@ export const [connectionsTableSize, setConnectionsTableSize] = makePersisted(
|
||||
createSignal<TAILWINDCSS_SIZE>(TAILWINDCSS_SIZE.XS),
|
||||
{ name: 'connectionsTableSize', storage: localStorage },
|
||||
)
|
||||
export const [
|
||||
connectionsTableColumnVisibility,
|
||||
setConnectionsTableColumnVisibility,
|
||||
] = makePersisted(
|
||||
createSignal<ConnectionsTableColumnVisibility>(
|
||||
CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY,
|
||||
),
|
||||
{
|
||||
name: 'connectionsTableColumnVisibility',
|
||||
storage: localStorage,
|
||||
},
|
||||
)
|
||||
export const [connectionsTableColumnOrder, setConnectionsTableColumnOrder] =
|
||||
makePersisted(
|
||||
createSignal<ConnectionsTableColumnOrder>(
|
||||
CONNECTIONS_TABLE_INITIAL_COLUMN_ORDER,
|
||||
),
|
||||
{
|
||||
name: 'connectionsTableColumnOrder',
|
||||
storage: localStorage,
|
||||
},
|
||||
)
|
||||
export const [clientSourceIPTags, setClientSourceIPTags] = makePersisted(
|
||||
createSignal<{ tagName: string; sourceIP: string }[]>([]),
|
||||
{
|
||||
name: 'clientSourceIPTags',
|
||||
storage: localStorage,
|
||||
},
|
||||
)
|
||||
export const [logsTableSize, setLogsTableSize] = makePersisted(
|
||||
createSignal<TAILWINDCSS_SIZE>(TAILWINDCSS_SIZE.XS),
|
||||
{ name: 'logsTableSize', storage: localStorage },
|
||||
|
@ -12,11 +12,9 @@ export type WsMsg = {
|
||||
// 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
|
||||
// they closed
|
||||
export let allConnections: Connection[] = []
|
||||
|
||||
export const setAllConnections = (allConns: Connection[]) => {
|
||||
allConnections = allConns
|
||||
}
|
||||
export const [allConnections, setAllConnections] = createSignal<Connection[]>(
|
||||
[],
|
||||
)
|
||||
|
||||
export let latestConnectionMsg: Accessor<WsMsg> = () => ({
|
||||
uploadTotal: 0,
|
||||
@ -103,7 +101,7 @@ export const restructRawMsgToConnection = (
|
||||
}
|
||||
|
||||
export const mergeAllConnections = (activeConns: Connection[]) => {
|
||||
return unionWith(allConnections, activeConns, (a, b) => a.id === b.id)
|
||||
return unionWith(allConnections(), activeConns, (a, b) => a.id === b.id)
|
||||
}
|
||||
|
||||
const diffClosedConnections = (
|
||||
|
7
src/types/index.d.ts
vendored
7
src/types/index.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
import { LOG_LEVEL } from '~/constants'
|
||||
import { CONNECTIONS_TABLE_ACCESSOR_KEY, LOG_LEVEL } from '~/constants'
|
||||
|
||||
declare module 'solid-js' {
|
||||
namespace JSX {
|
||||
@ -176,3 +176,8 @@ export type BackendVersion = {
|
||||
meta: boolean
|
||||
version: string
|
||||
}
|
||||
|
||||
export type ConnectionsTableColumnVisibility = Partial<
|
||||
Record<CONNECTIONS_TABLE_ACCESSOR_KEY, boolean>
|
||||
>
|
||||
export type ConnectionsTableColumnOrder = CONNECTIONS_TABLE_ACCESSOR_KEY[]
|
||||
|
Loading…
x
Reference in New Issue
Block a user