diff --git a/config.go b/config.go
index f233822..9811148 100644
--- a/config.go
+++ b/config.go
@@ -25,8 +25,8 @@ type Router struct {
}
type Probe struct {
- Enabled bool `json:"enabled"`
- RouterDestination int `json:"router_destination"`
+ Enabled bool `json:"enabled"`
+ RouterDestinations []int `json:"router_destinations"`
}
type Salvo struct {
diff --git a/config.json.example b/config.json.example
index 183b8f2..816ed34 100644
--- a/config.json.example
+++ b/config.json.example
@@ -9,7 +9,7 @@
},
"probe": {
"enabled": true,
- "router_destination": 72
+ "router_destinations": [71, 72]
},
"salvos": []
}
\ No newline at end of file
diff --git a/main.go b/main.go
index e3d1b41..e6baed1 100644
--- a/main.go
+++ b/main.go
@@ -9,6 +9,7 @@ import (
"net/http"
"os"
"os/signal"
+ "strconv"
"sync"
"syscall"
@@ -21,7 +22,7 @@ import (
var (
router *nk.Router
- ProbeHandler *ProbeSocketHandler
+ ProbeHandlers []*ProbeSocketHandler
MatrixWSConnections = make(map[WebsocketConnection]uuid.UUID)
MatrixWSConnectionsMutex sync.Mutex
Upgrader = websocket.Upgrader{
@@ -90,21 +91,24 @@ func main() {
}
if Config.Probe.Enabled {
- ProbeHandler = &ProbeSocketHandler{
- clients: make(map[*ProbeClient]bool),
- register: make(chan *ProbeClient),
- unregister: make(chan *ProbeClient),
- broadcast: make(chan *[]byte),
- upgrader: &websocket.Upgrader{
- ReadBufferSize: readBufferSize,
- WriteBufferSize: writeBufferSize,
- CheckOrigin: func(r *http.Request) bool {
- return true
+ ProbeHandlers = make([]*ProbeSocketHandler, len(Config.Probe.RouterDestinations))
+ for i := range Config.Probe.RouterDestinations {
+ ProbeHandlers[i] = &ProbeSocketHandler{
+ clients: make(map[*ProbeClient]bool),
+ register: make(chan *ProbeClient),
+ unregister: make(chan *ProbeClient),
+ broadcast: make(chan *[]byte),
+ upgrader: &websocket.Upgrader{
+ ReadBufferSize: readBufferSize,
+ WriteBufferSize: writeBufferSize,
+ CheckOrigin: func(r *http.Request) bool {
+ return true
+ },
},
- },
- }
+ }
- go ProbeHandler.Run()
+ go ProbeHandlers[i].Run()
+ }
}
go router.Connect()
@@ -146,8 +150,26 @@ func serveHTTP() {
svc.GET("/v1/config", HandleConfig)
if Config.Probe.Enabled {
- svc.GET("/v1/ws/probe", ProbeHandler.ServeWS)
- svc.POST("/v1/probe/stream", HandleProbeStream)
+ svc.GET("/v1/ws/probe/:id", func(ctx *gin.Context) {
+ id := ctx.Param("id")
+ index, err := strconv.Atoi(id)
+ if err != nil {
+ ctx.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "message": "invalid probe id", "error": err.Error()})
+ log.Printf("[probe-viewer] unable to handle stream: %s", err)
+ return
+ }
+
+ if index > len(ProbeHandlers) || index < 0 {
+ ctx.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "message": "invalid probe id"})
+ log.Printf("[probe-viewer] unable to handle stream: %s", err)
+ return
+ }
+
+ ProbeHandlers[index].ServeWS(ctx)
+
+ })
+
+ svc.POST("/v1/probe/stream/:id", HandleProbeStream)
}
err := svc.Run(fmt.Sprintf(":%d", Config.Server.Port))
@@ -268,7 +290,15 @@ func HandleMatrixWS(c *gin.Context) {
}
func HandleProbeStream(c *gin.Context) {
- log.Printf("IncomingStream connected: %s\n", c.RemoteIP())
+ id := c.Param("id")
+ index, err := strconv.Atoi(id)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "message": "invalid probe id", "error": err.Error()})
+ log.Printf("[probe] unable to handle stream: %s", err)
+ return
+ }
+
+ log.Printf("stream for probe %d connected from %s", index, c.RemoteIP())
for {
data, err := ioutil.ReadAll(io.LimitReader(c.Request.Body, 1024))
@@ -276,8 +306,8 @@ func HandleProbeStream(c *gin.Context) {
break
}
- ProbeHandler.BroadcastData(&data)
+ ProbeHandlers[index].BroadcastData(&data)
}
- log.Printf("IncomingStream disconnected: %s\n", c.RemoteIP())
+ log.Printf("stream for probe %d disconnected from %s", index, c.RemoteIP())
}
diff --git a/ui/package.json b/ui/package.json
index 8445755..66d26e7 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -27,6 +27,9 @@
"babel-loader": "^9.1.2",
"carbon-components-react": "^8.24.0",
"eslint": "^8.35.0",
+ "eslint-plugin-import": "^2.25.3",
+ "eslint-plugin-jsx-a11y": "^6.5.1",
+ "eslint-plugin-react-hooks": "^4.3.0",
"html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "^2.7.2",
"react": "17.0.1",
@@ -40,6 +43,7 @@
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"css-loader": "^6.7.3",
+ "eslint-config-airbnb": "^19.0.4",
"eslint-plugin-react": "^7.32.2",
"prettier": "1.17.0",
"sass": "^1.58.3",
diff --git a/ui/src/App.jsx b/ui/src/App.jsx
index 6e9e0d6..f5d9884 100644
--- a/ui/src/App.jsx
+++ b/ui/src/App.jsx
@@ -21,7 +21,8 @@ import {
PortInput,
PortOutput,
Maximize,
- QueryQueue
+ QueryQueue,
+ ChooseItem
} from '@carbon/icons-react';
import Header from './Header.jsx';
@@ -37,6 +38,7 @@ const App = function App() {
)
const {matrix, loading: matrixLoading, error: matrixError, route} = useMatrix()
+ const [selectedProbe, setSelectedProbe] = useState(0)
const [selectedDestination, setSelectedDestination] = useState(1)
const [ProbeSOTRouting, setProbeSODRouting] = useState(false)
const [probeFullscreen, setProbeFullscreen] = useState(false)
@@ -44,25 +46,25 @@ const App = function App() {
useEffect(() => {
if (config?.probe.enabled && ProbeSOTRouting) {
- route(config.probe.router_destination, matrix.destinations[selectedDestination - 1].source.id)
+ route(config.probe.router_destinations[selectedProbe], matrix.destinations[selectedDestination - 1].source.id)
}
- if (config?.probe.enabled && selectedDestination == config.probe.router_destination && ProbeSOTRouting) {
+ if (config?.probe.enabled && selectedDestination == config.probe.router_destinations[selectedProbe] && ProbeSOTRouting) {
setProbeSODRouting(false)
}
}, [selectedDestination]);
useEffect(() => {
if (config?.probe.enabled && ProbeSOTRouting) {
- if (matrix.destinations[selectedDestination - 1].source.id != matrix.destinations[config.probe.router_destination - 1].source.id) {
- route(config.probe.router_destination, matrix.destinations[selectedDestination - 1].source.id)
+ if (matrix.destinations[selectedDestination - 1].source.id != matrix.destinations[config.probe.router_destinations[selectedProbe] - 1].source.id) {
+ route(config.probe.router_destinations[selectedProbe], matrix.destinations[selectedDestination - 1].source.id)
}
}
}, [matrix]);
useEffect(() => {
if (config?.probe.enabled && ProbeSOTRouting) {
- if (matrix.destinations[selectedDestination - 1].source.id != matrix.destinations[config.probe.router_destination - 1].source.id) {
- route(config.probe.router_destination, matrix.destinations[selectedDestination - 1].source.id)
+ if (matrix.destinations[selectedDestination - 1].source.id != matrix.destinations[config.probe.router_destinations[selectedProbe] - 1].source.id) {
+ route(config.probe.router_destinations[selectedProbe], matrix.destinations[selectedDestination - 1].source.id)
}
}
}, [ProbeSOTRouting]);
@@ -83,12 +85,12 @@ const App = function App() {
{config.probe.enabled &&
Probe: {matrix.destinations?.[config.probe.router_destination - 1]?.source?.label}>}
+ modalHeading={<>Probe {selectedProbe + 1}: {matrix.destinations?.[config.probe.router_destinations[selectedProbe] - 1]?.source?.label}>}
passiveModal
onRequestClose={()=> setProbeFullscreen(false)}
className="fullscreenProbe"
>
-
+
}
<>
@@ -152,13 +153,31 @@ const App = function App() {
{ config.probe.enabled &&
<>
- Probe
+ { config.probe.router_destinations.map((dst, index) => (
+
+ ))}
+
-
+
Probe Follow: {ProbeSOTRouting ? matrix.destinations?.[selectedDestination - 1]?.label : "None"}
- Probe Source: {matrix.destinations?.[config.probe.router_destination - 1]?.source?.label}
+ Probe Source: {matrix.destinations?.[config.probe.router_destinations[selectedProbe] - 1]?.source?.label}