Skip to content

Commit d93f513

Browse files
committedSep 16, 2021
feat: add task management
1 parent b70e899 commit d93f513

15 files changed

+581
-4743
lines changed
 

‎assets/yprocmon.png

4.18 KB
Loading

‎src/yhttp.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ void start_http_server()
3131
req.get_header_value("User-Agent").c_str(),
3232
res.status);
3333
});
34+
auto ret = svr.set_mount_point("/", "./www");
3435
svr.Get("/api/ping", [](const httplib::Request &, httplib::Response &res)
3536
{ res.set_content("{\"ping\": true}", "application/json"); });
3637
svr.Get("/api/status",
@@ -47,7 +48,7 @@ void start_http_server()
4748
writer.StartArray();
4849
for(auto const& e : state.instances)
4950
{
50-
e.Serialize(writer);
51+
e.second.Serialize(writer);
5152
}
5253
writer.EndArray();
5354
res.set_content(s.GetString(), "application/json");
@@ -144,7 +145,7 @@ void start_http_server()
144145
e.Serialize(writer);
145146
res.set_content(s.GetString(), "application/json");
146147
std::lock_guard<std::mutex> guard(state.instances_mutex);
147-
state.instances.push_back(std::move(e));
148+
state.instances[e.pi.dwProcessId] = (std::move(e));
148149
}
149150
else
150151
{

‎src/yprocmon.cc

+8
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ int _detour_program(const PROCESS_INFORMATION pi)
6060
console_print("[PROC] GetExitCodeProcess failed: %ld\n",
6161
GetLastError());
6262
}
63+
std::lock_guard<std::mutex> guard(state.instances_mutex);
64+
if (state.instances.count(pi.dwProcessId) != 0)
65+
{
66+
state.instances[pi.dwProcessId].status = INSTANCE_EXITED;
67+
state.instances[pi.dwProcessId].exitCode = dwResult;
68+
state.instances[pi.dwProcessId].waiting.detach();
69+
}
6370
return 0;
6471
}
6572

@@ -107,6 +114,7 @@ bool detour_program(const detour_startup& startup, instance_entry& ret)
107114
ret.pi.dwThreadId = pi.dwThreadId;
108115
ret.pi.hProcess = pi.hProcess;
109116
ret.pi.hThread = pi.hThread;
117+
ret.status = INSTANCE_SPAWNED;
110118
ret.name = std::string(startup.name);
111119
ret.timestamp = time(nullptr) * 1000;
112120
return true;

‎src/yprocmon.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,5 @@ struct yprocmon_state
8888
std::mutex operations_mutex;
8989
std::mutex instances_mutex;
9090
std::vector<yhook_message_entry> operations;
91-
std::vector<instance_entry> instances;
91+
std::map<DWORD, instance_entry> instances;
9292
} state;

‎yprocmon-front/package-lock.json

+67-4,585
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎yprocmon-front/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"react-router-dom": "^5.3.0",
1919
"react-scripts": "4.0.3",
2020
"react-transition-group": "^4.4.2",
21+
"react-virtualized": "^9.22.3",
2122
"web-vitals": "^1.1.2"
2223
},
2324
"scripts": {

‎yprocmon-front/src/App.js

+61-62
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,98 @@
1-
import logo from './yprocmon.png'
2-
import './App.css';
1+
import logo from "./yprocmon.png";
2+
import "./App.css";
33
import {
44
Badge,
55
Container,
66
Row,
77
Col,
88
OverlayTrigger,
99
Tab,
10-
Tooltip
11-
} from 'react-bootstrap'
10+
Tooltip,
11+
} from "react-bootstrap";
1212
import {
1313
BrowserRouter as Router,
1414
Switch,
1515
Route,
1616
Link,
1717
useRouteMatch,
18-
useParams
19-
} from 'react-router-dom';
18+
useParams,
19+
} from "react-router-dom";
2020

