Skip to content

Commit

Permalink
Remember last settings. v1.0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
winlinvip committed Sep 6, 2023
1 parent e550c55 commit 7dc4e00
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 63 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ curl http://localhost:2023/tc/api/v1/config/raw -X POST -d 'ls'
#{"code":100,"data":"invalid cmd ls"}
```

For TC command, see:

* [Set traffic control (tcset command)](https://tcconfig.readthedocs.io/en/latest/pages/usage/tcset/index.html)
* [Delete traffic control (tcdel command)](https://tcconfig.readthedocs.io/en/latest/pages/usage/tcdel/index.html)
* [Display traffic control configurations (tcshow command)](https://tcconfig.readthedocs.io/en/latest/pages/usage/tcshow/index.html)

## Development in macOS

Build UI:
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"strings"
)

const version = "1.0.4"
const version = "1.0.5"

func main() {
ctx := logger.WithContext(context.Background())
Expand Down
2 changes: 1 addition & 1 deletion tc.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ func (v *NetworkOptions) Execute(ctx context.Context) error {
if v.identifyKey != "all" && v.identifyValue == "" {
return errors.Errorf("no identifyValue for identifyKey=%v", v.identifyKey)
}
if v.identifyKey != "all" && v.identifyKey != "serverPort" && v.identifyKey != "clientPort" && v.identifyKey != "clientIP" {
if v.identifyKey != "all" && v.identifyKey != "serverPort" && v.identifyKey != "clientPort" && v.identifyKey != "clientIp" {
return errors.Errorf("invalid identifyKey=%v", v.identifyKey)
}
if v.strategy == "" {
Expand Down
16 changes: 5 additions & 11 deletions ui/src/components/NetFilter.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import React from "react";
import {Col, Form, InputGroup, Row} from "react-bootstrap";

export default function NetFilter({onChange, gIfaces}) {
const [iface, setIface] = React.useState();
const [protocol, setProtocol] = React.useState('ip');
const [direction, setDirection] = React.useState('incoming');
const [identifyKey, setIdentifyKey] = React.useState('all');
const [identifyValue, setIdentifyValue] = React.useState();
export default function NetFilter({gIfaces,
iface, setIface, protocol, setProtocol, direction,
setDirection, identifyKey, setIdentifyKey, identifyValue, setIdentifyValue,
}) {
const [ivVisible, setIvVisible] = React.useState(false);
const [ivLabel, setIvLabel] = React.useState('IP');

Expand All @@ -17,10 +15,6 @@ export default function NetFilter({onChange, gIfaces}) {
setIvLabel(nv === "clientIp" ? 'IP' : '端口');
}, [setIdentifyKey, setIvLabel, setIvVisible]);

React.useEffect(() => {
onChange && onChange(iface, protocol, direction, identifyKey, identifyValue);
}, [iface, protocol, direction, identifyKey, identifyValue, onChange]);

return (
<Row>
<Col xs='auto'>
Expand Down Expand Up @@ -89,7 +83,7 @@ export default function NetFilter({onChange, gIfaces}) {
<Form.Text> * 请输入{ivLabel}</Form.Text>
<InputGroup hasValidation>
<Form.Control
required type="input" placeholder={`请输入匹配的${ivLabel}`}
required type="input" placeholder={`请输入匹配的${ivLabel}`} defaultValue={identifyValue}
onChange={(e) => setIdentifyValue(e.target.value)}
/>
<Form.Control.Feedback type='invalid' tooltip>请输入{ivLabel}</Form.Control.Feedback>
Expand Down
83 changes: 35 additions & 48 deletions ui/src/pages/SingleStategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import NetFilter from "../components/NetFilter";
import {TcConfigQuery} from "../components/TcConfigQuery";
import axios from "axios";
import {useErrorHandler} from "react-error-boundary";
import {SimpleStrategyStorage} from "../utils";

export default function SingleStategy() {
const [scanPanels, setScanPanels] = React.useState([0]);
Expand Down Expand Up @@ -35,10 +36,15 @@ export default function SingleStategy() {
setScanPanels([ref.current.scanPanels.length, ...ref.current.scanPanels]);
}, [setScanPanels, ref]);

// Load filter and strategy from storage.
const defaultFilter = SimpleStrategyStorage.loadFilter() || {};
const defaultStrategy = SimpleStrategyStorage.loadStrategy() || {};
console.log(`load filter=${JSON.stringify(defaultFilter)}, strategy=${JSON.stringify(defaultStrategy)}`);

return <TcErrorBoundary>
<Container fluid={true}>
<TcErrorBoundary>
<SingleStategySetting/>
<SingleStategySetting defaultFilter={defaultFilter} defaultStrategy={defaultStrategy}/>
<p/>
{scanPanels?.length && scanPanels.map(e => {
return <React.Fragment key={e}>
Expand All @@ -56,22 +62,22 @@ export default function SingleStategy() {
</TcErrorBoundary>;
}

function SingleStategySetting() {
function SingleStategySetting({defaultFilter, defaultStrategy}) {
const [executing, setExecuting] = React.useState(false);
const [refresh, setRefresh] = React.useState(0);
const [validated, setValidated] = React.useState(false);
const handleError = useErrorHandler();

const [iface, setIface] = React.useState();
const [protocol, setProtocol] = React.useState();
const [direction, setDirection] = React.useState();
const [identifyKey, setIdentifyKey] = React.useState();
const [identifyValue, setIdentifyValue] = React.useState();
const [strategy, setStrategy] = React.useState();
const [loss, setLoss] = React.useState();
const [delay, setDelay] = React.useState();
const [rate, setRate] = React.useState();
const [delayDistro, setDelayDistro] = React.useState();
const [iface, setIface] = React.useState(defaultFilter.iface);
const [protocol, setProtocol] = React.useState(defaultFilter.protocol || 'ip');
const [direction, setDirection] = React.useState(defaultFilter.direction || 'incoming');
const [identifyKey, setIdentifyKey] = React.useState(defaultFilter.identifyKey || 'all');
const [identifyValue, setIdentifyValue] = React.useState(defaultFilter.identifyValue);
const [strategy, setStrategy] = React.useState(defaultStrategy.strategy || 'loss');
const [loss, setLoss] = React.useState(defaultStrategy.loss || '1');
const [delay, setDelay] = React.useState(defaultStrategy.delay || '1');
const [rate, setRate] = React.useState(defaultStrategy.rate || '1000000');
const [delayDistro, setDelayDistro] = React.useState(defaultStrategy.delayDistro);

const [gIfaces, setGIfaces] = React.useState();
const [ifbs, setIfbs] = React.useState();
Expand All @@ -97,28 +103,6 @@ function SingleStategySetting() {
});
}, [refresh, setIfbs, setGIfaces]);

// When user change filters.
const updateFilter = React.useCallback((iface, protocol, direction, identifyKey, identifyValue) => {
setIface(iface);
setProtocol(protocol);
setDirection(direction);
setIdentifyKey(identifyKey);
setIdentifyValue(identifyValue);
console.log(`update filter iface=${iface}, protocol=${protocol}, direction=${direction}, identify=${identifyKey}/${identifyValue}`)
}, [setIface, setProtocol, setDirection, setIdentifyKey, setIdentifyValue]);

// When user change strategy.
const updateStategy = React.useCallback((strategy, loss, delay, rate, delayDistro) => {
if (delayDistro && Number(delayDistro) > Number(delay)) return alert(`延迟抖动${delayDistro}不能大于延迟${delay}`);

setStrategy(strategy);
setLoss(loss);
setDelay(delay);
setRate(rate);
setDelayDistro(delayDistro);
console.log(`update strategy strategy=${strategy}, loss=${loss}, delay=${delay}, rate=${rate}, delayDistro=${delayDistro}`);
}, [setStrategy, setLoss, setDelay, setRate]);

// Reset the TC config.
const resetNetwork = React.useCallback((e) => {
if (!iface) {
Expand Down Expand Up @@ -148,6 +132,10 @@ function SingleStategySetting() {
return alert(`延迟抖动${delayDistro}不能大于延迟${delay}`);
}

SimpleStrategyStorage.saveFilter(iface, protocol, direction, identifyKey, identifyValue);
SimpleStrategyStorage.saveStrategy(strategy, loss, delay, rate, delayDistro);
console.log(`save iface=${iface}, protocol=${protocol}, direction=${direction}, identify=${identifyKey}/${identifyValue}, strategy=${strategy}, loss=${loss}, delay=${delay}, rate=${rate}, delayDistro=${delayDistro}`);

setExecuting(true);
const queries = [
iface ? `iface=${iface}` : null,
Expand Down Expand Up @@ -179,12 +167,19 @@ function SingleStategySetting() {
<Form noValidate validated={validated}>
<Row>
<Col xs='auto'>
<NetFilter onChange={updateFilter} gIfaces={gIfaces}/>
{gIfaces && <NetFilter gIfaces={gIfaces}
iface={iface} setIface={setIface} protocol={protocol} setProtocol={setProtocol}
direction={direction} setDirection={setDirection} identifyKey={identifyKey}
setIdentifyKey={setIdentifyKey} identifyValue={identifyValue}
setIdentifyValue={setIdentifyValue}/>}
</Col>
</Row>
<Row>
<Col xs='auto'>
<StrategySetting onChange={updateStategy}/>
<StrategySetting strategy={strategy} setStrategy={setStrategy} loss={loss}
setLoss={setLoss} delay={delay} setDelay={setDelay}
rate={rate} setRate={setRate} delayDistro={delayDistro}
setDelayDistro={setDelayDistro}/>
</Col>
</Row>
<Row>
Expand Down Expand Up @@ -217,17 +212,9 @@ function SingleStategySetting() {
</Accordion>;
}

function StrategySetting({onChange}) {
const [strategy, setStrategy] = React.useState('loss');
const [loss, setLoss] = React.useState('1');
const [delay, setDelay] = React.useState('1');
const [rate, setRate] = React.useState('1000000');
const [delayDistro, setDelayDistro] = React.useState();

React.useEffect(() => {
onChange && onChange(strategy, loss, delay, rate, delayDistro);
}, [strategy, loss, delay, rate, delayDistro, onChange]);

function StrategySetting({
strategy, setStrategy, loss, setLoss, delay, setDelay, rate, setRate, delayDistro, setDelayDistro,
}) {
return <Row>
<Col xs='auto'>
<Form.Group className="mb-3">
Expand Down Expand Up @@ -278,7 +265,7 @@ function StrategySetting({onChange}) {
<Form.Label><b>延迟抖动</b></Form.Label>
<Form.Text> * 可选, 延迟区间为[{Number(delay)-Number(delayDistro || 0)}, {Number(delay)+Number(delayDistro || 0)}]正态分布</Form.Text>
<InputGroup className="mb-3">
<Form.Control
<Form.Control defaultValue={delayDistro}
required type="input" placeholder={`请输入抖动的的值`}
onChange={(e) => setDelayDistro(e.target.value)}
/>
Expand Down
31 changes: 29 additions & 2 deletions ui/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const Utils = {
// copy({id: 0}, ['msg': 'hi'])
// Return an object:
// {id: 0, msg: 'hi'}
copy(from, extras) {
copy: (from, extras) => {
let cp = Utils.merge({}, from);

for (let i = 0; i < extras?.length; i += 2) {
Expand All @@ -19,7 +19,7 @@ export const Utils = {
return cp;
},
// Merge two object, rewrite dst by src fields.
merge(dst, src) {
merge: (dst, src) => {
if (typeof dst !== 'object') return src;
if (typeof src !== 'object') return src;

Expand All @@ -33,3 +33,30 @@ export const Utils = {
return cp;
}
};

export const SimpleStrategyStorage = {
saveFilter: (iface, protocol, direction, identifyKey, identifyValue) => {
localStorage.setItem('TC_UI_SIMPLE_STRATEGY_FILTER', JSON.stringify({
iface, protocol, direction, identifyKey, identifyValue,
}));
},
loadFilter: () => {
const info = localStorage.getItem('TC_UI_SIMPLE_STRATEGY_FILTER');
return info ? JSON.parse(info) : null;
},
clearFilter: () => {
localStorage.removeItem('TC_UI_SIMPLE_STRATEGY_FILTER');
},
saveStrategy: (strategy, loss, delay, rate, delayDistro) => {
localStorage.setItem('TC_UI_SIMPLE_STRATEGY_STRATEGY', JSON.stringify({
strategy, loss, delay, rate, delayDistro,
}));
},
loadStrategy: () => {
const info = localStorage.getItem('TC_UI_SIMPLE_STRATEGY_STRATEGY');
return info ? JSON.parse(info) : null;
},
clearStrategy: () => {
localStorage.removeItem('TC_UI_SIMPLE_STRATEGY_STRATEGY');
}
};

0 comments on commit 7dc4e00

Please sign in to comment.