From fa1af141e47ac2737cfcb719487f782043e15f8b Mon Sep 17 00:00:00 2001 From: leandrofars Date: Mon, 23 Oct 2023 23:37:38 -0300 Subject: [PATCH] feat: http file server --- .../controller/internal/api/fw_update.go | 126 ++++++++++++++++++ backend/services/http_file_server/.env | 2 + backend/services/http_file_server/.gitignore | 2 + backend/services/http_file_server/go.mod | 5 + backend/services/http_file_server/go.sum | 2 + backend/services/http_file_server/main.go | 50 +++++++ 6 files changed, 187 insertions(+) create mode 100644 backend/services/controller/internal/api/fw_update.go create mode 100644 backend/services/http_file_server/.env create mode 100644 backend/services/http_file_server/.gitignore create mode 100644 backend/services/http_file_server/go.mod create mode 100644 backend/services/http_file_server/go.sum create mode 100644 backend/services/http_file_server/main.go diff --git a/backend/services/controller/internal/api/fw_update.go b/backend/services/controller/internal/api/fw_update.go new file mode 100644 index 0000000..bac2134 --- /dev/null +++ b/backend/services/controller/internal/api/fw_update.go @@ -0,0 +1,126 @@ +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" + "google.golang.org/protobuf/proto" +) + +type FwUpdate struct { + Url string +} + +func (a *Api) deviceFwUpdate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + sn := vars["sn"] + a.deviceExists(sn, w) + + var payload FwUpdate + + err := json.NewDecoder(r.Body).Decode(&payload) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode("Bad body, err: " + err.Error()) + return + } + + 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(REQUEST_TIMEOUT): + 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 + } + + partition := checkAvaiableFwPartition(getMsgAnswer.ReqPathResults) + if partition == "" { + 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 + } + + log.Println("URL to download firmware:", payload.Url) + + receiver := usp_msg.Operate{ + Command: "Device.DeviceInfo.FirmwareImage." + partition + "Download()", + CommandKey: "Download()", + SendResp: true, + InputArgs: map[string]string{ + "URL": payload.Url, + "AutoActivate": "true", + //"Username": "", + //"Password": "", + "FileSize": "0", //TODO: send firmware length + //"CheckSumAlgorithm": "", + //"CheckSum": "", //TODO: send firmware with checksum + }, + } + + msg = utils.NewOperateMsg(receiver) + a.uspCall(msg, sn, w) +} + +// Check which fw image is activated +func checkAvaiableFwPartition(reqPathResult []*usp_msg.GetResp_RequestedPathResult) string { + for _, x := range reqPathResult { + partitionsNumber := len(x.ResolvedPathResults) + if partitionsNumber > 1 { + log.Printf("Device has %d firmware partitions", partitionsNumber) + for _, y := range x.ResolvedPathResults { + //TODO: verify if validation failed is trustable + if y.ResultParams["Status"] == "Available" || y.ResultParams["Status"] == "ValidationFailed" { + partition := y.ResolvedPath[len(y.ResolvedPath)-2:] + log.Printf("Partition %s is avaiable", partition) + return partition + } + } + } else { + return "" + } + } + return "" +} diff --git a/backend/services/http_file_server/.env b/backend/services/http_file_server/.env new file mode 100644 index 0000000..8eb288c --- /dev/null +++ b/backend/services/http_file_server/.env @@ -0,0 +1,2 @@ +DIRECTORY_PATH="./firmwares" +SERVER_PORT=":8004" \ No newline at end of file diff --git a/backend/services/http_file_server/.gitignore b/backend/services/http_file_server/.gitignore new file mode 100644 index 0000000..0835f35 --- /dev/null +++ b/backend/services/http_file_server/.gitignore @@ -0,0 +1,2 @@ +firmwares/ +.env.local \ No newline at end of file diff --git a/backend/services/http_file_server/go.mod b/backend/services/http_file_server/go.mod new file mode 100644 index 0000000..b286dc5 --- /dev/null +++ b/backend/services/http_file_server/go.mod @@ -0,0 +1,5 @@ +module github.com/leandrofars/oktopus/http_file_server + +go 1.21.3 + +require github.com/joho/godotenv v1.5.1 // indirect diff --git a/backend/services/http_file_server/go.sum b/backend/services/http_file_server/go.sum new file mode 100644 index 0000000..d61b19e --- /dev/null +++ b/backend/services/http_file_server/go.sum @@ -0,0 +1,2 @@ +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= diff --git a/backend/services/http_file_server/main.go b/backend/services/http_file_server/main.go new file mode 100644 index 0000000..82e7705 --- /dev/null +++ b/backend/services/http_file_server/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + + "github.com/joho/godotenv" +) + +func main() { + + err := godotenv.Load() + + localEnv := ".env.local" + + if _, err := os.Stat(localEnv); err == nil { + _ = godotenv.Overload(localEnv) + log.Println("Loaded variables from '.env.local'") + } else { + log.Println("Loaded variables from '.env'") + } + + if err != nil { + log.Println("Error to load environment variables:", err) + } + + directoryPath := os.Getenv("DIRECTORY_PATH") + + // Check if the directory exists + _, err = os.Stat(directoryPath) + if os.IsNotExist(err) { + fmt.Printf("Directory '%s' not found.\n", directoryPath) + return + } + + // Create a file server handler to serve the directory's contents + fileServer := http.FileServer(http.Dir(directoryPath)) + + // Create a new HTTP server and handle requests + http.Handle("/", fileServer) + + port := os.Getenv("SERVER_PORT") + fmt.Printf("Server started at http://localhost:%s\n", port) + err = http.ListenAndServe(port, nil) + if err != nil { + fmt.Printf("Error starting server: %s\n", err) + } +}