summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorHaishan <[email protected]>2019-11-08 00:40:48 +0800
committerHaishan <[email protected]>2019-11-09 13:21:25 +0800
commit6754620d7abfb7273cff5f173349eb507fc0c8b7 (patch)
treed91136e4863b4c41ff521bf182dd8cc08bb355bc /src/components
parent130793446476e47b2e5f0457482dfbfdf1944b48 (diff)
feat: connections inspection
Diffstat (limited to 'src/components')
-rw-r--r--src/components/ConnectionTable.js99
-rw-r--r--src/components/ConnectionTable.module.css38
-rw-r--r--src/components/Connections.js55
-rw-r--r--src/components/Root.css5
-rw-r--r--src/components/Root.js2
-rw-r--r--src/components/SideBar.js25
-rw-r--r--src/components/SvgActivity.js24
-rw-r--r--src/components/SvgCommand.js25
-rw-r--r--src/components/SvgFile.js25
-rw-r--r--src/components/SvgGlobe.js25
-rw-r--r--src/components/SvgSettings.js25
11 files changed, 213 insertions, 135 deletions
diff --git a/src/components/ConnectionTable.js b/src/components/ConnectionTable.js
new file mode 100644
index 0000000..547925c
--- /dev/null
+++ b/src/components/ConnectionTable.js
@@ -0,0 +1,99 @@
+import React from 'react';
+import { ArrowUp, ArrowDown } from 'react-feather';
+import prettyBytes from '../misc/pretty-bytes';
+import { formatDistance } from 'date-fns';
+import cx from 'classnames';
+import { useTable, useSortBy } from 'react-table';
+
+import s from './ConnectionTable.module.css';
+
+const columns = [
+ { Header: 'Host', accessor: 'host' },
+ { Header: 'Download', accessor: 'download' },
+ { Header: 'Upload', accessor: 'upload' },
+ { Header: 'Network', accessor: 'network' },
+ { Header: 'Type', accessor: 'type' },
+ { Header: 'Chains', accessor: 'chains' },
+ { Header: 'Rule', accessor: 'rule' },
+ { Header: 'Time', accessor: 'start' },
+ { Header: 'Source IP', accessor: 'sourceIP' },
+ { Header: 'Source Port', accessor: 'sourcePort' },
+ { Header: 'Designation IP', accessor: 'destinationIP' },
+ { Header: 'Designation Port', accessor: 'destinationPort' }
+];
+
+function renderCell(cell, now) {
+ switch (cell.column.id) {
+ case 'start':
+ return formatDistance(-cell.value, now);
+ case 'download':
+ case 'upload':
+ return prettyBytes(cell.value);
+ default:
+ return cell.value;
+ }
+}
+
+function Table({ data }) {
+ const now = new Date();
+ const {
+ getTableProps,
+ // getTableBodyProps,
+ headerGroups,
+ rows,
+ prepareRow
+ } = useTable(
+ {
+ columns,
+ data
+ },
+ useSortBy
+ );
+ return (
+ <div {...getTableProps()}>
+ <div className={s.thead}>
+ {headerGroups.map(headerGroup => (
+ <div {...headerGroup.getHeaderGroupProps()} className={s.tr}>
+ {headerGroup.headers.map(column => (
+ <div
+ {...column.getHeaderProps(column.getSortByToggleProps())}
+ className={s.th}
+ >
+ <span>{column.render('Header')}</span>
+ <span>
+ {column.isSorted ? (
+ column.isSortedDesc ? (
+ <ArrowDown size={16} />
+ ) : (
+ <ArrowUp size={16} />
+ )
+ ) : null}
+ </span>
+ </div>
+ ))}
+
+ {rows.map((row, i) => {
+ prepareRow(row);
+ return row.cells.map((cell, j) => {
+ return (
+ <div
+ {...cell.getCellProps()}
+ className={cx(
+ s.td,
+ i % 2 === 0 ? s.odd : false,
+ j === 1 || j === 2 ? s.du : false
+ )}
+ >
+ {renderCell(cell, now)}
+ </div>
+ );
+ });
+ })}
+ </div>
+ ))}
+ </div>
+ </div>
+ );
+}
+
+export default Table;
diff --git a/src/components/ConnectionTable.module.css b/src/components/ConnectionTable.module.css
new file mode 100644
index 0000000..f145542
--- /dev/null
+++ b/src/components/ConnectionTable.module.css
@@ -0,0 +1,38 @@
+.thead .tr {
+ display: grid;
+ grid-template-columns: repeat(12, max-content);
+}
+
+.th {
+ padding: 8px 0 14px 10px;
+ height: 50px;
+ background: var(--color-background);
+ position: sticky;
+ top: 0;
+
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.th span:last-child {
+ margin-left: 10px;
+ width: 16px;
+ height: 16px;
+}
+
+.td {
+ padding: 8px 10px;
+ font-size: 0.9em;
+
+ font-family: var(--font-normal);
+}
+
+.td.odd {
+ background: var(--color-row-odd);
+}
+
+/* download upload td cells */
+.du {
+ text-align: right;
+}
diff --git a/src/components/Connections.js b/src/components/Connections.js
new file mode 100644
index 0000000..66ce2ef
--- /dev/null
+++ b/src/components/Connections.js
@@ -0,0 +1,55 @@
+import React from 'react';
+import ContentHeader from 'c/ContentHeader';
+import ConnectionTable from 'c/ConnectionTable';
+import useRemainingViewPortHeight from '../hooks/useRemainingViewPortHeight';
+import { useStoreState } from 'm/store';
+import { getClashAPIConfig } from 'd/app';
+import * as connAPI from '../api/connections';
+
+const { useEffect, useState } = React;
+
+const paddingBottom = 30;
+
+function formatConnectionDataItem(i) {
+ const { id, metadata, upload, download, start, chains, rule } = i;
+ // const started = formatDistance(new Date(start), now);
+ return {
+ id,
+ upload,
+ download,
+ start: 0 - new Date(start),
+ chains: chains.reverse().join(' / '),
+ rule,
+ ...metadata
+ };
+}
+
+function Conn() {
+ const [refContainer, containerHeight] = useRemainingViewPortHeight();
+ const config = useStoreState(getClashAPIConfig);
+ const [conns, setConns] = useState([]);
+ useEffect(() => {
+ function read({ connections }) {
+ const x = connections.map(c => formatConnectionDataItem(c));
+ setConns(x);
+ }
+ return connAPI.fetchData(config, read);
+ }, [config]);
+ return (
+ <div>
+ <ContentHeader title="Connections" />
+ <div
+ ref={refContainer}
+ style={{ padding: 30, paddingBottom, paddingTop: 0 }}
+ >
+ <div
+ style={{ height: containerHeight - paddingBottom, overflow: 'auto' }}
+ >
+ <ConnectionTable data={conns} />
+ </div>
+ </div>
+ </div>
+ );
+}
+
+export default Conn;
diff --git a/src/components/Root.css b/src/components/Root.css
index c08c538..88b3d6c 100644
--- a/src/components/Root.css
+++ b/src/components/Root.css
@@ -61,6 +61,9 @@
:root {
--font-mono: 'Roboto Mono', Menlo, monospace;
+ --font-normal: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica,
+ Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol,
+ 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
--color-focus-blue: #1a73e8;
}
@@ -88,6 +91,7 @@ body.dark {
--color-btn-bg: #232323;
--color-btn-fg: #bebebe;
--color-bg-proxy-selected: #303030;
+ --color-row-odd: #282828;
}
body.light {
@@ -104,4 +108,5 @@ body.light {
--color-btn-bg: #f4f4f4;
--color-btn-fg: #101010;
--color-bg-proxy-selected: #cfcfcf;
+ --color-row-odd: #f5f5f5;
}
diff --git a/src/components/Root.js b/src/components/Root.js
index e7f0e60..56c0be0 100644
--- a/src/components/Root.js
+++ b/src/components/Root.js
@@ -8,6 +8,7 @@ import SideBar from 'c/SideBar';
import Home from 'c/Home';
import Logs from 'c/Logs';
import Config from 'c/Config';
+import Connections from 'c/Connections';
import APIDiscovery from 'c/APIDiscovery';
import { store } from '../store/configureStore';
import './Root.css';
@@ -45,6 +46,7 @@ const Root = () => (
<div className={s0.content}>
<Suspense fallback={<Loading2 />}>
<Route exact path="/" render={() => <Home />} />
+ <Route exact path="/connections" component={Connections} />
<Route exact path="/overview" render={() => <Home />} />
<Route exact path="/configs" component={Config} />
<Route exact path="/logs" component={Logs} />
diff --git a/src/components/SideBar.js b/src/components/SideBar.js
index c78d6a9..09b517e 100644
--- a/src/components/SideBar.js
+++ b/src/components/SideBar.js
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import { Link } from 'react-router-dom';
+import { Command, Activity, Globe, Link2, Settings, File } from 'react-feather';
import { useActions } from 'm/store';
import { switchTheme } from 'd/app';
@@ -11,20 +12,16 @@ import Icon from 'c/Icon';
import moon from 's/moon.svg';
import SvgYacd from './SvgYacd';
-import SvgActivity from './SvgActivity';
-import SvgGlobe from './SvgGlobe';
-import SvgCommand from './SvgCommand';
-import SvgSettings from './SvgSettings';
-import SvgFile from './SvgFile';
import s from 'c/SideBar.module.css';
const icons = {
- activity: SvgActivity,
- globe: SvgGlobe,
- command: SvgCommand,
- file: SvgFile,
- settings: SvgSettings
+ activity: Activity,
+ globe: Globe,
+ command: Command,
+ file: File,
+ settings: Settings,
+ link: Link2
};
const SideBarRow = React.memo(function SideBarRow({
@@ -38,7 +35,7 @@ const SideBarRow = React.memo(function SideBarRow({
const className = cx(s.row, isActive ? s.rowActive : null);
return (
<Link to={to} className={className}>
- <Comp isActive={isActive} />
+ <Comp />
<div className={s.label}>{labelText}</div>
</Link>
);
@@ -90,6 +87,12 @@ function SideBar({ location }) {
labelText="Rules"
/>
<SideBarRow
+ to="/connections"
+ location={location}
+ iconId="link"
+ labelText="Conns"
+ />
+ <SideBarRow
to="/configs"
location={location}
iconId="settings"
diff --git a/src/components/SvgActivity.js b/src/components/SvgActivity.js
deleted file mode 100644
index 38946b1..0000000
--- a/src/components/SvgActivity.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-
-function SvgActivity() {
- return (
- <svg
- width="28"
- height="28"
- viewBox="0 0 28 28"
- xmlns="http://www.w3.org/2000/svg"
- >
- <g
- fill="none"
- fillRule="evenodd"
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- >
- <path d="M24 14h-4l-3 9-6-18-3 9H4" stroke="currentColor" />
- </g>
- </svg>
- );
-}
-
-export default SvgActivity;
diff --git a/src/components/SvgCommand.js b/src/components/SvgCommand.js
deleted file mode 100644
index 58fb607..0000000
--- a/src/components/SvgCommand.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-
-export default function SvgCommand() {
- return (
- <svg
- width="28"
- height="28"
- viewBox="0 0 28 28"
- xmlns="http://www.w3.org/2000/svg"
- >
- <g
- fill="none"
- fillRule="evenodd"
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- >
- <path
- d="M20 5a3 3 0 0 0-3 3v12a3 3 0 1 0 3-3H8a3 3 0 1 0 3 3V8a3 3 0 1 0-3 3h12a3 3 0 0 0 0-6z"
- stroke="currentColor"
- />
- </g>
- </svg>
- );
-}
diff --git a/src/components/SvgFile.js b/src/components/SvgFile.js
deleted file mode 100644
index 503f7b3..0000000
--- a/src/components/SvgFile.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-
-export default function SvgFile() {
- return (
- <svg
- width="28"
- height="28"
- viewBox="0 0 28 28"
- xmlns="http://www.w3.org/2000/svg"
- >
- <g
- fill="none"
- fillRule="evenodd"
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- >
- <g stroke="currentColor">
- <path d="M16 4H8a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10l-6-6z" />
- <path d="M16 4v6h6M18 15h-8M18 19h-8M12 11h-2" />
- </g>
- </g>
- </svg>
- );
-}
diff --git a/src/components/SvgGlobe.js b/src/components/SvgGlobe.js
deleted file mode 100644
index d7c66da..0000000
--- a/src/components/SvgGlobe.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-
-export default function SvgGlobe() {
- return (
- <svg
- width="28"
- height="28"
- viewBox="0 0 28 28"
- xmlns="http://www.w3.org/2000/svg"
- >
- <g
- fill="none"
- fillRule="evenodd"
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- >
- <g transform="translate(4 4)" stroke="currentColor">
- <circle cx="10" cy="10" r="10" />
- <path d="M0 10h20M10 0a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
- </g>
- </g>
- </svg>
- );
-}
diff --git a/src/components/SvgSettings.js b/src/components/SvgSettings.js
deleted file mode 100644
index 1d7af11..0000000
--- a/src/components/SvgSettings.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-
-export default function SvgSettings() {
- return (
- <svg
- width="28"
- height="28"
- viewBox="0 0 28 28"
- xmlns="http://www.w3.org/2000/svg"
- >
- <g
- fill="none"
- fillRule="evenodd"
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- >
- <g transform="translate(3 3)" stroke="currentColor">
- <circle cx="11" cy="11" r="3" />
- <path d="M18.4 14a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V20a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 8 18.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H2a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 3.6 8a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H8a1.65 1.65 0 0 0 1-1.51V2a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V8c.26.604.852.997 1.51 1H20a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
- </g>
- </g>
- </svg>
- );
-}