From 00272ff9a6ae962193c0cd57a75d5adddef4a70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leandro=20Ant=C3=B4nio=20Farias=20Machado?= Date: Thu, 17 Aug 2023 01:05:10 -0300 Subject: [PATCH 1/5] feat(docker): container networks --- devops/docker-compose.yaml | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/devops/docker-compose.yaml b/devops/docker-compose.yaml index 2149524..6b52c28 100644 --- a/devops/docker-compose.yaml +++ b/devops/docker-compose.yaml @@ -7,26 +7,29 @@ services: stdin_open: true volumes: - ../:/app/oktopus - command: bash -c "cd /app/oktopus/backend/services/controller && go run cmd/oktopus/main.go -mongo mongodb://mongodb:27017 -a mochi -p 1883" + command: bash -c "cd /app/oktopus/backend/services/controller && go run cmd/oktopus/main.go -mongo mongodb://172.16.235.2:27017 -a 172.16.235.4 -p 1883" ports: - 8000:8000 depends_on: - mongodb - mochi networks: - - usp_network + usp_network: + ipv4_address: 172.16.235.1 mongodb: image: mongo container_name: mongodb networks: - - usp_network + usp_network: + ipv4_address: 172.16.235.2 redis: image: redis container_name: redis networks: - - usp_network + usp_network: + ipv4_address: 172.16.235.3 mochi: image: 'golang:1.18' @@ -41,7 +44,8 @@ services: depends_on: - redis networks: - - usp_network + usp_network: + ipv4_address: 172.16.235.4 socketio: image: 'node:14.20' @@ -54,7 +58,8 @@ services: ports: - 5000:5000 networks: - - usp_network + usp_network: + ipv4_address: 172.16.235.5 frontend: image: 'node:14.20' @@ -67,8 +72,14 @@ services: ports: - 3000:3000 networks: - - usp_network + usp_network: + ipv4_address: 172.16.235.6 networks: - usp_network: + usp_network: driver: bridge + ipam: + driver: default + config: + - subnet: 172.16.235.0/24 + gateway: 172.16.235.1 \ No newline at end of file From 274b0f51b8eea54eee66b21e580f266812e37c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leandro=20Ant=C3=B4nio=20Farias=20Machado?= Date: Sat, 19 Aug 2023 11:37:06 -0300 Subject: [PATCH 2/5] feat(controller): get env vars form .env --- backend/services/controller/.env | 13 +++- .../services/controller/cmd/oktopus/main.go | 70 ++++++++++++++----- 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/backend/services/controller/.env b/backend/services/controller/.env index 8650e1e..60e39d7 100644 --- a/backend/services/controller/.env +++ b/backend/services/controller/.env @@ -1 +1,12 @@ -SECRET_API_KEY="" \ No newline at end of file +SECRET_API_KEY="secretkey" # !IMPORTANT: Change this to your own secret key, and don't share. +MONGO_URI="" +DEVICES_STATUS_TOPIC="" +DEVICE_PUB_TOPIC="" +BROKER_ADDR="" +BROKER_PORT="" +BROKER_TLS="" +BROKER_USERNAME="" +BROKER_PASSWORD="" +BROKER_CLIENTID="" +BROKER_QOS="" +REST_API_PORT="" \ No newline at end of file diff --git a/backend/services/controller/cmd/oktopus/main.go b/backend/services/controller/cmd/oktopus/main.go index c75822d..8c50402 100755 --- a/backend/services/controller/cmd/oktopus/main.go +++ b/backend/services/controller/cmd/oktopus/main.go @@ -5,15 +5,17 @@ package main import ( "context" "flag" + "log" + "os" + "os/signal" + "strconv" + "sync" + "syscall" + "github.com/joho/godotenv" "github.com/leandrofars/oktopus/internal/api" "github.com/leandrofars/oktopus/internal/db" usp_msg "github.com/leandrofars/oktopus/internal/usp_message" - "log" - "os" - "os/signal" - "sync" - "syscall" "github.com/leandrofars/oktopus/internal/mqtt" "github.com/leandrofars/oktopus/internal/mtp" @@ -43,19 +45,26 @@ func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) + /* + App variables priority: + 1º - Flag through command line. + 2º - Env variables. + 3º - Default flag value + */ + log.Println("Starting Oktopus Project TR-369 Controller Version:", VERSION) // fl_endpointId := flag.String("endpoint_id", "proto::oktopus-controller", "Defines the enpoint id the Agent must trust on.") - flDevicesTopic := flag.String("d", "oktopus/+/status/+", "That's the topic mqtt broker end new devices info.") - flSubTopic := flag.String("sub", "oktopus/+/controller/+", "That's the topic agent must publish to, and the controller keeps on listening.") - flBrokerAddr := flag.String("a", "localhost", "Mqtt broker adrress") - flBrokerPort := flag.String("p", "1883", "Mqtt broker port") - flTlsCert := flag.Bool("tls", false, "Connect to broker over TLS") - flBrokerUsername := flag.String("u", "", "Mqtt broker username") - flBrokerPassword := flag.String("P", "", "Mqtt broker password") - flBrokerClientId := flag.String("i", "", "A clientid for the Mqtt connection") - flBrokerQos := flag.Int("q", 0, "Quality of service of mqtt messages delivery") - flAddrDB := flag.String("mongo", "mongodb://localhost:27017/", "MongoDB URI") - flApiPort := flag.String("ap", "8000", "Rest api port") + flDevicesTopic := flag.String("d", lookupEnvOrString("DEVICES_STATUS_TOPIC", "oktopus/+/status/+"), "That's the topic mqtt broker end new devices info.") + flSubTopic := flag.String("sub", lookupEnvOrString("DEVICE_PUB_TOPIC", "oktopus/+/controller/+"), "That's the topic agent must publish to, and the controller keeps on listening.") + flBrokerAddr := flag.String("a", lookupEnvOrString("BROKER_ADDR", "localhost"), "Mqtt broker adrress") + flBrokerPort := flag.String("p", lookupEnvOrString("BROKER_PORT", "1883"), "Mqtt broker port") + flTlsCert := flag.Bool("tls", lookupEnvOrBool("BROKER_TLS", false), "Connect to broker over TLS") + flBrokerUsername := flag.String("u", lookupEnvOrString("BROKER_USERNAME", ""), "Mqtt broker username") + flBrokerPassword := flag.String("P", lookupEnvOrString("BROKER_PASSWORD", ""), "Mqtt broker password") + flBrokerClientId := flag.String("i", lookupEnvOrString("BROKER_CLIENTID", ""), "A clientid for the Mqtt connection") + flBrokerQos := flag.Int("q", lookupEnvOrInt("BROKER_QOS", 0), "Quality of service of mqtt messages delivery") + flAddrDB := flag.String("mongo", lookupEnvOrString("MONGO_URI", "mongodb://localhost:27017/"), "MongoDB URI") + flApiPort := flag.String("ap", lookupEnvOrString("REST_API_PORT", "8000"), "Rest api port") flHelp := flag.Bool("help", false, "Help") flag.Parse() @@ -101,3 +110,32 @@ func main() { log.Println("(⌐■_■) Oktopus is out!") } + +func lookupEnvOrString(key string, defaultVal string) string { + if val, _ := os.LookupEnv(key); val != "" { + return val + } + return defaultVal +} + +func lookupEnvOrInt(key string, defaultVal int) int { + if val, _ := os.LookupEnv(key); val != "" { + v, err := strconv.Atoi(val) + if err != nil { + log.Fatalf("LookupEnvOrInt[%s]: %v", key, err) + } + return v + } + return defaultVal +} + +func lookupEnvOrBool(key string, defaultVal bool) bool { + if val, _ := os.LookupEnv(key); val != "" { + v, err := strconv.ParseBool(val) + if err != nil { + log.Fatalf("LookupEnvOrInt[%s]: %v", key, err) + } + return v + } + return defaultVal +} From f866222b8dc8a8950dbb62dbe9890e8647a965ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leandro=20Ant=C3=B4nio=20Farias=20Machado?= Date: Mon, 4 Sep 2023 01:28:16 -0300 Subject: [PATCH 3/5] feat(api): new files organization + fix(api): map lock error --- .gitignore | 1 - .../services/controller/cmd/oktopus/main.go | 2 +- .../services/controller/internal/api/api.go | 741 ------------------ .../controller/internal/api/device.go | 510 ++++++++++++ .../services/controller/internal/api/user.go | 167 ++++ .../services/controller/internal/api/wifi.go | 150 ++++ 6 files changed, 828 insertions(+), 743 deletions(-) create mode 100644 backend/services/controller/internal/api/device.go create mode 100644 backend/services/controller/internal/api/user.go create mode 100644 backend/services/controller/internal/api/wifi.go diff --git a/.gitignore b/.gitignore index 64eeb06..3f71b3a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,5 @@ go.work *.acl .idea .vscode -controller main mochi \ No newline at end of file diff --git a/backend/services/controller/cmd/oktopus/main.go b/backend/services/controller/cmd/oktopus/main.go index 8c50402..038f83f 100755 --- a/backend/services/controller/cmd/oktopus/main.go +++ b/backend/services/controller/cmd/oktopus/main.go @@ -63,7 +63,7 @@ func main() { flBrokerPassword := flag.String("P", lookupEnvOrString("BROKER_PASSWORD", ""), "Mqtt broker password") flBrokerClientId := flag.String("i", lookupEnvOrString("BROKER_CLIENTID", ""), "A clientid for the Mqtt connection") flBrokerQos := flag.Int("q", lookupEnvOrInt("BROKER_QOS", 0), "Quality of service of mqtt messages delivery") - flAddrDB := flag.String("mongo", lookupEnvOrString("MONGO_URI", "mongodb://localhost:27017/"), "MongoDB URI") + flAddrDB := flag.String("mongo", lookupEnvOrString("MONGO_URI", "mongodb://localhost:27017"), "MongoDB URI") flApiPort := flag.String("ap", lookupEnvOrString("REST_API_PORT", "8000"), "Rest api port") flHelp := flag.Bool("help", false, "Help") diff --git a/backend/services/controller/internal/api/api.go b/backend/services/controller/internal/api/api.go index d5e4c8a..0007990 100644 --- a/backend/services/controller/internal/api/api.go +++ b/backend/services/controller/internal/api/api.go @@ -1,24 +1,17 @@ package api import ( - "encoding/json" "log" "net/http" - "strconv" - "strings" "sync" "time" "github.com/gorilla/mux" - "github.com/leandrofars/oktopus/internal/api/auth" "github.com/leandrofars/oktopus/internal/api/cors" "github.com/leandrofars/oktopus/internal/api/middleware" "github.com/leandrofars/oktopus/internal/db" "github.com/leandrofars/oktopus/internal/mtp" usp_msg "github.com/leandrofars/oktopus/internal/usp_message" - "github.com/leandrofars/oktopus/internal/utils" - "go.mongodb.org/mongo-driver/mongo" - "google.golang.org/protobuf/proto" ) type Api struct { @@ -111,737 +104,3 @@ func StartApi(a Api) { }() log.Println("Running Api at port", a.Port) } - -func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) { - devices, err := a.Db.RetrieveDevices() - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - err = json.NewEncoder(w).Encode(devices) - if err != nil { - log.Println(err) - } - - return -} - -func (a *Api) retrieveUsers(w http.ResponseWriter, r *http.Request) { - users, err := a.Db.FindAllUsers() - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - for _, x := range users { - delete(x, "password") - } - - err = json.NewEncoder(w).Encode(users) - if err != nil { - log.Println(err) - } - return -} - -// Check which fw image is activated -func checkAvaiableFwPartition(reqPathResult []*usp_msg.GetResp_RequestedPathResult) int { - for _, x := range reqPathResult { - partitionsNumber := len(x.ResolvedPathResults) - if partitionsNumber > 1 { - log.Printf("Device has %d firmware partitions", partitionsNumber) - } - for i, y := range x.ResolvedPathResults { - if y.ResultParams["Status"] == "Available" { - log.Printf("Partition %d is avaiable", i) - return i - } - } - } - return -1 -} - -func (a *Api) deviceFwUpdate(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - sn := vars["sn"] - a.deviceExists(sn, w) - - msg := utils.NewGetMsg(usp_msg.Get{ - ParamPaths: []string{"Device.DeviceInfo.FirmwareImage.*.Status"}, - MaxDepth: 1, - }) - encodedMsg, err := proto.Marshal(&msg) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - record := utils.NewUspRecord(encodedMsg, sn) - tr369Message, err := proto.Marshal(&record) - if err != nil { - log.Fatalln("Failed to encode tr369 record:", err) - } - - a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) - log.Println("Sending Msg:", msg.Header.MsgId) - a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) - - var getMsgAnswer *usp_msg.GetResp - - select { - case msg := <-a.MsgQueue[msg.Header.MsgId]: - log.Printf("Received Msg: %s", msg.Header.MsgId) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - getMsgAnswer = msg.Body.GetResponse().GetGetResp() - case <-time.After(time.Second * 55): - log.Printf("Request %s Timed Out", msg.Header.MsgId) - w.WriteHeader(http.StatusGatewayTimeout) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode("Request Timed Out") - return - } - - // Check which fw image is activated - partition := checkAvaiableFwPartition(getMsgAnswer.ReqPathResults) - if partition < 0 { - log.Println("Error to get device available firmware partition, probably it has only one partition") - w.WriteHeader(http.StatusInternalServerError) - json.NewEncoder(w).Encode("Server don't have the hability to update device with only one partition") - return - //TODO: update device with only one partition - } - - var receiver = usp_msg.Operate{ - Command: "Device.DeviceInfo.FirmwareImage.1.Download()", - CommandKey: "Download()", - SendResp: true, - InputArgs: map[string]string{ - "URL": "http://cronos.intelbras.com.br/download/PON/121AC/beta/121AC-2.3-230620-77753201df4f1e2c607a7236746c8491.tar", //TODO: use dynamic url - "AutoActivate": "true", - //"Username": "", - //"Password": "", - "FileSize": "0", //TODO: send firmware length - //"CheckSumAlgorithm": "", - //"CheckSum": "", - }, - } - - msg = utils.NewOperateMsg(receiver) - encodedMsg, err = proto.Marshal(&msg) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - record = utils.NewUspRecord(encodedMsg, sn) - tr369Message, err = proto.Marshal(&record) - if err != nil { - log.Fatalln("Failed to encode tr369 record:", err) - } - - a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) - log.Println("Sending Msg:", msg.Header.MsgId) - a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) - - select { - case msg := <-a.MsgQueue[msg.Header.MsgId]: - log.Printf("Received Msg: %s", msg.Header.MsgId) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode(msg.Body.GetResponse().GetSetResp()) - return - case <-time.After(time.Second * 55): - log.Printf("Request %s Timed Out", msg.Header.MsgId) - w.WriteHeader(http.StatusGatewayTimeout) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode("Request Timed Out") - return - } -} - -func (a *Api) deviceWifi(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - sn := vars["sn"] - a.deviceExists(sn, w) - - if r.Method == http.MethodGet { - msg := utils.NewGetMsg(usp_msg.Get{ - ParamPaths: []string{ - "Device.WiFi.SSID.[Enable==true].SSID", - //"Device.WiFi.AccessPoint.[Enable==true].SSIDReference", - "Device.WiFi.AccessPoint.[Enable==true].Security.ModeEnabled", - "Device.WiFi.AccessPoint.[Enable==true].Security.ModesSupported", - //"Device.WiFi.EndPoint.[Enable==true].", - "Device.WiFi.Radio.[Enable==true].AutoChannelEnable", - "Device.WiFi.Radio.[Enable==true].Channel", - "Device.WiFi.Radio.[Enable==true].CurrentOperatingChannelBandwidth", - "Device.WiFi.Radio.[Enable==true].OperatingFrequencyBand", - //"Device.WiFi.Radio.[Enable==true].PossibleChannels", - "Device.WiFi.Radio.[Enable==true].SupportedOperatingChannelBandwidths", - }, - MaxDepth: 2, - }) - - encodedMsg, err := proto.Marshal(&msg) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - record := utils.NewUspRecord(encodedMsg, sn) - tr369Message, err := proto.Marshal(&record) - if err != nil { - log.Fatalln("Failed to encode tr369 record:", err) - } - - //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) - a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) - log.Println("Sending Msg:", msg.Header.MsgId) - a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) - - //TODO: verify in protocol and in other models, the Device.Wifi parameters. Maybe in the future, to use SSIDReference from AccessPoint - select { - case msg := <-a.MsgQueue[msg.Header.MsgId]: - log.Printf("Received Msg: %s", msg.Header.MsgId) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - answer := msg.Body.GetResponse().GetGetResp() - - var wifi [2]WiFi - - //TODO: better algorithm, might use something faster an more reliable - //TODO: full fill the commented wifi resources - for _, x := range answer.ReqPathResults { - if x.RequestedPath == "Device.WiFi.SSID.[Enable==true].SSID" { - for i, y := range x.ResolvedPathResults { - wifi[i].SSID = y.ResultParams["SSID"] - } - continue - } - if x.RequestedPath == "Device.WiFi.AccessPoint.[Enable==true].Security.ModeEnabled" { - for i, y := range x.ResolvedPathResults { - wifi[i].Security = y.ResultParams["Security.ModeEnabled"] - } - continue - } - if x.RequestedPath == "Device.WiFi.AccessPoint.[Enable==true].Security.ModesSupported" { - for i, y := range x.ResolvedPathResults { - wifi[i].SecurityCapabilities = strings.Split(y.ResultParams["Security.ModesSupported"], ",") - } - continue - } - if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].AutoChannelEnable" { - for i, y := range x.ResolvedPathResults { - autoChannel, err := strconv.ParseBool(y.ResultParams["AutoChannelEnable"]) - if err != nil { - log.Println(err) - wifi[i].AutoChannelEnable = false - } else { - wifi[i].AutoChannelEnable = autoChannel - } - } - continue - } - if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].Channel" { - for i, y := range x.ResolvedPathResults { - channel, err := strconv.Atoi(y.ResultParams["Channel"]) - if err != nil { - log.Println(err) - wifi[i].Channel = -1 - } else { - wifi[i].Channel = channel - } - } - continue - } - if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].CurrentOperatingChannelBandwidth" { - for i, y := range x.ResolvedPathResults { - wifi[i].ChannelBandwidth = y.ResultParams["CurrentOperatingChannelBandwidth"] - } - continue - } - if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].OperatingFrequencyBand" { - for i, y := range x.ResolvedPathResults { - wifi[i].FrequencyBand = y.ResultParams["OperatingFrequencyBand"] - } - continue - } - if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].SupportedOperatingChannelBandwidths" { - for i, y := range x.ResolvedPathResults { - wifi[i].SupportedChannelBandwidths = strings.Split(y.ResultParams["SupportedOperatingChannelBandwidths"], ",") - } - continue - } - } - json.NewEncoder(w).Encode(&wifi) - return - case <-time.After(time.Second * 45): - log.Printf("Request %s Timed Out", msg.Header.MsgId) - w.WriteHeader(http.StatusGatewayTimeout) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode("Request Timed Out") - return - } - } -} - -func (a *Api) deviceGetParameterInstances(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - sn := vars["sn"] - a.deviceExists(sn, w) - - var receiver usp_msg.GetInstances - - err := json.NewDecoder(r.Body).Decode(&receiver) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - msg := utils.NewGetParametersInstancesMsg(receiver) - encodedMsg, err := proto.Marshal(&msg) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - record := utils.NewUspRecord(encodedMsg, sn) - tr369Message, err := proto.Marshal(&record) - if err != nil { - log.Fatalln("Failed to encode tr369 record:", err) - } - - //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) - a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) - log.Println("Sending Msg:", msg.Header.MsgId) - a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) - - select { - case msg := <-a.MsgQueue[msg.Header.MsgId]: - log.Printf("Received Msg: %s", msg.Header.MsgId) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetInstancesResp()) - return - case <-time.After(time.Second * 55): - log.Printf("Request %s Timed Out", msg.Header.MsgId) - w.WriteHeader(http.StatusGatewayTimeout) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode("Request Timed Out") - return - } -} - -func (a *Api) deviceGetSupportedParametersMsg(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - sn := vars["sn"] - a.deviceExists(sn, w) - - var receiver usp_msg.GetSupportedDM - - err := json.NewDecoder(r.Body).Decode(&receiver) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - msg := utils.NewGetSupportedParametersMsg(receiver) - encodedMsg, err := proto.Marshal(&msg) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - record := utils.NewUspRecord(encodedMsg, sn) - tr369Message, err := proto.Marshal(&record) - if err != nil { - log.Fatalln("Failed to encode tr369 record:", err) - } - - //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) - a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) - log.Println("Sending Msg:", msg.Header.MsgId) - a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) - - select { - case msg := <-a.MsgQueue[msg.Header.MsgId]: - log.Printf("Received Msg: %s", msg.Header.MsgId) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetSupportedDmResp()) - return - case <-time.After(time.Second * 55): - log.Printf("Request %s Timed Out", msg.Header.MsgId) - w.WriteHeader(http.StatusGatewayTimeout) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode("Request Timed Out") - return - } -} - -func (a *Api) deviceCreateMsg(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - sn := vars["sn"] - a.deviceExists(sn, w) - - var receiver usp_msg.Add - - err := json.NewDecoder(r.Body).Decode(&receiver) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - msg := utils.NewCreateMsg(receiver) - encodedMsg, err := proto.Marshal(&msg) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - record := utils.NewUspRecord(encodedMsg, sn) - tr369Message, err := proto.Marshal(&record) - if err != nil { - log.Fatalln("Failed to encode tr369 record:", err) - } - - //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) - a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) - log.Println("Sending Msg:", msg.Header.MsgId) - a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) - - select { - case msg := <-a.MsgQueue[msg.Header.MsgId]: - log.Printf("Received Msg: %s", msg.Header.MsgId) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode(msg.Body.GetResponse().GetAddResp()) - return - case <-time.After(time.Second * 55): - log.Printf("Request %s Timed Out", msg.Header.MsgId) - w.WriteHeader(http.StatusGatewayTimeout) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode("Request Timed Out") - return - } -} - -func (a *Api) deviceGetMsg(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - sn := vars["sn"] - - a.deviceExists(sn, w) - - var receiver usp_msg.Get - - err := json.NewDecoder(r.Body).Decode(&receiver) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - msg := utils.NewGetMsg(receiver) - encodedMsg, err := proto.Marshal(&msg) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - record := utils.NewUspRecord(encodedMsg, sn) - tr369Message, err := proto.Marshal(&record) - if err != nil { - log.Fatalln("Failed to encode tr369 record:", err) - } - - a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) - - log.Println("Sending Msg:", msg.Header.MsgId) - a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) - - select { - case msg := <-a.MsgQueue[msg.Header.MsgId]: - log.Printf("Received Msg: %s", msg.Header.MsgId) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetResp()) - return - case <-time.After(time.Second * 55): - log.Printf("Request %s Timed Out", msg.Header.MsgId) - w.WriteHeader(http.StatusGatewayTimeout) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode("Request Timed Out") - return - } -} - -func (a *Api) deviceDeleteMsg(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - sn := vars["sn"] - a.deviceExists(sn, w) - - var receiver usp_msg.Delete - - err := json.NewDecoder(r.Body).Decode(&receiver) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - msg := utils.NewDelMsg(receiver) - encodedMsg, err := proto.Marshal(&msg) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - record := utils.NewUspRecord(encodedMsg, sn) - tr369Message, err := proto.Marshal(&record) - if err != nil { - log.Fatalln("Failed to encode tr369 record:", err) - } - - //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) - a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) - log.Println("Sending Msg:", msg.Header.MsgId) - a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) - - select { - case msg := <-a.MsgQueue[msg.Header.MsgId]: - log.Printf("Received Msg: %s", msg.Header.MsgId) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode(msg.Body.GetResponse().GetDeleteResp()) - return - case <-time.After(time.Second * 55): - log.Printf("Request %s Timed Out", msg.Header.MsgId) - w.WriteHeader(http.StatusGatewayTimeout) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode("Request Timed Out") - return - } -} - -func (a *Api) deviceUpdateMsg(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - sn := vars["sn"] - a.deviceExists(sn, w) - - var receiver usp_msg.Set - - err := json.NewDecoder(r.Body).Decode(&receiver) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - msg := utils.NewSetMsg(receiver) - encodedMsg, err := proto.Marshal(&msg) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - record := utils.NewUspRecord(encodedMsg, sn) - tr369Message, err := proto.Marshal(&record) - if err != nil { - log.Fatalln("Failed to encode tr369 record:", err) - } - - //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) - a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) - log.Println("Sending Msg:", msg.Header.MsgId) - a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) - - select { - case msg := <-a.MsgQueue[msg.Header.MsgId]: - log.Printf("Received Msg: %s", msg.Header.MsgId) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode(msg.Body.GetResponse().GetSetResp()) - return - case <-time.After(time.Second * 55): - log.Printf("Request %s Timed Out", msg.Header.MsgId) - w.WriteHeader(http.StatusGatewayTimeout) - delete(a.MsgQueue, msg.Header.MsgId) - log.Println("requests queue:", a.MsgQueue) - json.NewEncoder(w).Encode("Request Timed Out") - return - } -} - -func (a *Api) deviceExists(sn string, w http.ResponseWriter) { - _, err := a.Db.RetrieveDevice(sn) - if err != nil { - if err == mongo.ErrNoDocuments { - w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode("No device with serial number " + sn + " was found") - return - } - w.WriteHeader(http.StatusInternalServerError) - return - } -} - -func (a *Api) registerUser(w http.ResponseWriter, r *http.Request) { - - tokenString := r.Header.Get("Authorization") - if tokenString == "" { - w.WriteHeader(http.StatusUnauthorized) - return - } - email, err := auth.ValidateToken(tokenString) - if err != nil { - w.WriteHeader(http.StatusUnauthorized) - return - } - - //Check if user which is requesting creation has the necessary privileges - rUser, err := a.Db.FindUser(email) - if rUser.Level != AdminUser { - w.WriteHeader(http.StatusForbidden) - return - } - - var user db.User - err = json.NewDecoder(r.Body).Decode(&user) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - - user.Level = NormalUser - - if err := user.HashPassword(user.Password); err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - if err := a.Db.RegisterUser(user); err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } -} - -func (a *Api) registerAdminUser(w http.ResponseWriter, r *http.Request) { - - var user db.User - err := json.NewDecoder(r.Body).Decode(&user) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - - users, err := a.Db.FindAllUsers() - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - adminExists := adminUserExists(users) - if adminExists { - log.Println("There might exist only one admin") - w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode("There might exist only one admin") - return - } - - user.Level = AdminUser - - if err := user.HashPassword(user.Password); err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - if err := a.Db.RegisterUser(user); err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } -} - -func adminUserExists(users []map[string]interface{}) bool { - for _, x := range users { - if x["level"].(int32) == AdminUser { - log.Println("Admin exists") - return true - } - } - return false -} - -func (a *Api) adminUserExists(w http.ResponseWriter, r *http.Request) { - - users, err := a.Db.FindAllUsers() - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - adminExits := adminUserExists(users) - json.NewEncoder(w).Encode(adminExits) - return -} - -type TokenRequest struct { - Email string `json:"email"` - Password string `json:"password"` -} - -func (a *Api) generateToken(w http.ResponseWriter, r *http.Request) { - var tokenReq TokenRequest - - err := json.NewDecoder(r.Body).Decode(&tokenReq) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - - user, err := a.Db.FindUser(tokenReq.Email) - if err != nil { - w.WriteHeader(http.StatusUnauthorized) - json.NewEncoder(w).Encode("Invalid Credentials") - return - } - - credentialError := user.CheckPassword(tokenReq.Password) - if credentialError != nil { - w.WriteHeader(http.StatusUnauthorized) - json.NewEncoder(w).Encode("Invalid Credentials") - return - } - - token, err := auth.GenerateJWT(user.Email, user.Name) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - w.Header().Add("Content-Type", "application/json") - json.NewEncoder(w).Encode(token) - return -} diff --git a/backend/services/controller/internal/api/device.go b/backend/services/controller/internal/api/device.go new file mode 100644 index 0000000..5d4d135 --- /dev/null +++ b/backend/services/controller/internal/api/device.go @@ -0,0 +1,510 @@ +package api + +import ( + "encoding/json" + "log" + "net/http" + "time" + + "github.com/gorilla/mux" + usp_msg "github.com/leandrofars/oktopus/internal/usp_message" + "github.com/leandrofars/oktopus/internal/utils" + "go.mongodb.org/mongo-driver/mongo" + "google.golang.org/protobuf/proto" +) + +func (a *Api) deviceFwUpdate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + sn := vars["sn"] + a.deviceExists(sn, w) + + msg := utils.NewGetMsg(usp_msg.Get{ + ParamPaths: []string{"Device.DeviceInfo.FirmwareImage.*.Status"}, + MaxDepth: 1, + }) + encodedMsg, err := proto.Marshal(&msg) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + record := utils.NewUspRecord(encodedMsg, sn) + tr369Message, err := proto.Marshal(&record) + if err != nil { + log.Fatalln("Failed to encode tr369 record:", err) + } + + a.QMutex.Lock() + a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) + a.QMutex.Unlock() + log.Println("Sending Msg:", msg.Header.MsgId) + a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) + + var getMsgAnswer *usp_msg.GetResp + + select { + case msg := <-a.MsgQueue[msg.Header.MsgId]: + log.Printf("Received Msg: %s", msg.Header.MsgId) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + getMsgAnswer = msg.Body.GetResponse().GetGetResp() + case <-time.After(time.Second * 55): + log.Printf("Request %s Timed Out", msg.Header.MsgId) + w.WriteHeader(http.StatusGatewayTimeout) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode("Request Timed Out") + return + } + + // Check which fw image is activated + partition := checkAvaiableFwPartition(getMsgAnswer.ReqPathResults) + if partition < 0 { + log.Println("Error to get device available firmware partition, probably it has only one partition") + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode("Server don't have the hability to update device with only one partition") + return + //TODO: update device with only one partition + } + + var receiver = usp_msg.Operate{ + Command: "Device.DeviceInfo.FirmwareImage.1.Download()", + CommandKey: "Download()", + SendResp: true, + InputArgs: map[string]string{ + "URL": "http://cronos.intelbras.com.br/download/PON/121AC/beta/121AC-2.3-230620-77753201df4f1e2c607a7236746c8491.tar", //TODO: use dynamic url + "AutoActivate": "true", + //"Username": "", + //"Password": "", + "FileSize": "0", //TODO: send firmware length + //"CheckSumAlgorithm": "", + //"CheckSum": "", + }, + } + + msg = utils.NewOperateMsg(receiver) + encodedMsg, err = proto.Marshal(&msg) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + record = utils.NewUspRecord(encodedMsg, sn) + tr369Message, err = proto.Marshal(&record) + if err != nil { + log.Fatalln("Failed to encode tr369 record:", err) + } + + a.QMutex.Lock() + a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) + a.QMutex.Unlock() + log.Println("Sending Msg:", msg.Header.MsgId) + a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) + + select { + case msg := <-a.MsgQueue[msg.Header.MsgId]: + log.Printf("Received Msg: %s", msg.Header.MsgId) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode(msg.Body.GetResponse().GetSetResp()) + return + case <-time.After(time.Second * 55): + log.Printf("Request %s Timed Out", msg.Header.MsgId) + w.WriteHeader(http.StatusGatewayTimeout) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode("Request Timed Out") + return + } +} + +// Check which fw image is activated +func checkAvaiableFwPartition(reqPathResult []*usp_msg.GetResp_RequestedPathResult) int { + for _, x := range reqPathResult { + partitionsNumber := len(x.ResolvedPathResults) + if partitionsNumber > 1 { + log.Printf("Device has %d firmware partitions", partitionsNumber) + } + for i, y := range x.ResolvedPathResults { + if y.ResultParams["Status"] == "Available" { + log.Printf("Partition %d is avaiable", i) + return i + } + } + } + return -1 +} + +func (a *Api) deviceGetSupportedParametersMsg(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + sn := vars["sn"] + a.deviceExists(sn, w) + + var receiver usp_msg.GetSupportedDM + + err := json.NewDecoder(r.Body).Decode(&receiver) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + msg := utils.NewGetSupportedParametersMsg(receiver) + encodedMsg, err := proto.Marshal(&msg) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + record := utils.NewUspRecord(encodedMsg, sn) + tr369Message, err := proto.Marshal(&record) + if err != nil { + log.Fatalln("Failed to encode tr369 record:", err) + } + + //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) + a.QMutex.Lock() + a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) + a.QMutex.Unlock() + log.Println("Sending Msg:", msg.Header.MsgId) + a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) + + select { + case msg := <-a.MsgQueue[msg.Header.MsgId]: + log.Printf("Received Msg: %s", msg.Header.MsgId) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetSupportedDmResp()) + return + case <-time.After(time.Second * 55): + log.Printf("Request %s Timed Out", msg.Header.MsgId) + w.WriteHeader(http.StatusGatewayTimeout) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode("Request Timed Out") + return + } +} + +func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) { + devices, err := a.Db.RetrieveDevices() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + err = json.NewEncoder(w).Encode(devices) + if err != nil { + log.Println(err) + } + + return +} + +func (a *Api) deviceCreateMsg(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + sn := vars["sn"] + a.deviceExists(sn, w) + + var receiver usp_msg.Add + + err := json.NewDecoder(r.Body).Decode(&receiver) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + msg := utils.NewCreateMsg(receiver) + encodedMsg, err := proto.Marshal(&msg) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + record := utils.NewUspRecord(encodedMsg, sn) + tr369Message, err := proto.Marshal(&record) + if err != nil { + log.Fatalln("Failed to encode tr369 record:", err) + } + + //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) + a.QMutex.Lock() + a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) + a.QMutex.Unlock() + log.Println("Sending Msg:", msg.Header.MsgId) + a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) + + select { + case msg := <-a.MsgQueue[msg.Header.MsgId]: + log.Printf("Received Msg: %s", msg.Header.MsgId) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode(msg.Body.GetResponse().GetAddResp()) + return + case <-time.After(time.Second * 55): + log.Printf("Request %s Timed Out", msg.Header.MsgId) + w.WriteHeader(http.StatusGatewayTimeout) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode("Request Timed Out") + return + } +} + +func (a *Api) deviceGetMsg(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + sn := vars["sn"] + + a.deviceExists(sn, w) + + var receiver usp_msg.Get + + err := json.NewDecoder(r.Body).Decode(&receiver) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + msg := utils.NewGetMsg(receiver) + encodedMsg, err := proto.Marshal(&msg) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + record := utils.NewUspRecord(encodedMsg, sn) + tr369Message, err := proto.Marshal(&record) + if err != nil { + log.Fatalln("Failed to encode tr369 record:", err) + } + + a.QMutex.Lock() + a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) + a.QMutex.Unlock() + + log.Println("Sending Msg:", msg.Header.MsgId) + a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) + + select { + case msg := <-a.MsgQueue[msg.Header.MsgId]: + log.Printf("Received Msg: %s", msg.Header.MsgId) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetResp()) + return + case <-time.After(time.Second * 55): + log.Printf("Request %s Timed Out", msg.Header.MsgId) + w.WriteHeader(http.StatusGatewayTimeout) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode("Request Timed Out") + return + } +} + +func (a *Api) deviceDeleteMsg(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + sn := vars["sn"] + a.deviceExists(sn, w) + + var receiver usp_msg.Delete + + err := json.NewDecoder(r.Body).Decode(&receiver) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + msg := utils.NewDelMsg(receiver) + encodedMsg, err := proto.Marshal(&msg) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + record := utils.NewUspRecord(encodedMsg, sn) + tr369Message, err := proto.Marshal(&record) + if err != nil { + log.Fatalln("Failed to encode tr369 record:", err) + } + + //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) + a.QMutex.Lock() + a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) + a.QMutex.Unlock() + log.Println("Sending Msg:", msg.Header.MsgId) + a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) + + select { + case msg := <-a.MsgQueue[msg.Header.MsgId]: + log.Printf("Received Msg: %s", msg.Header.MsgId) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode(msg.Body.GetResponse().GetDeleteResp()) + return + case <-time.After(time.Second * 55): + log.Printf("Request %s Timed Out", msg.Header.MsgId) + w.WriteHeader(http.StatusGatewayTimeout) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode("Request Timed Out") + return + } +} + +func (a *Api) deviceUpdateMsg(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + sn := vars["sn"] + a.deviceExists(sn, w) + + var receiver usp_msg.Set + + err := json.NewDecoder(r.Body).Decode(&receiver) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + msg := utils.NewSetMsg(receiver) + encodedMsg, err := proto.Marshal(&msg) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + record := utils.NewUspRecord(encodedMsg, sn) + tr369Message, err := proto.Marshal(&record) + if err != nil { + log.Fatalln("Failed to encode tr369 record:", err) + } + + //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) + a.QMutex.Lock() + a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) + a.QMutex.Unlock() + log.Println("Sending Msg:", msg.Header.MsgId) + a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) + + select { + case msg := <-a.MsgQueue[msg.Header.MsgId]: + log.Printf("Received Msg: %s", msg.Header.MsgId) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode(msg.Body.GetResponse().GetSetResp()) + return + case <-time.After(time.Second * 55): + log.Printf("Request %s Timed Out", msg.Header.MsgId) + w.WriteHeader(http.StatusGatewayTimeout) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode("Request Timed Out") + return + } +} + +func (a *Api) deviceExists(sn string, w http.ResponseWriter) { + _, err := a.Db.RetrieveDevice(sn) + if err != nil { + if err == mongo.ErrNoDocuments { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode("No device with serial number " + sn + " was found") + return + } + w.WriteHeader(http.StatusInternalServerError) + return + } +} + +func (a *Api) deviceGetParameterInstances(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + sn := vars["sn"] + a.deviceExists(sn, w) + + var receiver usp_msg.GetInstances + + err := json.NewDecoder(r.Body).Decode(&receiver) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + msg := utils.NewGetParametersInstancesMsg(receiver) + encodedMsg, err := proto.Marshal(&msg) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + record := utils.NewUspRecord(encodedMsg, sn) + tr369Message, err := proto.Marshal(&record) + if err != nil { + log.Fatalln("Failed to encode tr369 record:", err) + } + + a.QMutex.Lock() + a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) + a.QMutex.Unlock() + log.Println("Sending Msg:", msg.Header.MsgId) + a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) + + select { + case msg := <-a.MsgQueue[msg.Header.MsgId]: + log.Printf("Received Msg: %s", msg.Header.MsgId) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetInstancesResp()) + return + case <-time.After(time.Second * 55): + log.Printf("Request %s Timed Out", msg.Header.MsgId) + w.WriteHeader(http.StatusGatewayTimeout) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode("Request Timed Out") + return + } +} diff --git a/backend/services/controller/internal/api/user.go b/backend/services/controller/internal/api/user.go new file mode 100644 index 0000000..916047f --- /dev/null +++ b/backend/services/controller/internal/api/user.go @@ -0,0 +1,167 @@ +package api + +import ( + "encoding/json" + "log" + "net/http" + + "github.com/leandrofars/oktopus/internal/api/auth" + "github.com/leandrofars/oktopus/internal/db" +) + +func (a *Api) retrieveUsers(w http.ResponseWriter, r *http.Request) { + users, err := a.Db.FindAllUsers() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + for _, x := range users { + delete(x, "password") + } + + err = json.NewEncoder(w).Encode(users) + if err != nil { + log.Println(err) + } + return +} + +func (a *Api) registerUser(w http.ResponseWriter, r *http.Request) { + + tokenString := r.Header.Get("Authorization") + if tokenString == "" { + w.WriteHeader(http.StatusUnauthorized) + return + } + email, err := auth.ValidateToken(tokenString) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + return + } + + //Check if user which is requesting creation has the necessary privileges + rUser, err := a.Db.FindUser(email) + if rUser.Level != AdminUser { + w.WriteHeader(http.StatusForbidden) + return + } + + var user db.User + err = json.NewDecoder(r.Body).Decode(&user) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + user.Level = NormalUser + + if err := user.HashPassword(user.Password); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + if err := a.Db.RegisterUser(user); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } +} + +func (a *Api) registerAdminUser(w http.ResponseWriter, r *http.Request) { + + var user db.User + err := json.NewDecoder(r.Body).Decode(&user) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + users, err := a.Db.FindAllUsers() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + adminExists := adminUserExists(users) + if adminExists { + log.Println("There might exist only one admin") + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode("There might exist only one admin") + return + } + + user.Level = AdminUser + + if err := user.HashPassword(user.Password); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + if err := a.Db.RegisterUser(user); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } +} + +func adminUserExists(users []map[string]interface{}) bool { + for _, x := range users { + if x["level"].(int32) == AdminUser { + log.Println("Admin exists") + return true + } + } + return false +} + +func (a *Api) adminUserExists(w http.ResponseWriter, r *http.Request) { + + users, err := a.Db.FindAllUsers() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + adminExits := adminUserExists(users) + json.NewEncoder(w).Encode(adminExits) + return +} + +type TokenRequest struct { + Email string `json:"email"` + Password string `json:"password"` +} + +func (a *Api) generateToken(w http.ResponseWriter, r *http.Request) { + var tokenReq TokenRequest + + err := json.NewDecoder(r.Body).Decode(&tokenReq) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + user, err := a.Db.FindUser(tokenReq.Email) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(w).Encode("Invalid Credentials") + return + } + + credentialError := user.CheckPassword(tokenReq.Password) + if credentialError != nil { + w.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(w).Encode("Invalid Credentials") + return + } + + token, err := auth.GenerateJWT(user.Email, user.Name) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Add("Content-Type", "application/json") + json.NewEncoder(w).Encode(token) + return +} diff --git a/backend/services/controller/internal/api/wifi.go b/backend/services/controller/internal/api/wifi.go new file mode 100644 index 0000000..262a85b --- /dev/null +++ b/backend/services/controller/internal/api/wifi.go @@ -0,0 +1,150 @@ +package api + +import ( + "encoding/json" + "log" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gorilla/mux" + + usp_msg "github.com/leandrofars/oktopus/internal/usp_message" + "github.com/leandrofars/oktopus/internal/utils" + "google.golang.org/protobuf/proto" +) + +func (a *Api) deviceWifi(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + sn := vars["sn"] + a.deviceExists(sn, w) + + if r.Method == http.MethodGet { + msg := utils.NewGetMsg(usp_msg.Get{ + ParamPaths: []string{ + "Device.WiFi.SSID.[Enable==true].SSID", + //"Device.WiFi.AccessPoint.[Enable==true].SSIDReference", + "Device.WiFi.AccessPoint.[Enable==true].Security.ModeEnabled", + "Device.WiFi.AccessPoint.[Enable==true].Security.ModesSupported", + //"Device.WiFi.EndPoint.[Enable==true].", + "Device.WiFi.Radio.[Enable==true].AutoChannelEnable", + "Device.WiFi.Radio.[Enable==true].Channel", + "Device.WiFi.Radio.[Enable==true].CurrentOperatingChannelBandwidth", + "Device.WiFi.Radio.[Enable==true].OperatingFrequencyBand", + //"Device.WiFi.Radio.[Enable==true].PossibleChannels", + "Device.WiFi.Radio.[Enable==true].SupportedOperatingChannelBandwidths", + }, + MaxDepth: 2, + }) + + encodedMsg, err := proto.Marshal(&msg) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + record := utils.NewUspRecord(encodedMsg, sn) + tr369Message, err := proto.Marshal(&record) + if err != nil { + log.Fatalln("Failed to encode tr369 record:", err) + } + + //a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn) + a.QMutex.Lock() + a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg) + a.QMutex.Unlock() + log.Println("Sending Msg:", msg.Header.MsgId) + a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn, false) + + //TODO: verify in protocol and in other models, the Device.Wifi parameters. Maybe in the future, to use SSIDReference from AccessPoint + select { + case msg := <-a.MsgQueue[msg.Header.MsgId]: + log.Printf("Received Msg: %s", msg.Header.MsgId) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + answer := msg.Body.GetResponse().GetGetResp() + + var wifi [2]WiFi + + //TODO: better algorithm, might use something faster an more reliable + //TODO: full fill the commented wifi resources + for _, x := range answer.ReqPathResults { + if x.RequestedPath == "Device.WiFi.SSID.[Enable==true].SSID" { + for i, y := range x.ResolvedPathResults { + wifi[i].SSID = y.ResultParams["SSID"] + } + continue + } + if x.RequestedPath == "Device.WiFi.AccessPoint.[Enable==true].Security.ModeEnabled" { + for i, y := range x.ResolvedPathResults { + wifi[i].Security = y.ResultParams["Security.ModeEnabled"] + } + continue + } + if x.RequestedPath == "Device.WiFi.AccessPoint.[Enable==true].Security.ModesSupported" { + for i, y := range x.ResolvedPathResults { + wifi[i].SecurityCapabilities = strings.Split(y.ResultParams["Security.ModesSupported"], ",") + } + continue + } + if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].AutoChannelEnable" { + for i, y := range x.ResolvedPathResults { + autoChannel, err := strconv.ParseBool(y.ResultParams["AutoChannelEnable"]) + if err != nil { + log.Println(err) + wifi[i].AutoChannelEnable = false + } else { + wifi[i].AutoChannelEnable = autoChannel + } + } + continue + } + if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].Channel" { + for i, y := range x.ResolvedPathResults { + channel, err := strconv.Atoi(y.ResultParams["Channel"]) + if err != nil { + log.Println(err) + wifi[i].Channel = -1 + } else { + wifi[i].Channel = channel + } + } + continue + } + if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].CurrentOperatingChannelBandwidth" { + for i, y := range x.ResolvedPathResults { + wifi[i].ChannelBandwidth = y.ResultParams["CurrentOperatingChannelBandwidth"] + } + continue + } + if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].OperatingFrequencyBand" { + for i, y := range x.ResolvedPathResults { + wifi[i].FrequencyBand = y.ResultParams["OperatingFrequencyBand"] + } + continue + } + if x.RequestedPath == "Device.WiFi.Radio.[Enable==true].SupportedOperatingChannelBandwidths" { + for i, y := range x.ResolvedPathResults { + wifi[i].SupportedChannelBandwidths = strings.Split(y.ResultParams["SupportedOperatingChannelBandwidths"], ",") + } + continue + } + } + json.NewEncoder(w).Encode(&wifi) + return + case <-time.After(time.Second * 45): + log.Printf("Request %s Timed Out", msg.Header.MsgId) + w.WriteHeader(http.StatusGatewayTimeout) + a.QMutex.Lock() + delete(a.MsgQueue, msg.Header.MsgId) + a.QMutex.Unlock() + log.Println("requests queue:", a.MsgQueue) + json.NewEncoder(w).Encode("Request Timed Out") + return + } + } +} From 9b87096a33d2728c0c521fedd1b3e145cc6c3dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leandro=20Ant=C3=B4nio=20Farias=20Machado?= Date: Mon, 4 Sep 2023 23:03:17 -0300 Subject: [PATCH 4/5] feat( params discovery ): get params value --- .../src/sections/devices/devices-discovery.js | 89 ++++++++++++++++--- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/frontend/src/sections/devices/devices-discovery.js b/frontend/src/sections/devices/devices-discovery.js index e175a58..88b9c9b 100644 --- a/frontend/src/sections/devices/devices-discovery.js +++ b/frontend/src/sections/devices/devices-discovery.js @@ -7,16 +7,11 @@ import { List, ListItem, ListItemText, - Collapse, Box, - Tabs, - Tab, - ListItemIcon, - ListItemAvatar, - Avatar } from '@mui/material'; import CircularProgress from '@mui/material/CircularProgress'; import PlusCircleIcon from '@heroicons/react/24/outline/PlusCircleIcon'; +import ArrowUturnLeftIcon from '@heroicons/react/24/outline/ArrowUturnLeftIcon' import { useRouter } from 'next/router'; @@ -25,6 +20,7 @@ export const DevicesDiscovery = () => { const router = useRouter() const [deviceParameters, setDeviceParameters] = useState(null) +const [deviceParametersValue, setDeviceParametersValue] = useState([]) const initialize = async (raw) => { let content = await getDeviceParameters(raw) @@ -57,8 +53,8 @@ const getDeviceParameters = async (raw) =>{ JSON.stringify({ "obj_paths": ["Device."], "first_level_only" : true, - "return_commands" : false, - "return_events" : false, + "return_commands" : true, + "return_events" : true, "return_params" : true }) ); @@ -99,13 +95,65 @@ const getDeviceParameters = async (raw) =>{ let content = await getDeviceParameters(raw) - console.log(content) + console.log("content:",content) setDeviceParameters(content) + + let supportedParams = content.req_obj_results[0].supported_objs[0].supported_params + let parametersToFetch = () => { + let paramsToFetch = [] + for (let i =0; i < supportedParams.length ;i++){ + paramsToFetch.push(content.req_obj_results[0].supported_objs[0].supported_obj_path+supportedParams[i].param_name) + } + return paramsToFetch + } + + if (supportedParams !== undefined) { + const fetchparameters = parametersToFetch() + console.log("parameters to fetch: ", fetchparameters) + + raw = JSON.stringify({ + "param_paths": fetchparameters, + "max_depth": 1 + }) + + let result = await getDeviceParametersValue(raw) + console.log("result:", result) + + let values = [] + let setValues = result.req_path_results.map((x)=>{ + let path = x.requested_path.split(".") + let param = path[path.length -1] + return values.push(x.resolved_path_results[0].result_params[param]) + }) + console.log(values) + setDeviceParametersValue(values) + } + } + + const getDeviceParametersValue = async (raw) => { + + var myHeaders = new Headers(); + myHeaders.append("Content-Type", "application/json"); + myHeaders.append("Authorization", localStorage.getItem("token")); + + var requestOptions = { + method: 'PUT', + headers: myHeaders, + redirect: 'follow', + body: raw + }; + + let result = await (await fetch(`${process.env.NEXT_PUBLIC_REST_ENPOINT}/device/${router.query.id[0]}/get`, requestOptions)) + if (result.status != 200) { + throw new Error('Please check your email and password'); + }else{ + return result.json() + } + } const showParameters = () => { - console.log(deviceParameters) return deviceParameters.req_obj_results[0].supported_objs.map((x,i)=> { return ( @@ -113,9 +161,25 @@ const getDeviceParameters = async (raw) =>{ key={x.supported_obj_path} divider={true} secondaryAction={ + i == 0 && x.supported_obj_path != "Device." ? + + { + let paths = x.supported_obj_path.split(".") + console.log(paths) + updateDeviceParameters(paths[paths.length -3]+".") + } + }> + + + + + : updateDeviceParameters(x.supported_obj_path)}> + { + x.supported_obj_path != "Device." && + } } @@ -129,7 +193,7 @@ const getDeviceParameters = async (raw) =>{ /> { x.supported_params && - x.supported_params.map((y)=>{ + x.supported_params.map((y, index)=>{ return { boxShadow: 'rgba(149, 157, 165, 0.2) 0px 0px 5px;', pl: 4 }} + secondaryAction={ +
{deviceParametersValue[index]}
+ } > Date: Mon, 4 Sep 2023 23:03:50 -0300 Subject: [PATCH 5/5] chore: comment chat feature --- frontend/src/layouts/dashboard/config.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/layouts/dashboard/config.js b/frontend/src/layouts/dashboard/config.js index a3bfa19..9634ba8 100644 --- a/frontend/src/layouts/dashboard/config.js +++ b/frontend/src/layouts/dashboard/config.js @@ -24,15 +24,15 @@ export const items = [ ) }, - { - title: 'Chat (beta)', - path: '/chat', - icon: ( - - - - ) - }, + // { + // title: 'Chat (beta)', + // path: '/chat', + // icon: ( + // + // + // + // ) + // }, { title: 'Map', path: '/map',