Feature: show process name and path in connections page (#95)

Co-authored-by: MetaCubeX <maze.y2b@github.com>
This commit is contained in:
Meta 2022-04-22 18:28:22 +08:00 committed by GitHub
parent 542aa21330
commit 5ba5058c70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 51 additions and 20 deletions

View File

@ -1,7 +1,7 @@
import classnames from 'classnames' import classnames from 'classnames'
import { useMemo } from 'react' import { useMemo } from 'react'
import { formatTraffic } from '@lib/helper' import { basePath, formatTraffic } from '@lib/helper'
import { BaseComponentProps } from '@models' import { BaseComponentProps } from '@models'
import { useI18n } from '@stores' import { useI18n } from '@stores'
@ -18,21 +18,21 @@ export function ConnectionInfo (props: ConnectionsInfoProps) {
return ( return (
<div className={classnames(props.className, 'text-sm flex flex-col')}> <div className={classnames(props.className, 'text-sm flex flex-col')}>
<div className="flex my-3"> <div className="flex my-3">
<span className="font-bold w-16">{t('info.id')}</span> <span className="font-bold w-20">{t('info.id')}</span>
<span className="font-mono">{props.connection.id}</span> <span className="font-mono">{props.connection.id}</span>
</div> </div>
<div className="flex my-3 justify-between"> <div className="flex my-3 justify-between">
<div className="flex flex-1"> <div className="flex flex-1">
<span className="font-bold w-16">{t('info.network')}</span> <span className="font-bold w-20">{t('info.network')}</span>
<span className="font-mono">{props.connection.metadata?.network}</span> <span className="font-mono">{props.connection.metadata?.network}</span>
</div> </div>
<div className="flex flex-1"> <div className="flex flex-1">
<span className="font-bold w-16">{t('info.inbound')}</span> <span className="font-bold w-20">{t('info.inbound')}</span>
<span className="font-mono">{props.connection.metadata?.type}</span> <span className="font-mono">{props.connection.metadata?.type}</span>
</div> </div>
</div> </div>
<div className="flex my-3"> <div className="flex my-3">
<span className="font-bold w-16">{t('info.host')}</span> <span className="font-bold w-20">{t('info.host')}</span>
<span className="font-mono flex-1 break-all">{ <span className="font-mono flex-1 break-all">{
props.connection.metadata?.host props.connection.metadata?.host
? `${props.connection.metadata.host}:${props.connection.metadata?.destinationPort}` ? `${props.connection.metadata.host}:${props.connection.metadata?.destinationPort}`
@ -40,7 +40,7 @@ export function ConnectionInfo (props: ConnectionsInfoProps) {
}</span> }</span>
</div> </div>
<div className="flex my-3"> <div className="flex my-3">
<span className="font-bold w-16">{t('info.dstIP')}</span> <span className="font-bold w-20">{t('info.dstIP')}</span>
<span className="font-mono">{ <span className="font-mono">{
props.connection.metadata?.destinationIP props.connection.metadata?.destinationIP
? `${props.connection.metadata.destinationIP}:${props.connection.metadata?.destinationPort}` ? `${props.connection.metadata.destinationIP}:${props.connection.metadata?.destinationPort}`
@ -48,35 +48,51 @@ export function ConnectionInfo (props: ConnectionsInfoProps) {
}</span> }</span>
</div> </div>
<div className="flex my-3"> <div className="flex my-3">
<span className="font-bold w-16">{t('info.srcIP')}</span> <span className="font-bold w-20">{t('info.srcIP')}</span>
<span className="font-mono">{ <span className="font-mono">{
`${props.connection.metadata?.sourceIP}:${props.connection.metadata?.sourcePort}` `${props.connection.metadata?.sourceIP}:${props.connection.metadata?.sourcePort}`
}</span> }</span>
</div> </div>
<div className="flex my-3"> <div className="flex my-3">
<span className="font-bold w-16">{t('info.rule')}</span> <span className="font-bold w-20">{t('info.process')}</span>
<span className="font-mono">{
props.connection.metadata?.processPath
? `${basePath(props.connection.metadata.processPath)}`
: t('info.hostEmpty')
}</span>
</div>
<div className="flex my-3">
<span className="font-bold w-20">{t('info.processPath')}</span>
<span className="font-mono flex-1 break-all">{
props.connection.metadata?.processPath
? `${props.connection.metadata.processPath}`
: t('info.hostEmpty')
}</span>
</div>
<div className="flex my-3">
<span className="font-bold w-20">{t('info.rule')}</span>
<span className="font-mono"> <span className="font-mono">
{ props.connection.rule && `${props.connection.rule}${props.connection.rulePayload && `(${props.connection.rulePayload})`}` } { props.connection.rule && `${props.connection.rule}${props.connection.rulePayload && ` :: ${props.connection.rulePayload}`}` }
</span> </span>
</div> </div>
<div className="flex my-3"> <div className="flex my-3">
<span className="font-bold w-16">{t('info.chains')}</span> <span className="font-bold w-20">{t('info.chains')}</span>
<span className="font-mono flex-1 break-all"> <span className="font-mono flex-1 break-all">
{ props.connection.chains?.slice().reverse().join(' / ') } { props.connection.chains?.slice().reverse().join(' / ') }
</span> </span>
</div> </div>
<div className="flex my-3 justify-between"> <div className="flex my-3 justify-between">
<div className="flex flex-1"> <div className="flex flex-1">
<span className="font-bold w-16">{t('info.upload')}</span> <span className="font-bold w-20">{t('info.upload')}</span>
<span className="font-mono">{formatTraffic(props.connection.upload ?? 0)}</span> <span className="font-mono">{formatTraffic(props.connection.upload ?? 0)}</span>
</div> </div>
<div className="flex flex-1"> <div className="flex flex-1">
<span className="font-bold w-16">{t('info.download')}</span> <span className="font-bold w-20">{t('info.download')}</span>
<span className="font-mono">{formatTraffic(props.connection.download ?? 0)}</span> <span className="font-mono">{formatTraffic(props.connection.download ?? 0)}</span>
</div> </div>
</div> </div>
<div className="flex my-3"> <div className="flex my-3">
<span className="font-bold w-16">{t('info.status')}</span> <span className="font-bold w-20">{t('info.status')}</span>
<span className="font-mono">{ <span className="font-mono">{
!props.connection.completed !props.connection.completed
? <span className="text-green">{t('info.opening')}</span> ? <span className="text-green">{t('info.opening')}</span>

View File

@ -7,10 +7,10 @@ import { useMemo, useLayoutEffect, useRef, useState, useEffect } from 'react'
import { Header, Checkbox, Modal, Icon, Drawer, Card, Button } from '@components' import { Header, Checkbox, Modal, Icon, Drawer, Card, Button } from '@components'
import { fromNow } from '@lib/date' import { fromNow } from '@lib/date'
import { formatTraffic } from '@lib/helper' import { basePath, formatTraffic } from '@lib/helper'
import { useObject, useVisible } from '@lib/hook' import { useObject, useVisible } from '@lib/hook'
import * as API from '@lib/request' import * as API from '@lib/request'
import { BaseComponentProps, RuleType } from '@models' import { BaseComponentProps } from '@models'
import { useClient, useConnectionStreamReader, useI18n } from '@stores' import { useClient, useConnectionStreamReader, useI18n } from '@stores'
import { Devices } from './Devices' import { Devices } from './Devices'
@ -21,6 +21,7 @@ import './style.scss'
enum Columns { enum Columns {
Host = 'host', Host = 'host',
Network = 'network', Network = 'network',
Process = 'process',
Type = 'type', Type = 'type',
Chains = 'chains', Chains = 'chains',
Rule = 'rule', Rule = 'rule',
@ -31,7 +32,7 @@ enum Columns {
Time = 'time', Time = 'time',
} }
const shouldCenter = new Set<string>([Columns.Network, Columns.Type, Columns.Rule, Columns.Speed, Columns.Upload, Columns.Download, Columns.SourceIP, Columns.Time]) const shouldCenter = new Set<string>([Columns.Network, Columns.Type, Columns.Speed, Columns.Upload, Columns.Download, Columns.SourceIP, Columns.Time, Columns.Process])
function formatSpeed (upload: number, download: number) { function formatSpeed (upload: number, download: number) {
switch (true) { switch (true) {
@ -74,13 +75,14 @@ export default function Connections () {
id: c.id, id: c.id,
host: `${c.metadata.host || c.metadata.destinationIP}:${c.metadata.destinationPort}`, host: `${c.metadata.host || c.metadata.destinationIP}:${c.metadata.destinationPort}`,
chains: c.chains.slice().reverse().join(' / '), chains: c.chains.slice().reverse().join(' / '),
rule: c.rule === RuleType.RuleSet ? `${c.rule}(${c.rulePayload})` : c.rule, rule: c.rulePayload ? `${c.rule} :: ${c.rulePayload}` : c.rule,
time: new Date(c.start).getTime(), time: new Date(c.start).getTime(),
upload: c.upload, upload: c.upload,
download: c.download, download: c.download,
sourceIP: c.metadata.sourceIP, sourceIP: c.metadata.sourceIP,
type: c.metadata.type, type: c.metadata.type,
network: c.metadata.network.toUpperCase(), network: c.metadata.network.toUpperCase(),
process: c.metadata.processPath,
speed: { upload: c.uploadSpeed, download: c.downloadSpeed }, speed: { upload: c.uploadSpeed, download: c.downloadSpeed },
completed: !!c.completed, completed: !!c.completed,
original: c, original: c,
@ -100,9 +102,10 @@ export default function Connections () {
() => table.createColumns([ () => table.createColumns([
table.createDataColumn(Columns.Host, { minWidth: 260, width: 260, header: t(`columns.${Columns.Host}`) }), table.createDataColumn(Columns.Host, { minWidth: 260, width: 260, header: t(`columns.${Columns.Host}`) }),
table.createDataColumn(Columns.Network, { minWidth: 80, width: 80, header: t(`columns.${Columns.Network}`) }), table.createDataColumn(Columns.Network, { minWidth: 80, width: 80, header: t(`columns.${Columns.Network}`) }),
table.createDataColumn(Columns.Type, { minWidth: 120, width: 120, header: t(`columns.${Columns.Type}`) }), table.createDataColumn(Columns.Type, { minWidth: 100, width: 100, header: t(`columns.${Columns.Type}`) }),
table.createDataColumn(Columns.Chains, { minWidth: 200, width: 200, header: t(`columns.${Columns.Chains}`) }), table.createDataColumn(Columns.Chains, { minWidth: 200, width: 200, header: t(`columns.${Columns.Chains}`) }),
table.createDataColumn(Columns.Rule, { minWidth: 140, width: 140, header: t(`columns.${Columns.Rule}`) }), table.createDataColumn(Columns.Rule, { minWidth: 140, width: 140, header: t(`columns.${Columns.Rule}`) }),
table.createDataColumn(Columns.Process, { minWidth: 100, width: 100, header: t(`columns.${Columns.Process}`), cell: cell => cell.value ? basePath(cell.value) : '-' }),
table.createDataColumn( table.createDataColumn(
row => [row.speed.upload, row.speed.download], row => [row.speed.upload, row.speed.download],
{ {
@ -163,7 +166,7 @@ export default function Connections () {
sortRowsFn, sortRowsFn,
columnFilterRowsFn, columnFilterRowsFn,
initialState: { initialState: {
sorting: [{ id: Columns.Time, desc: true }], sorting: [{ id: Columns.Time, desc: false }],
}, },
columnResizeMode: 'onChange', columnResizeMode: 'onChange',
enableColumnResizing: true, enableColumnResizing: true,

View File

@ -15,6 +15,7 @@ export interface FormatConnection {
download: number download: number
type: string type: string
network: string network: string
process?: string
sourceIP: string sourceIP: string
speed: { speed: {
upload: number upload: number

View File

@ -68,6 +68,7 @@ const EN = {
network: 'Network', network: 'Network',
type: 'Type', type: 'Type',
chains: 'Chains', chains: 'Chains',
process: 'Process',
rule: 'Rule', rule: 'Rule',
time: 'Time', time: 'Time',
speed: 'Speed', speed: 'Speed',
@ -86,6 +87,8 @@ const EN = {
upload: 'Upload', upload: 'Upload',
download: 'Download', download: 'Download',
network: 'Network', network: 'Network',
process: 'Process',
processPath: 'Path',
inbound: 'Inbound', inbound: 'Inbound',
rule: 'Rule', rule: 'Rule',
chains: 'Chains', chains: 'Chains',

View File

@ -66,6 +66,7 @@ const CN = {
columns: { columns: {
host: '域名', host: '域名',
network: '网络', network: '网络',
process: '进程',
type: '类型', type: '类型',
chains: '节点链', chains: '节点链',
rule: '规则', rule: '规则',
@ -86,6 +87,8 @@ const CN = {
upload: '上传', upload: '上传',
download: '下载', download: '下载',
network: '网络', network: '网络',
process: '进程',
processPath: '路径',
inbound: '入口', inbound: '入口',
rule: '规则', rule: '规则',
chains: '代理', chains: '代理',

View File

@ -17,3 +17,7 @@ export function formatTraffic (num: number) {
const exp = Math.floor(Math.log(num || 1) / Math.log(1024)) const exp = Math.floor(Math.log(num || 1) / Math.log(1024))
return `${floor(num / Math.pow(1024, exp), 2).toFixed(2)} ${s?.[exp] ?? ''}` return `${floor(num / Math.pow(1024, exp), 2).toFixed(2)} ${s?.[exp] ?? ''}`
} }
export function basePath (path: string) {
return path.replace(/.*[/\\]/, '')
}

View File

@ -56,7 +56,7 @@ interface History {
export interface Proxy { export interface Proxy {
name: string name: string
type: 'Direct' | 'Reject' | 'Shadowsocks' | 'Vmess' | 'Socks' | 'Http' | 'Snell' type: 'Direct' | 'Reject' | 'Shadowsocks' | 'Vmess' | 'Trojan' | 'Socks' | 'Http' | 'Snell'
history: History[] history: History[]
} }
@ -80,6 +80,7 @@ export interface Connections {
network: string network: string
type: string type: string
host: string host: string
processPath?: string
sourceIP: string sourceIP: string
sourcePort: string sourcePort: string
destinationPort: string destinationPort: string