Skip to content

Commit 49b05bd

Browse files
committed
switch log scroller on/off
1 parent c4f160c commit 49b05bd

5 files changed

Lines changed: 83 additions & 52 deletions

File tree

internal/nrtm4serve/rpc/rpchandler.go

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,11 @@ import (
1010
)
1111

1212
var (
13-
// ErrResponseUnauthorized returned when user has no session or is not authorized to execute method
14-
ErrResponseUnauthorized = JSONRPCError{Code: -32000, Message: "Unauthorized"}
15-
// ErrNoPrivilege user does not have the privilege to view resource
16-
ErrNoPrivilege = JSONRPCError{Code: -32098, Message: "No privilege"}
17-
1813
parseErrorResponse = JSONRPCResponse{JSONRPC: "2.0", Error: &JSONRPCError{Code: -32700, Message: "Parse error"}}
1914
invalidRequestResponse = JSONRPCResponse{JSONRPC: "2.0", Error: &JSONRPCError{Code: -32600, Message: "Invalid Request"}}
2015
methodNotFoundResponse = JSONRPCResponse{JSONRPC: "2.0", Error: &JSONRPCError{Code: -32601, Message: "Method not found"}}
2116
invalidParamsResponse = JSONRPCResponse{JSONRPC: "2.0", Error: &JSONRPCError{Code: -32602, Message: "Invalid params"}}
22-
unauthorizedResponse = JSONRPCResponse{JSONRPC: "2.0", Error: &ErrResponseUnauthorized}
17+
// unauthorizedResponse = JSONRPCResponse{JSONRPC: "2.0", Error: &ErrResponseUnauthorized}
2318
// User-defined codes from -32000 to -32099
2419
emptyResponse = JSONRPCResponse{JSONRPC: "2.0", Error: &JSONRPCError{Code: -32099, Message: ""}}
2520
)
@@ -58,9 +53,9 @@ func (e JSONRPCError) Error() string {
5853
// JSONRPCResponse A response
5954
type JSONRPCResponse struct {
6055
JSONRPC string `json:"jsonrpc"`
61-
ID interface{} `json:"id"`
56+
ID any `json:"id"`
6257
Error *JSONRPCError `json:"error,omitempty"`
63-
Result interface{} `json:"result,omitempty"`
58+
Result any `json:"result,omitempty"`
6459
}
6560

6661
// Handler which implements the JSONRPC 2.0 specification at https://www.jsonrpc.org/specification
@@ -113,7 +108,7 @@ func (handler Handler) ProcessRPC(w http.ResponseWriter, r *http.Request) {
113108
if ok {
114109
response = handler.execRPCRequest(w, r, sess, rpcreq)
115110
} else {
116-
w.WriteHeader(http.StatusForbidden)
111+
w.WriteHeader(http.StatusUnauthorized)
117112
return
118113
}
119114
} else {
@@ -126,19 +121,19 @@ func (handler Handler) ProcessRPC(w http.ResponseWriter, r *http.Request) {
126121

127122
func (handler Handler) execRPCRequest(w http.ResponseWriter, r *http.Request, session WebSession, req JSONRPCRequest) JSONRPCResponse {
128123
if req.JSONRPC != "2.0" || req.Method == nil || len(*req.Method) == 0 || req.ID == nil {
129-
return emptyResponse
124+
return invalidRequestResponse
130125
}
131126
targetMethod := reflect.ValueOf(handler.API).MethodByName(*req.Method)
132127
if reflect.Invalid == targetMethod.Kind() {
133-
return emptyResponse
128+
return methodNotFoundResponse
134129
}
135130
in := make([]reflect.Value, targetMethod.Type().NumIn())
136131
if targetMethod.Type().NumIn() < len(req.Params) {
137132
return invalidParamsResponse
138133
}
139134

140135
filled := 0
141-
for paramIdx := 0; paramIdx < targetMethod.Type().NumIn(); paramIdx++ {
136+
for paramIdx := range targetMethod.Type().NumIn() {
142137
if reflect.TypeOf(w).AssignableTo(targetMethod.Type().In(paramIdx)) {
143138
in[paramIdx] = reflect.ValueOf(w)
144139
filled++
@@ -253,7 +248,7 @@ func (handler Handler) execRPCRequest(w http.ResponseWriter, r *http.Request, se
253248
return rpcResponse
254249
}
255250

256-
func collectReturns(apiResp interface{}) (interface{}, *JSONRPCError) {
251+
func collectReturns(apiResp any) (any, *JSONRPCError) {
257252
switch resp := apiResp.(type) {
258253
case JSONRPCError:
259254
return nil, &resp
@@ -264,7 +259,7 @@ func collectReturns(apiResp interface{}) (interface{}, *JSONRPCError) {
264259
return nil, &rpcErr
265260
default:
266261
if apiResp == nil {
267-
return "OK", nil
262+
return "", nil
268263
}
269264
return apiResp, nil
270265
}

internal/nrtm4serve/webapi.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ import (
99
)
1010

1111
var (
12-
// DeltaUnavaliableErrCode JSON RPC error code
13-
DeltaUnavaliableErrCode = -32060
14-
// Hash256ErrCode JSON RPC error code
15-
Hash256ErrCode = -32020
16-
// SnapshotInsertFailedErrCode JSON RPC error code
17-
SnapshotInsertFailedErrCode = -32040
12+
// Application codes from -32000 to -32098
13+
14+
// Hash256ErrorCode JSON RPC error code
15+
Hash256ErrorCode = -32010
16+
// SnapshotInsertFailedErrorCode JSON RPC error code
17+
SnapshotInsertFailedErrorCode = -32020
18+
// DeltaUnavaliableErrorCode JSON RPC error code
19+
DeltaUnavaliableErrorCode = -32030
20+
// NRTMServiceErrorCode problem with the service
21+
NRTMServiceErrorCode = -32040
1822
)
1923

2024
// WebAPI defines the RPC functions used by the web client
@@ -23,7 +27,7 @@ type WebAPI struct {
2327
Processor service.NRTMProcessor
2428
}
2529

26-
// GetAuth implements interface -- allows requests to all methods
30+
// GetAuth implements rpc.API interface -- allows requests to all methods
2731
func (api WebAPI) GetAuth(w http.ResponseWriter, r *http.Request, req rpc.JSONRPCRequest) (rpc.WebSession, bool) {
2832
return rpc.WebSession{}, true
2933
}
@@ -51,7 +55,7 @@ func (api WebAPI) Connect(url, label string) (string, error) {
5155
func (api WebAPI) Update(src, label string) (persist.NRTMSourceDetails, error) {
5256
target, err := api.Processor.Update(src, label)
5357
if err != nil {
54-
return persist.NRTMSourceDetails{}, err
58+
return wrapResponse(persist.NRTMSourceDetails{}, err)
5559
}
5660
deets, err := api.Processor.ListSources()
5761
if err != nil {
@@ -75,11 +79,15 @@ func wrapResponse[T any](res T, err error) (T, error) {
7579
return res, nil
7680
}
7781
if err == service.ErrHashMismatch {
78-
return res, rpc.JSONRPCError{Code: Hash256ErrCode, Message: err.Error()}
82+
return res, rpc.JSONRPCError{Code: Hash256ErrorCode, Message: err.Error()}
7983
} else if err == service.ErrNextConsecutiveDeltaUnavaliable {
80-
return res, rpc.JSONRPCError{Code: DeltaUnavaliableErrCode, Message: err.Error()}
84+
return res, rpc.JSONRPCError{Code: DeltaUnavaliableErrorCode, Message: err.Error()}
8185
} else if err == service.ErrSnapshotInsertFailed {
82-
return res, rpc.JSONRPCError{Code: SnapshotInsertFailedErrCode, Message: err.Error()}
86+
return res, rpc.JSONRPCError{Code: SnapshotInsertFailedErrorCode, Message: err.Error()}
87+
}
88+
switch err.(type) {
89+
case service.ErrNRTMServiceError:
90+
return res, rpc.JSONRPCError{Code: NRTMServiceErrorCode, Message: err.Error()}
8391
}
8492
return res, err
8593
}

web/src/components/logs/LogPanel.tsx

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { Fragment } from "react/jsx-runtime";
22

3-
import { Box, createTheme, ThemeProvider, Typography } from "@mui/material";
3+
import { createTheme, SxProps, ThemeProvider } from "@mui/material";
4+
import Box from "@mui/material/Box";
5+
import Fab from "@mui/material/Fab";
46
import Grid from "@mui/material/Grid2";
7+
import Zoom from "@mui/material/Zoom";
58

9+
import Typography from "@mui/material/Typography";
10+
import WatchIcon from "@mui/icons-material/Watch";
11+
import WatchOffIcon from "@mui/icons-material/WatchOff";
612
import { LogLine, printParams } from "./model";
13+
import { useState } from "react";
714

815
const theme = createTheme({
916
typography: {
10-
subtitle1: {
11-
fontSize: 12,
12-
},
1317
body1: {
1418
fontFamily: "monospace",
1519
fontSize: 12,
@@ -18,6 +22,12 @@ const theme = createTheme({
1822
},
1923
});
2024

25+
const fabStyle: SxProps = {
26+
position: "absolute",
27+
bottom: 16,
28+
right: 16,
29+
};
30+
2131
const levels = ["ERROR", "WARN", "INFO", "DEBUG"];
2232

2333
const showDate = (dstr: string): string => {
@@ -31,12 +41,17 @@ interface LogPanelProps {
3141
}
3242

3343
export default function LogPanel({ messageHistory, level }: LogPanelProps) {
44+
const [scrollBottom, setScrollBottom] = useState(true);
3445
let to;
3546
clearTimeout(to);
3647
to = setTimeout(() => {
48+
if (!scrollBottom) {
49+
return;
50+
}
3751
const el = document.getElementById("gridend");
3852
el && el.scrollIntoView();
3953
}, 800);
54+
4055
return (
4156
<ThemeProvider theme={theme}>
4257
<Grid container spacing={0} columns={12}>
@@ -60,6 +75,17 @@ export default function LogPanel({ messageHistory, level }: LogPanelProps) {
6075
</Fragment>
6176
))}
6277
</Grid>
78+
<Box sx={{ "& > :not(style)": { m: 1 } }}>
79+
<Fab
80+
sx={fabStyle}
81+
size="small"
82+
color="secondary"
83+
aria-label="add"
84+
onClick={() => setScrollBottom(!scrollBottom)}
85+
>
86+
{scrollBottom ? <WatchIcon /> : <WatchOffIcon />}
87+
</Fab>
88+
</Box>
6389
<Box id="gridend"></Box>
6490
</ThemeProvider>
6591
);

web/src/components/sources/Source.tsx

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -142,17 +142,20 @@ export default function Source({
142142

143143
<Typography variant="h4" component="h2" sx={{ mb: 2 }}>
144144
{source.Source} {source.Label}
145+
{!!loading && <CircularProgress size="1em" />}
145146
</Typography>
146147
<Box sx={{ mb: 1 }}>{alert}</Box>
147148
<Stack spacing={1} direction="row" sx={{ mb: 1 }}>
148-
<Button
149-
variant="outlined"
150-
size="small"
151-
startIcon={<UpdateIcon />}
152-
onClick={updateSourceClicked}
153-
>
154-
Update
155-
</Button>
149+
<Box sx={{ flexGrow: 1, display: "flex" }}>
150+
<Button
151+
variant="outlined"
152+
size="small"
153+
startIcon={<UpdateIcon />}
154+
onClick={updateSourceClicked}
155+
>
156+
Update
157+
</Button>
158+
</Box>
156159
<Button
157160
variant="outlined"
158161
color="error"
@@ -174,14 +177,10 @@ export default function Source({
174177
<Label>Label</Label>
175178
</Grid>
176179
<Grid size={{ xs: 8, md: 8 }}>
177-
{!!loading ? (
178-
<CircularProgress />
179-
) : (
180-
<LabelControl
181-
value={source.Label}
182-
onTextEntered={saveLabel}
183-
></LabelControl>
184-
)}
180+
<LabelControl
181+
value={source.Label}
182+
onTextEntered={saveLabel}
183+
></LabelControl>
185184
</Grid>
186185
<Grid size={{ xs: 4, md: 4 }}>
187186
<Label>Version</Label>

web/src/components/sources/Sources.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import WebAPIClient from "../../client/WebAPIClient.ts";
1515
import SourcesTable from "./SourcesTable.tsx";
1616
import SourcesInput from "./SourcesInput.tsx";
1717
import Source from "./Source.tsx";
18+
import { JsonRPCError } from "../../client/RPCClient.ts";
1819

1920
export default function Sources() {
2021
const [pageLoading, setPageLoading] = useState(false);
@@ -31,19 +32,25 @@ export default function Sources() {
3132
}
3233
return a.Source.localeCompare(b.Source);
3334
};
34-
const newMessage = (msg: string, level?: AlertColor) => {
35+
const newMessage = (msg: string | JsonRPCError, level?: AlertColor) => {
3536
let icon;
3637
if (!level) {
3738
level = "info";
3839
icon = <ErrorIcon fontSize="inherit" />;
3940
}
41+
let text: string;
42+
if (typeof msg === "string") {
43+
text = msg;
44+
} else {
45+
text = `RPCError${msg.code} ${msg.message}`;
46+
}
4047
setAlert(
4148
<Alert
4249
icon={icon}
4350
severity={level}
4451
sx={{ width: "100%", maxWidth: { sm: "100%", md: "1700px" } }}
4552
>
46-
{msg}
53+
{text}
4754
</Alert>
4855
);
4956
};
@@ -113,11 +120,7 @@ export default function Sources() {
113120
console.log("success", msg);
114121
},
115122
(err) => {
116-
if (err.hasOwnProperty("message")) {
117-
newMessage(err.message, "error");
118-
} else {
119-
newMessage("Error: " + err, "error");
120-
}
123+
newMessage(err, "error");
121124
}
122125
)
123126
.finally(() => setDataLoading(false));

0 commit comments

Comments
 (0)