diff --git a/Dockerfile b/Dockerfile index 0e98d5c..5281e65 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,8 @@ ADD public/ public/ ADD cmd/ cmd/ +ADD api/ api/ + RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o agecem-org . # Alpine diff --git a/api/api.go b/api/api.go new file mode 100644 index 0000000..70334f9 --- /dev/null +++ b/api/api.go @@ -0,0 +1,101 @@ +package api + +import ( + "errors" + "fmt" + "io/ioutil" + "net/http" +) + +type API struct { + Protocol string + Host string + Port int + Opts APIOptions +} + +type APIOptions struct { + KeyAuth bool + Key string +} + +func New(protocol, host string, port int, opts APIOptions) (*API, error) { + api := API{ + Protocol: protocol, + Host: host, + Port: port, + Opts: opts, + } + + return &api, nil +} + +// Call returns a []byte representing a response body. +// Can be used for GET or DELETE methods +func (a *API) Call(method, route string) ([]byte, error) { + endpoint := fmt.Sprintf("%s://%s:%d", + a.Protocol, + a.Host, + a.Port, + ) + request := fmt.Sprintf("%s%s", endpoint, route) + + switch method { + case http.MethodGet: + // Create client + client := &http.Client{} + + // Create request + request, err := http.NewRequest(http.MethodGet, request, nil) + if err != nil { + return nil, err + } + + if a.Opts.KeyAuth { + request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) + } + + // Fetch Request + response, err := client.Do(request) + if err != nil { + return nil, err + } + + defer response.Body.Close() + + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, err + } + + return body, nil + case http.MethodDelete: + // Create client + client := &http.Client{} + + // Create request + req, err := http.NewRequest(http.MethodDelete, request, nil) + if err != nil { + return nil, err + } + + if a.Opts.KeyAuth { + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Opts.Key)) + } + + // Fetch Request + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Read Response Body + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return respBody, nil + } + return nil, errors.New(fmt.Sprintf("method must be 'GET' or 'DELETE', got '%s'", method)) +} diff --git a/cmd/server.go b/cmd/server.go index db3bb32..71f5a54 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -6,6 +6,7 @@ package cmd import ( "context" "crypto/subtle" + "encoding/json" "fmt" "log" @@ -20,6 +21,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + "git.agecem.com/agecem/agecem-org/api" "git.agecem.com/agecem/agecem-org/public" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" @@ -148,6 +150,10 @@ func RunServer() { e.GET("/formulaires", handleFormulaires) + // Public Routes + + e.GET("/public/documentation/:bucket/:document", handlePublicDocumentation) + e.Logger.Fatal(e.Start( fmt.Sprintf(":%d", viper.GetInt("server.port")))) } @@ -573,20 +579,98 @@ func handleActualiteArticle(c echo.Context) error { article := c.Param("article") return c.String(http.StatusOK, fmt.Sprintf("Article: %s", article)) } + func handleVieEtudiante(c echo.Context) error { return c.Render(http.StatusOK, "vie-etudiante-html", nil) } + func handleVieEtudianteOrganisme(c echo.Context) error { organisme := c.Param("organisme") return c.String(http.StatusOK, fmt.Sprintf("Organisme: %s", organisme)) } + func handleDocumentation(c echo.Context) error { - return c.Render(http.StatusOK, "documentation-html", nil) + client, err := api.New("http", "localhost", viper.GetInt("server.port"), api.APIOptions{ + KeyAuth: viper.GetBool("server.api.auth"), + Key: viper.GetString("server.api.key"), + }) + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + result, err := client.Call(http.MethodGet, "/v1/bucket") + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + var buckets []string + + err = json.Unmarshal(result, &buckets) + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + type Bucket struct { + Name string + Documents []string + } + + var data []Bucket + + for _, bucket := range buckets { + result, err := client.Call(http.MethodGet, fmt.Sprintf("/v1/bucket/%s", bucket)) + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + var documents []string + + err = json.Unmarshal(result, &documents) + if err != nil { + return c.Render(http.StatusInternalServerError, "documentation-html", nil) + } + + data = append(data, Bucket{ + Name: bucket, + Documents: documents, + }) + } + + return c.Render(http.StatusOK, "documentation-html", data) } + func handleFormulaires(c echo.Context) error { return c.Render(http.StatusOK, "formulaires-html", nil) } +func handlePublicDocumentation(c echo.Context) error { + client, err := api.New("http", "localhost", viper.GetInt("server.port"), api.APIOptions{ + KeyAuth: viper.GetBool("server.api.auth"), + Key: viper.GetString("server.api.key"), + }) + if err != nil { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + bucket := c.Param("bucket") + document := c.Param("document") + + result, err := client.Call(http.MethodGet, fmt.Sprintf("/v1/bucket/%s/%s", bucket, document)) + if err != nil { + return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) + } + + // Check if result can fit inside a map containing a message + var result_map map[string]string + + err = json.Unmarshal(result, &result_map) + if err == nil { + return c.JSON(http.StatusBadRequest, result_map) + } + + return c.Blob(http.StatusOK, "application/octet-stream", result) +} + // CSS Handlers func handleStaticCSSIndex(c echo.Context) error { diff --git a/public/html/documentation.gohtml b/public/html/documentation.gohtml index 813d77e..27f2a06 100644 --- a/public/html/documentation.gohtml +++ b/public/html/documentation.gohtml @@ -9,6 +9,18 @@ {{ template "header-html" }}

Documentation

+

+ {{ range . }} + {{ $bucket_name := .Name }} +

{{ $bucket_name }}

+ + + {{ end }} +

{{ end }}