21-
import NavBar from './components/NavBar'
22-
import About from './pages/About';
23-
import Rules from './pages/Rules';
24-
import Monitor from './pages/Monitor';
21+
import NavBar from "./components/NavBar";
22+
import About from "./pages/About";
23+
import Rules from "./pages/Rules";
24+
import Monitor from "./pages/Monitor";
2525

26-
import { AppProvider } from './AppContext'
27-
import { useReducer } from 'react';
28-
import Footer from './components/Footer';
26+
import { AppProvider, initalState } from "./AppContext";
27+
import { useReducer } from "react";
28+
import Footer from "./components/Footer";
2929

3030
function App() {
3131
const [state, dispatch] = useReducer((state, action) => {
32-
switch(action.type) {
33-
case 'OPERATIONS_APPEND':
32+
switch (action.type) {
33+
case "OPERATIONS_APPEND":
3434
if (!action.payload || action.payload.length == 0) return state;
3535
const newOperations = action.payload.map((operation) => {
36-
const { type, data } = operation
36+
const { type, data } = operation;
3737
let display = {
3838
type,
39-
summary: ''
40-
}
41-
let summary
42-
if (type == 'SPAWN') {
43-
display.type = (<Badge bg="dark">{type}</Badge>);
44-
display.summary = (
45-
<>
46-
<Badge bg="dark">process: {data.process}</Badge>{' '}
47-
<Badge bg="dark">pid: {data.pid}</Badge>
48-
</>
49-
)
50-
} else if (type == 'HOOK') {
51-
display.type = (<Badge bg="primary">{data.name}</Badge>);
52-
display.summary = data.args.map(arg => [
53-
<OverlayTrigger
54-
placement="bottom"
55-
delay={{ show: 250, hide: 400 }}
56-
overlay={(props) => <Tooltip {...props}>{arg.value}</Tooltip>}
57-
>
58-
<Badge bg="light" text="dark" key={arg.name}>
59-
<span>{arg.name}:</span>
60-
<Badge pill bg="info">{arg.value.length > 37 ? arg.value.substr(0, 37) + '...' : arg.value}</Badge>
61-
</Badge>
62-
</OverlayTrigger>,
63-
' '
64-
])
39+
summary: "",
40+
};
41+
let summary;
42+
if (type == "SPAWN") {
43+
display.type = 'spawn'
44+
display.summary = [
45+
{
46+
key: 'process',
47+
value: data.process
48+
},
49+
{
50+
key: 'pid',
51+
value: data.pid
52+
}
53+
];
54+
} else if (type == "HOOK") {
55+
display.type = data.name;
56+
display.summary = data.args.map((arg) => ({
57+
key: arg.name,
58+
value:
59+
arg.value.length > 37
60+
? arg.value.substr(0, 37) + "..."
61+
: arg.value,
62+
tooltip: arg.value
63+
}));
6564
}
6665
return {
6766
...operation,
68-
display
69-
}
70-
})
67+
display,
68+
};
69+
});
70+
return {
71+
...state,
72+
operations: state.operations.concat(newOperations),
73+
};
74+
break;
75+
case "OPERATIONS_CLEAR":
7176
return {
7277
...state,
73-
operations: state.operations.concat(newOperations)
74-
}
78+
operationClearAt: new Date().getTime(),
79+
operations: [],
80+
};
7581
break;
76-
case 'OPERATIONS_CLEAR':
82+
case "INSTANCES_UPDATE":
7783
return {
7884
...state,
79-
operationClearAt: (new Date).getTime(),
80-
operations: []
81-
}
85+
instances: action.payload,
86+
};
8287
break;
83-
case 'FILES_UPDATE':
88+
case "FILES_UPDATE":
8489
return {
8590
...state,
86-
files: action.payload
87-
}
91+
files: action.payload,
92+
};
8893
break;
8994
}
90-
}, {
91-
operations: [],
92-
rules: [],
93-
files: [],
94-
instances: [],
95-
operationClearAt: 0
96-
})
95+
}, initalState);
9796
// const operationTimer = setInterval(async () => {
9897
// let newOperations;
9998
// if (operations.length != 0) {
@@ -106,7 +105,7 @@ function App() {
106105
// }, 500);
107106
return (
108107
<Router>
109-
<AppProvider value={{state, dispatch}}>
108+
<AppProvider value={{ state, dispatch }}>
110109
<div className="app">
111110
<header className="app-header">
112111
<NavBar logo={logo} title="yprocmon" version="v1.0.0" />

‎yprocmon-front/src/AppContext.js

+30-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,43 @@
11
import React, { Context } from "react";
2+
import { Badge } from "react-bootstrap";
23

34
const context = React.createContext();
45

56
export default context;
67
export const AppProvider = context.Provider;
8+
export const initalState = {
9+
operations: [],
10+
rules: [],
11+
files: [],
12+
instances: [],
13+
// typemaps: {
14+
// MessageBox: <Badge variant="info">MessageBox</Badge>,
15+
// CreateFile: <Badge variant="">CreateFile</Badge>,
16+
// WriteFile: <Badge variant="">WriteFile</Badge>,
17+
// ReadFile: <Badge variant="">ReadFile</Badge>,
18+
// HeapCreate: <Badge variant="">HeapCreate</Badge>,
19+
// HeapDestroy: <Badge variant="">HeapDestroy</Badge>,
20+
// HeapFree: <Badge variant="">HeapFree</Badge>,
21+
// RegCreateKeyEx: <Badge variant="">RegCreateKeyEx</Badge>,
22+
// RegSetValueEx: <Badge variant="">RegSetValueEx</Badge>,
23+
// RegDeleteValue: <Badge variant="">RegDeleteValue</Badge>,
24+
// RegCloseKey: <Badge variant="">RegCloseKey</Badge>,
25+
// RegOpenKeyEx: <Badge variant="">RegOpenKeyEx</Badge>,
26+
// socket: <Badge variant="">socket</Badge>,
27+
// bind: <Badge variant="">bind</Badge>,
28+
// send: <Badge variant="">send</Badge>,
29+
// connect: <Badge variant="">connect</Badge>,
30+
// recv: <Badge variant="">recv</Badge>,
31+
// memcpy: <Badge variant=""></Badge>,
32+
// },
33+
operationClearAt: 0,
34+
isCapturing: true
35+
};
736
// export const normalizeOperations = (ops, rules) => {
837
// ops.map(o => {
938
// return {
1039
// ...o,
1140
// summary
12-
// }
41+
// }
1342
// })
1443
// }

‎yprocmon-front/src/api.js

+34-13
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,40 @@ import axios from 'axios'
22

33
class Api
44
{
5-
constructor(props) {
6-
this._client = axios.create({
7-
baseURL: props
8-
})
9-
}
10-
async files() {
11-
const res = await this._client.get('files')
12-
return res.data
13-
}
14-
async operations(after) {
15-
const res = await this._client.get(after ? `operations?after=${after}` : 'operations')
16-
return res.data
17-
}
5+
constructor(props) {
6+
this._client = axios.create({
7+
baseURL: props
8+
})
9+
}
10+
async files() {
11+
const res = await this._client.get('files')
12+
return res.data
13+
}
14+
async operations(after) {
15+
const res = await this._client.get(after ? `operations?after=${after}` : 'operations')
16+
return res.data
17+
}
18+
async instances() {
19+
const res = await this._client.get('instances')
20+
return res.data
21+
}
22+
async upload(file) {
23+
const formData = new FormData();
24+
formData.append("file", file);
25+
const res = await this._client.post('upload', formData, {
26+
headers: {
27+
"Content-Type": "multipart/form-data",
28+
},
29+
})
30+
return res.data;
31+
}
32+
async run(name, command) {
33+
const params = new URLSearchParams();
34+
params.append("name", name)
35+
params.append("command", command)
36+
const res = await this._client.post(`run`, params);
37+
return res.data;
38+
}
1839
}
1940

2041
export default new Api('/api')
+20-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
11
.file-list-item-tail {
22
float: right;
3-
}
3+
}
4+
5+
.file-list-box {
6+
position: relative;
7+
}
8+
9+
.file-list-overlay {
10+
position: absolute;
11+
width: 100%;
12+
height: 100%;
13+
background: #000;
14+
opacity: 0.7;
15+
display: flex;
16+
justify-content: center;
17+
align-items: center;
18+
}
19+
20+
.file-list-placeholder {
21+
padding: 40px 20px 40px 20px;
22+
}

‎yprocmon-front/src/components/FileList.js

+43-27
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,66 @@
11
import './FileList.css';
22
import {
33
Badge,
4+
Button,
45
Row,
56
Col,
6-
ListGroup
7+
ListGroup,
8+
Spinner
79
} from 'react-bootstrap'
810
import { useState } from 'react';
911
import { useDropzone } from 'react-dropzone';
10-
import { BsAppIndicator, BsPlayFill } from 'react-icons/bs'
12+
import { BsAppIndicator, BsPlayFill, BsUpload } from 'react-icons/bs'
1113

1214
function FileList(props) {
1315
const {
16+
open,
1417
getRootProps,
1518
getInputProps,
1619
isDragActive,
1720
isDragAccept,
1821
isDragReject
1922
} = useDropzone({
2023
accept: '.exe',
21-
onDropAccepted: files => console.log(files)
24+
onDropAccepted: (f) => {
25+
if(!props.uploading) {
26+
props.onUpload && props.onUpload(f)
27+
}
28+
},
29+
noClick: true
2230
});
2331
return (
24-
<ListGroup>
25-
{
26-
props.files && props.files.map((f, idx) => {
27-
const active = props.active == f.name
28-
return (
29-
<ListGroup.Item action key={f.name} variant={active ? "success" : undefined}>
30-
<span><BsAppIndicator /></span>{' '}
31-
<span>{ f.name }</span>
32-
<div className="file-list-item-tail">
33-
{/* <Badge pill bg="primary">
34-
{ new Date(f.timestamp * 1000).toLocaleString() }
35-
</Badge>{' '} */}
36-
<Badge pill bg="success"><BsPlayFill /></Badge>{' '}
37-
</div>
38-
</ListGroup.Item>
39-
)
40-
})
41-
}
42-
<ListGroup.Item>
43-
<div {...getRootProps({})}>
44-
<input {...getInputProps()} />
45-
<p>Drag 'n' drop some files here, or click to select files</p>
46-
</div>
47-
</ListGroup.Item>
32+
<ListGroup className="file-list-box" {...getRootProps({isDragActive, isDragAccept, isDragReject})}>
33+
{
34+
(props.files && props.files.length != 0) ? props.files.map((f, idx) => {
35+
const active = props.active == f.name
36+
return (
37+
// <ListGroup.Item action key={f.name} variant={active ? "success" : undefined}>
38+
<ListGroup.Item action key={f.name} onClick={() => props.onSelect && props.onSelect(f)}>
39+
<span><BsAppIndicator /></span>{' '}
40+
<span>{ f.name }</span>
41+
<div className="file-list-item-tail">
42+
<Badge pill bg="success"><BsPlayFill /></Badge>{' '}
43+
</div>
44+
</ListGroup.Item>
45+
)
46+
}) : (<ListGroup.Item><div className='file-list-placeholder'>No files available.</div></ListGroup.Item>)
47+
}
48+
<ListGroup.Item>
49+
<input {...getInputProps()} />
50+
<Button variant="secondary" size="sm" onClick={open}>
51+
Drag 'n' drop or click to select files.
52+
</Button>
53+
</ListGroup.Item>
54+
<div className="file-list-overlay" style={(isDragActive && !props.uploading) ? {} : { display: 'none' }}>
55+
<div className="text-white">
56+
<BsUpload />{' '}<span>Drag 'n' drop some files here.</span>
57+
</div>
58+
</div>
59+
<div className="file-list-overlay" style={props.uploading ? {} : { display: 'none' }}>
60+
<Spinner animation="border" role="status">
61+
<span className="visually-hidden">Uploading...</span>
62+
</Spinner>
63+
</div>
4864
</ListGroup>
4965
);
5066
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.instance-list-item-tail {
2+
float: right;
3+
}
4+
5+
.instance-list-box {
6+
position: relative;
7+
}
8+
9+
.instance-list-overlay {
10+
position: absolute;
11+
width: 100%;
12+
height: 100%;
13+
background: #000;
14+
opacity: 0.7;
15+
display: flex;
16+
justify-content: center;
17+
align-items: center;
18+
}
19+
20+
.instance-list-placeholder {
21+
padding: 40px 20px 40px 20px;
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import './InstanceList.css';
2+
import {
3+
Badge,
4+
Button,
5+
Row,
6+
Col,
7+
ListGroup,
8+
Spinner
9+
} from 'react-bootstrap'
10+
import { useState } from 'react';
11+
import { BsAppIndicator, BsPlayFill, BsUpload } from 'react-icons/bs'
12+
13+
function InstanceList(props) {
14+
return (
15+
<ListGroup className="instance-list-box">
16+
{
17+
(props.instances && props.instances.length != 0) ? props.instances.sort((x, y) => x.timestamp - y.timestamp).map((f, idx) => {
18+
return (
19+
<ListGroup.Item variant={f.status == "exited" ? "danger" : "success"} key={f.pid} onClick={() => props.onSelect && props.onSelect(f)}>
20+
<span><BsAppIndicator /></span>{' '}
21+
<span>{ f.name }</span>
22+
<div className="instance-list-item-tail">
23+
{
24+
f.status == "exited" ? <Badge pill bg="danger">Exitcode: { f.exitCode }</Badge> : <Badge pill bg="success">PID: { f.pid }</Badge>
25+
}
26+
</div>
27+
</ListGroup.Item>
28+
)
29+
}) : (<ListGroup.Item><div className='instance-list-placeholder'>No instances.</div></ListGroup.Item>)
30+
}
31+
</ListGroup>
32+
);
33+
}
34+
35+
export default InstanceList;

‎yprocmon-front/src/components/OperationList.js

+207-32
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,196 @@
11
import "./FileList.css";
2-
import { Container, Badge, Table, Placeholder } from "react-bootstrap";
2+
import {
3+
Container,
4+
Badge,
5+
Table,
6+
OverlayTrigger,
7+
Tooltip,
8+
Placeholder,
9+
} from "react-bootstrap";
310
import { useEffect, useState, useRef } from "react";
411
import { BsAppIndicator, BsPlayFill } from "react-icons/bs";
512
import { SwitchTransition, CSSTransition } from "react-transition-group";
13+
import { AutoSizer, List, Column } from "react-virtualized";
14+
import "react-virtualized/styles.css";
615

7-
const ListPlaceholder = Array(10).fill(0).map((_, i) => (
8-
<tr key={i}>
9-
{
10-
Array(5).fill(0).map((_, i) => (
11-
<td key={i}>
12-
<Placeholder as="div" animation="glow"><Placeholder xs={12} /></Placeholder>
13-
</td>
14-
))
15-
}
16-
</tr>
17-
))
16+
const ListPlaceholder = Array(10)
17+
.fill(0)
18+
.map((_, i) => (
19+
<tr key={i}>
20+
{Array(5)
21+
.fill(0)
22+
.map((_, i) => (
23+
<td key={i}>
24+
<Placeholder as="div" animation="glow">
25+
<Placeholder xs={12} />
26+
</Placeholder>
27+
</td>
28+
))}
29+
</tr>
30+
));
31+
32+
const typemaps = {
33+
spawn: <Badge bg="dark">spawn</Badge>,
34+
MessageBox: <Badge variant="info">MessageBox</Badge>,
35+
...Object.fromEntries(
36+
["CreateFile", "WriteFile", "ReadFile"].map((v) => [
37+
v,
38+
<Badge variant="info">{v}</Badge>,
39+
])
40+
),
41+
...Object.fromEntries(
42+
["HeapCreate", "HeapDestroy", "HeapFree"].map((v) => [
43+
v,
44+
<Badge variant="info">{v}</Badge>,
45+
])
46+
),
47+
...Object.fromEntries(
48+
[
49+
"RegCreateKeyEx",
50+
"RegSetValueEx",
51+
"RegDeleteValue",
52+
"RegCloseKey",
53+
"RegOpenKeyEx",
54+
].map((v) => [v, <Badge variant="info">{v}</Badge>])
55+
),
56+
...Object.fromEntries(
57+
["socket", "bind", "send", "connect", "recv"].map((v) => [
58+
v,
59+
<Badge variant="info">{v}</Badge>,
60+
])
61+
),
62+
memcpy: <Badge variant="info">memcpy</Badge>,
63+
};
1864

1965
function OperationList(props) {
2066
const [ready, setReady] = useState(false);
2167
const bottomElement = useRef(null);
22-
console.log('operation list inited')
2368
useEffect(() => {
2469
setReady(true);
2570
}, []);
2671
useEffect(() => {
27-
if(bottomElement.current) { bottomElement.current.scrollIntoView({ behavior: "smooth" }) };
72+
if (bottomElement.current) {
73+
bottomElement.current.scrollIntoView({ behavior: "smooth" });
74+
}
2875
}, [props.following, props.operations, props.filters, ready, props.loading]);
2976
const showPlaceholder = !ready || props.loading;
30-
const showData = !showPlaceholder && (props.operations && props.operations != 0);
77+
const showData =
78+
!showPlaceholder && props.operations && props.operations != 0;
3179
const showText = !showPlaceholder && !showData;
80+
const rowRenderer = ({ key, index, isScrolling, isVisible, style }) => {
81+
const o = props.operations[index];
82+
return (
83+
<tr key={key} style={style}>
84+
<td>{o.index}</td>
85+
<td>{o.time}</td>
86+
<td>{o.pid}</td>
87+
<td>{typemaps[o.display.type] || o.display.type}</td>
88+
<td>
89+
{typeof o.display.summary == "string"
90+
? o.display.summary
91+
: o.display.summary.map((s) => (
92+
<OverlayTrigger
93+
placement="bottom"
94+
delay={{ show: 250, hide: 400 }}
95+
overlay={(props) => <Tooltip {...props}>{s.tooltip}</Tooltip>}
96+
>
97+
<Badge bg="light" text="dark" key={s.key}>
98+
<span>{s.key}:</span>
99+
<Badge pill bg="info">
100+
{s.value}
101+
</Badge>
102+
</Badge>
103+
</OverlayTrigger>
104+
))}
105+
</td>
106+
</tr>
107+
);
108+
};
109+
const rowGetter = ({ index }) => {
110+
const o = props.operations[index];
111+
return {
112+
...o,
113+
type: typemaps[o.display.type],
114+
summary: o.display.summary.map((s) => (
115+
<OverlayTrigger
116+
placement="bottom"
117+
delay={{ show: 250, hide: 400 }}
118+
overlay={(props) => <Tooltip {...props}>{s.tooltip}</Tooltip>}
119+
>
120+
<Badge bg="light" text="dark" key={s.key}>
121+
<span>{s.key}:</span>
122+
<Badge pill bg="info">
123+
{s.value}
124+
</Badge>
125+
</Badge>
126+
</OverlayTrigger>
127+
)),
128+
};
129+
};
130+
// return (
131+
// <div>
132+
// <AutoSizer>
133+
// {({ height, width }) => (
134+
// <Table
135+
// width={width}
136+
// height={height}
137+
// headerHeight={20}
138+
// rowHeight={50}
139+
// rowCount={props.operations.length}
140+
// rowGetter={rowGetter}
141+
// >
142+
// {/* <Column label="ID" dataKey="index" /> */}
143+
// <Column label="Time" dataKey="time" width={300} />
144+
// <Column label="PID" dataKey="pid" width={100} />
145+
// <Column label="Type" dataKey="type" width={100} />
146+
// <Column label="Summary" dataKey="summary" width={100} />
147+
// {/* <Column width={200} label="Description" dataKey="description" /> */}
148+
// </Table>
149+
// )}
150+
// </AutoSizer>
151+
// </div>
152+
// );
153+
// return (
154+
// <Container>
155+
// {/* <div style={{ display: "flex", flexDirection: "column" }}> */}
156+
// <Table hover>
157+
// <thead>
158+
// <tr>
159+
// <th>ID</th>
160+
// <th>Time</th>
161+
// <th>Process</th>
162+
// <th>Type</th>
163+
// <th>Summary</th>
164+
// </tr>
165+
// </thead>
166+
// <tbody>
167+
// {showPlaceholder && ListPlaceholder}
168+
// <div>
169+
// {showData && (
170+
// <AutoSizer>
171+
// {({ height, width }) => (
172+
// <List
173+
// width={width}
174+
// height={height}
175+
// rowCount={props.operations.length}
176+
// rowHeight={20}
177+
// rowRenderer={rowRenderer}
178+
// />
179+
// )}
180+
// </AutoSizer>
181+
// )}
182+
// </div>
183+
// </tbody>
184+
// </Table>
185+
// {showText && (
186+
// <div style={{ textAlign: "center" }}>No operations are recorded.</div>
187+
// )}
188+
// <div ref={bottomElement}></div>
189+
// </Container>
190+
// );
32191
return (
33192
<Container>
34-
{/* <div style={{ display: "flex", flexDirection: "column" }}> */}
193+
{/* <div style={{ display: "flex", flexDirection: "column" }}> */}
35194
<Table hover>
36195
<thead>
37196
<tr>
@@ -43,23 +202,39 @@ function OperationList(props) {
43202
</tr>
44203
</thead>
45204
<tbody>
46-
{
47-
showPlaceholder && ListPlaceholder
48-
}
49-
{
50-
showData && props.operations.map((o, i) => {
51-
return (
52-
<tr variant="success" key={i}>
53-
<td>{i}</td>
54-
<td>{o.time}</td>
55-
<td>{o.pid}</td>
56-
<td>{o.display.type}</td>
57-
<td>{o.display.summary}</td>
58-
</tr>
59-
)
60-
}
205+
{showPlaceholder && ListPlaceholder}
206+
{showData && props.operations.map(o => {
207+
// if (o.type == 'HOOK') return [];
208+
return(
209+
<tr key={o.index}>
210+
<td>{o.index}</td>
211+
<td>{o.time}</td>
212+
<td>{o.pid}</td>
213+
<td>{typemaps[o.display.type] || o.display.type}</td>
214+
<td>
215+
{
216+
typeof o.display.summary == "string"
217+
? o.display.summary
218+
: o.display.summary.map((s) => [
219+
<OverlayTrigger
220+
placement="bottom"
221+
delay={{ show: 250, hide: 400 }}
222+
overlay={(props) => <Tooltip {...props}>{s.tooltip}</Tooltip>}
223+
>
224+
<Badge bg="light" text="dark" key={s.key}>
225+
<span>{s.key}:</span>
226+
<Badge pill bg="info">
227+
{s.value}
228+
</Badge>
229+
</Badge>
230+
</OverlayTrigger>,
231+
' '
232+
])
233+
}
234+
</td>
235+
</tr>
61236
)
62-
}
237+
})}
63238
</tbody>
64239
</Table>
65240
{showText && (

‎yprocmon-front/src/pages/Monitor.js

+49-19
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import "./Monitor.css";
22
import FileList from "../components/FileList";
3-
import { Button, Container, Row, Col, Offcanvas } from "react-bootstrap";
3+
import { Button, Container, Row, Col, Offcanvas, Tabs, Tab } from "react-bootstrap";
44
import React, { useState, useEffect, useContext, useRef, useMemo } from "react";
55

66
import api from "../api";
77
import OperationList from "../components/OperationList";
88
import AppContext, { AppProvider } from "../AppContext";
9+
import InstanceList from "../components/InstanceList";
10+
11+
import { BsListNested } from 'react-icons/bs'
912

1013
function useInterval(callback, delay) {
1114
const savedCallback = useRef();
@@ -25,11 +28,10 @@ function useInterval(callback, delay) {
2528
}
2629

2730
function Monitor(props) {
28-
const [canvasOpen, setCanvas] = useState(false);
31+
const [canvasOpen, setCanvasOpen] = useState(false);
2932
const [operationsLoading, setOperationsLoading] = useState(true);
30-
console.log('monitor inited')
33+
const [uploading, setUploading] = useState(false);
3134
useEffect(() => {
32-
console.log('monitor rendered')
3335
}, [])
3436
const [filters, setFilters] = useState({
3537

@@ -52,6 +54,13 @@ function Monitor(props) {
5254
setOperationsLock(false)
5355
setOperationsLoading(false);
5456
}
57+
const updateInstances = async () => {
58+
const payload = await api.instances()
59+
dispatch({
60+
type: "INSTANCES_UPDATE",
61+
payload
62+
});
63+
}
5564
useEffect(() => {
5665
api.files().then((data) => {
5766
dispatch({
@@ -60,33 +69,54 @@ function Monitor(props) {
6069
});
6170
});
6271
}, []);
63-
useInterval(updateOperations, isCapturing ? 1500 : null);
64-
const canvasHide = () => {
65-
setCanvas(false);
66-
};
72+
useInterval(() => {
73+
updateOperations()
74+
updateInstances()
75+
}, isCapturing ? 1500 : null);
76+
const onUpload = async (files) => {
77+
setUploading(true);
78+
await Promise.all(files.map(f => api.upload(f)))
79+
api.files().then((data) => {
80+
dispatch({
81+
type: "FILES_UPDATE",
82+
payload: data,
83+
});
84+
setUploading(false);
85+
});
86+
}
87+
const onFileSelect = async (file) => {
88+
await api.run(file.name, file.name);
89+
await updateInstances();
90+
}
6791
// const OperationListMemo = React.memo(OperationList);
6892
return (
6993
<>
7094
<Container className="app-monitor">
71-
<Button
72-
variant="primary"
73-
onClick={() => alert(state.operations.length)}
74-
>
75-
Check Operations
76-
</Button>
95+
<div className="app-toolbar">
96+
<Button
97+
variant="primary"
98+
onClick={() => setCanvasOpen(true)}>
99+
<BsListNested />{' '}Task Manager
100+
</Button>
101+
</div>
77102
<div className="app-operation-list">
78103
{/* <OperationList loading={operationsLoading} operations={state.operations} /> */}
79104
<OperationList loading={operationsLoading} operations={state.operations} />
80105
</div>
81106
</Container>
82-
<Offcanvas show={canvasOpen} onHide={canvasHide}>
107+
<Offcanvas show={canvasOpen} onHide={() => setCanvasOpen(false)}>
83108
<Offcanvas.Header closeButton>
84-
<Offcanvas.Title>Offcanvas</Offcanvas.Title>
109+
<Offcanvas.Title>Task Manager</Offcanvas.Title>
85110
</Offcanvas.Header>
86111
<Offcanvas.Body>
87-
<div className="app-file-list">
88-
<FileList files={state.files} active="ymsgbox2.exe"></FileList>
89-
</div>
112+
<Tabs defaultActiveKey="files">
113+
<Tab eventKey="files" title="Files">
114+
<FileList uploading={uploading} files={state.files} active="ymsgbox2.exe" onUpload={onUpload} onSelect={onFileSelect}></FileList>
115+
</Tab>
116+
<Tab eventKey="instances" title="Instances">
117+
<InstanceList instances={state.instances} onSelect={f => alert(f)}></InstanceList>
118+
</Tab>
119+
</Tabs>
90120
</Offcanvas.Body>
91121
</Offcanvas>
92122
</>

0 commit comments

Comments
 (0)
Please sign in to comment.