diff --git a/backend/.gitignore b/backend/.gitignore index 384729b1f..3ab18830c 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -19,4 +19,6 @@ c.out cmd/logger/ cmd/cmd cmd/adj -/logger/ \ No newline at end of file +/logger/ + +.env diff --git a/backend/cmd/main.go b/backend/cmd/main.go index b31a17d63..705687cd7 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -13,6 +13,7 @@ import ( adj_module "github.com/HyperloopUPV-H8/h9-backend/pkg/adj" "github.com/HyperloopUPV-H8/h9-backend/pkg/transport" "github.com/HyperloopUPV-H8/h9-backend/pkg/websocket" + "github.com/joho/godotenv" trace "github.com/rs/zerolog/log" ) @@ -28,6 +29,9 @@ const ( ) func main() { + // Load environment + godotenv.Load("../.env") + // Parse command line flags flags.Init() handleVersionFlag() @@ -143,6 +147,7 @@ func main() { <-interrupt trace.Info().Msg("shutting down backend") + } // <-- Hall of Fame --> diff --git a/backend/cmd/setup_vehicle.go b/backend/cmd/setup_vehicle.go index 8fe8999e2..2bd3b3d14 100644 --- a/backend/cmd/setup_vehicle.go +++ b/backend/cmd/setup_vehicle.go @@ -9,6 +9,7 @@ import ( "github.com/HyperloopUPV-H8/h9-backend/internal/flags" vehicle_models "github.com/HyperloopUPV-H8/h9-backend/internal/vehicle/models" + cloud_logs "github.com/HyperloopUPV-H8/h9-backend/pkg/cloud-logs" h "github.com/HyperloopUPV-H8/h9-backend/pkg/http" "github.com/HyperloopUPV-H8/h9-backend/pkg/websocket" @@ -151,6 +152,10 @@ func configureHTTPServer( fmt.Fprintf(os.Stderr, "error creating programableBoards handler: %v\n", err) } + cloud_auth := cloud_logs.CloudState{} + + cloud_auth.Login() + for _, server := range config.Server { mux := h.NewMux( h.Endpoint("/backend"+server.Endpoints.PodData, podDataHandle), @@ -158,8 +163,10 @@ func configureHTTPServer( h.Endpoint("/backend"+server.Endpoints.ProgramableBoards, programableBoardsHandle), h.Endpoint(server.Endpoints.Connections, upgrader), h.Endpoint(server.Endpoints.Files, h.HandleStatic(server.StaticPath)), + h.Endpoint("/logs/download", &cloud_logs.Downloader{Auth: &cloud_auth}), + h.Endpoint("POST /logs/upload", &cloud_logs.Uploader{Auth: &cloud_auth}), + h.Endpoint("/logs/list", &cloud_logs.Lister{Auth: &cloud_auth}), ) - httpServer := h.NewServer(server.Addr, mux) go httpServer.ListenAndServe() } diff --git a/backend/go.mod b/backend/go.mod index 2571668d5..98bd8addb 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -8,6 +8,7 @@ require ( github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.5.0 github.com/jmaralo/sntp v0.0.0-20240116111937-45a0a3419272 + github.com/joho/godotenv v1.5.1 github.com/pelletier/go-toml/v2 v2.0.7 github.com/pin/tftp/v3 v3.0.0 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c @@ -41,6 +42,4 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect ) -require ( - golang.org/x/net v0.38.0 // indirect -) +require golang.org/x/net v0.38.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index c9928069c..2db0cd449 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -23,8 +23,6 @@ github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcej github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -46,12 +44,12 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jmaralo/sntp v0.0.0-20240116111937-45a0a3419272 h1:dtQzdBn2P781UxyDPZd1tv4QE29ffpnmJ15qBkXHypY= github.com/jmaralo/sntp v0.0.0-20240116111937-45a0a3419272/go.mod h1:nA+gdd8RXyFnxlLOCu0lOLGcQuwNklQfWOUL8fAFBDI= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= diff --git a/backend/pkg/cloud-logs/cloud-utils.go b/backend/pkg/cloud-logs/cloud-utils.go new file mode 100644 index 000000000..6c8851aad --- /dev/null +++ b/backend/pkg/cloud-logs/cloud-utils.go @@ -0,0 +1,25 @@ +package cloud_logs + +type Logs struct { + Count int `json:"count"` + Logs []File `json:"logs"` +} + +type File struct { + Id int `json:"id"` + Filename string `json:"filename"` + Content_type string `json:"content:type"` + Size_bytes int `json:"size_bytes"` +} + +type Filepath struct { + Path string `json:"filepath"` +} + +type Token struct { + Token string `json:"access_token"` +} + +type Logins struct { + Password string `json:"password"` +} diff --git a/backend/pkg/cloud-logs/download-log.go b/backend/pkg/cloud-logs/download-log.go new file mode 100644 index 000000000..8be81e028 --- /dev/null +++ b/backend/pkg/cloud-logs/download-log.go @@ -0,0 +1,56 @@ +package cloud_logs + +import ( + "encoding/json" + "fmt" + "io" + "net/http" +) + +type Downloader struct { + Endpoint string + Client *http.Client + Auth *CloudState +} + +func (d *Downloader) setDefault() { + d.Endpoint = "http://localhost:8080/logs/download" + d.Client = &http.Client{} +} +func (d *Downloader) ServeHTTP(w http.ResponseWriter, req *http.Request) { + decoder := json.NewDecoder(req.Body) + d.setDefault() + chosen_file := File{} + decoder.Decode(&chosen_file) + path := fmt.Sprintf("%v/%v", d.Endpoint, chosen_file.Id) + res, err := download(d, path) + if err != nil { + w.WriteHeader(400) + } + + w.Header().Set("Content-Disposition", res.Header.Get("Content-Disposition")) + w.Header().Set("Content-Type", res.Header.Get("Content-Type")) + + io.Copy(w, res.Body) + defer res.Body.Close() + if err != nil { + w.WriteHeader(400) + } +} + +func download(d *Downloader, path string) (*http.Response, error) { + fmt.Print(path) + req, err := http.NewRequest("GET", path, nil) + if err != nil { + return nil, err + } + + req.Header.Set("Authorization", "Bearer "+d.Auth.CloudToken) + + res, err := d.Client.Do(req) + if err != nil { + return nil, err + } + + return res, nil +} diff --git a/backend/pkg/cloud-logs/list-logs.go b/backend/pkg/cloud-logs/list-logs.go new file mode 100644 index 000000000..f48c84f83 --- /dev/null +++ b/backend/pkg/cloud-logs/list-logs.go @@ -0,0 +1,53 @@ +package cloud_logs + +import ( + "encoding/json" + "net/http" +) + +type Lister struct { + Endpoint string + Client *http.Client + Auth *CloudState +} + +func (l *Lister) setDefault() { + l.Endpoint = "http://localhost:8080/logs/list" + l.Client = &http.Client{} +} + +func (l *Lister) ServeHTTP(w http.ResponseWriter, req *http.Request) { + l.setDefault() + list, err := get_logs_list(l) + if err != nil { + w.WriteHeader(400) + } + + jsonData, _ := json.Marshal(list) + w.Write(jsonData) +} + +func get_logs_list(l *Lister) (Logs, error) { + + log_list := Logs{} + req, err := http.NewRequest("GET", l.Endpoint, nil) + if err != nil { + return log_list, err + } + + req.Header.Set("Authorization", "Bearer "+l.Auth.CloudToken) + + res, err := l.Client.Do(req) + if err != nil { + return log_list, err + } + + decoder := json.NewDecoder(res.Body) + err = decoder.Decode(&log_list) + if err != nil { + return log_list, err + } + defer res.Body.Close() + + return log_list, nil +} diff --git a/backend/pkg/cloud-logs/login.go b/backend/pkg/cloud-logs/login.go new file mode 100644 index 000000000..1a99812db --- /dev/null +++ b/backend/pkg/cloud-logs/login.go @@ -0,0 +1,55 @@ +package cloud_logs + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "os" +) + +type CloudState struct { + CloudHash string + CloudSecret string + CloudToken string +} + +func (state *CloudState) Login() error { + + state.CloudHash = os.Getenv("AUTH_PASSWORD_HASH") + state.CloudSecret = os.Getenv("JWT_SECRET") + + login := Logins{ + Password: state.CloudSecret, + } + + body, err := json.Marshal(login) + if err != nil { + return fmt.Errorf(err.Error()) + } + + req, err := http.NewRequest("POST", "http://localhost:8080/auth/login", bytes.NewBuffer(body)) + if err != nil { + return fmt.Errorf(err.Error()) + } + client := &http.Client{} + req.Header.Set("Content-Type", "application/json") + + res, err := client.Do(req) + if err != nil { + return fmt.Errorf(err.Error()) + } + + token := Token{} + + decoder := json.NewDecoder(res.Body) + defer res.Body.Close() + + err = decoder.Decode(&token) + if err != nil { + return fmt.Errorf(err.Error()) + } + state.CloudToken = token.Token + + return nil +} diff --git a/backend/pkg/cloud-logs/upload-log.go b/backend/pkg/cloud-logs/upload-log.go new file mode 100644 index 000000000..7c193cf84 --- /dev/null +++ b/backend/pkg/cloud-logs/upload-log.go @@ -0,0 +1,63 @@ +package cloud_logs + +import ( + "bytes" + "encoding/json" + "io" + "mime/multipart" + "net/http" + "os" + "path/filepath" +) + +type Uploader struct { + Endpoint string + Client *http.Client + Auth *CloudState +} + +func (u *Uploader) setDefault() { + u.Endpoint = "http://localhost:8080/logs/upload" + u.Client = &http.Client{} +} + +func (u *Uploader) ServeHTTP(w http.ResponseWriter, req *http.Request) { + + path := Filepath{} + u.setDefault() + decoder := json.NewDecoder(req.Body) + + decoder.Decode(&path) + + file, err := os.Open(path.Path) + if err != nil { + w.WriteHeader(400) + } + + defer file.Close() + + body := &bytes.Buffer{} + + writer := multipart.NewWriter(body) + + part, err := writer.CreateFormFile("file", filepath.Base(path.Path)) + if err != nil { + w.WriteHeader(400) + } + + io.Copy(part, file) + + writer.Close() + + req, err = http.NewRequest("POST", u.Endpoint, body) + if err != nil { + w.WriteHeader(400) + } + + req.Header.Set("Authorization", "Bearer "+u.Auth.CloudToken) + + req.Header.Set("Content-Type", writer.FormDataContentType()) + + u.Client.Do(req) + +}