From 64c91b3aeba5a1e5561fe4a06e22f7943615bc83 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 4 Jul 2023 14:15:36 -0400 Subject: [PATCH 1/9] Ajouter config.Config pour viper.Unmarshal() --- config/config.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 config/config.go diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..08f45e5 --- /dev/null +++ b/config/config.go @@ -0,0 +1,36 @@ +package config + +/* +Permet de contenir la configuration obtenue par cobra/viper + +Example d'utilisation sans error handling: + +``` +var cfg config.Config +viper.Unmarshal(&cfg) +``` + +`cfg` devrait alors contenir la configuration, et les données peuvent être +obtenues simplement en utilisant la dot (.) notation +*/ +type Config struct { + Server struct { + Admin struct { + Auth bool `json:"auth"` + Password string `json:"password"` + Username string `json:"username"` + } `json:"admin"` + Api struct { + Auth bool `json:"auth"` + Key string `json:"key"` + } `json:"api"` + Documents struct { + AccessKeyId string `json:"access_key_id"` + Buckets []string `json:"buckets"` + Endpoint string `json:"endpoint"` + SecretAccessKey string `json:"secret_access_key"` + UseSSL bool `json:"use_ssl"` + } `json:"documents"` + Port int `json:"port"` + } `json:"server"` +} From da42387211ed8d033d6aefe368b92cc8ff3ef896 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 4 Jul 2023 16:05:23 -0400 Subject: [PATCH 2/9] =?UTF-8?q?WIP=20Ajouter=20config.Config=20et=20commen?= =?UTF-8?q?cer=20=C3=A0=20l'implanter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 + cmd/server.go | 140 ++++++++++++++++++++------------------------------ 2 files changed, 58 insertions(+), 84 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5281e65..920961a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,8 @@ ADD cmd/ cmd/ ADD api/ api/ +Add config/ config/ + RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o agecem-org . # Alpine diff --git a/cmd/server.go b/cmd/server.go index ff7d4d4..9d6d349 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/viper" "git.agecem.com/agecem/agecem-org/api" + "git.agecem.com/agecem/agecem-org/config" "git.agecem.com/agecem/agecem-org/public" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" @@ -32,6 +33,8 @@ type Template struct { templates *template.Template } +var cfg config.Config + var embedFS embed.FS // serverCmd represents the server command @@ -39,6 +42,10 @@ var serverCmd = &cobra.Command{ Use: "server", Short: "Démarrer le serveur web", Run: func(cmd *cobra.Command, args []string) { + if err := viper.Unmarshal(&cfg); err != nil { + log.Fatal(err) + } + RunServer() }, } @@ -64,7 +71,7 @@ func init() { // server.documents.access_key_id - --server-documents-access-key-id serverCmd.Flags().String("server-documents-access-key-id", "", "Storage server access key id (config: server.documents.access_key_id)") - viper.BindPFlag("documents.accessKeyID", serverCmd.Flags().Lookup("documents-access-key-id")) + viper.BindPFlag("server.documents.access_key_id", serverCmd.Flags().Lookup("server-documents-access-key-id")) // server.documents.secret_access_key - --server-documents-secret-access-key serverCmd.Flags().String("server-documents-secret-access-key", "", "Storage server secret access key (config: server.documents.secret_access_key)") @@ -114,13 +121,13 @@ func RunServer() { groupV1.Use(middleware.AddTrailingSlash()) - if viper.GetBool("server.api.auth") { - if len(viper.GetString("server.api.key")) < 10 { + if cfg.Server.Api.Auth { + if len(cfg.Server.Api.Key) < 10 { log.Fatal("server.api.auth is enabled, but server.api.key is too small (needs at least 10 characters)") } groupV1.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) { - return subtle.ConstantTimeCompare([]byte(key), []byte(viper.GetString("server.api.key"))) == 1, nil + return subtle.ConstantTimeCompare([]byte(key), []byte(cfg.Server.Api.Key)) == 1, nil })) log.Println("Key auth for /v1 activated") @@ -130,21 +137,19 @@ func RunServer() { groupAdmin.Use(middleware.AddTrailingSlash()) - if viper.GetBool("server.admin.auth") { - username := viper.GetString("server.admin.username") - password := viper.GetString("server.admin.password") - if len(username) < 5 { + if cfg.Server.Admin.Auth { + if len(cfg.Server.Admin.Username) < 5 { log.Fatal("server.admin.auth is enabled, but server.admin.username is too small (needs at least 5 characters)") } - if len(password) < 10 { + if len(cfg.Server.Admin.Password) < 10 { log.Fatal("server.admin.auth is enabled, but server.admin.password is too small (needs at least 10 characters)") } groupAdmin.Use(middleware.BasicAuth(func(username_entered, password_entered string, c echo.Context) (bool, error) { // Be careful to use constant time comparison to prevent timing attacks - if subtle.ConstantTimeCompare([]byte(username_entered), []byte(username)) == 1 && - subtle.ConstantTimeCompare([]byte(password_entered), []byte(password)) == 1 { + if subtle.ConstantTimeCompare([]byte(username_entered), []byte(cfg.Server.Admin.Username)) == 1 && + subtle.ConstantTimeCompare([]byte(password_entered), []byte(cfg.Server.Admin.Password)) == 1 { return true, nil } return false, nil @@ -208,7 +213,7 @@ func RunServer() { groupAdmin.POST("/documents/upload", handleAdminDocumentsUploadPOST) e.Logger.Fatal(e.Start( - fmt.Sprintf(":%d", viper.GetInt("server.port")))) + fmt.Sprintf(":%d", cfg.Server.Port))) } func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { @@ -228,30 +233,26 @@ func handleV1(c echo.Context) error { // handleV1Seed créé des buckets dans minio selon la liste de buckets dans server.documents.buckets // Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket. func handleV1Seed(c echo.Context) error { - documents_buckets := viper.GetStringSlice("server.documents.buckets") - documents_endpoint := viper.GetString("server.documents.endpoint") - documents_access_key_id := viper.GetString("server.documents.access_key_id") - documents_secret_access_key := viper.GetString("server.documents.secret_access_key") - documents_use_ssl := viper.GetBool("server.documents.use_ssl") - // Initialize minio client object - client, err := minio.New(documents_endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(documents_access_key_id, documents_secret_access_key, ""), - Secure: documents_use_ssl, + client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), + Secure: cfg.Server.Documents.UseSSL, }) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ "message": "Error during minio#New", + "error": err.Error(), }) } var new_buckets []string - for _, bucket := range documents_buckets { + for _, bucket := range cfg.Server.Documents.Buckets { exists, err := client.BucketExists(context.Background(), bucket) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ "message": "Error during minio#BucketExists", + "error": err.Error(), }) } @@ -262,6 +263,7 @@ func handleV1Seed(c echo.Context) error { if err = client.MakeBucket(context.Background(), bucket, minio.MakeBucketOptions{}); err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ "message": "Error during minio#MakeBucket", + "error": err.Error(), }) } new_buckets = append(new_buckets, bucket) @@ -282,16 +284,10 @@ func handleV1Seed(c echo.Context) error { // handleV1BucketList affiche les buckets permis par server.documents.buckets, qui existent. func handleV1BucketList(c echo.Context) error { - documents_buckets := viper.GetStringSlice("server.documents.buckets") - documents_endpoint := viper.GetString("server.documents.endpoint") - documents_access_key_id := viper.GetString("server.documents.access_key_id") - documents_secret_access_key := viper.GetString("server.documents.secret_access_key") - documents_use_ssl := viper.GetBool("server.documents.use_ssl") - // Initialize minio client object - client, err := minio.New(documents_endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(documents_access_key_id, documents_secret_access_key, ""), - Secure: documents_use_ssl, + client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), + Secure: cfg.Server.Documents.UseSSL, }) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ @@ -301,7 +297,7 @@ func handleV1BucketList(c echo.Context) error { var buckets []string - for _, bucket_name := range documents_buckets { + for _, bucket_name := range cfg.Server.Documents.Buckets { exists, err := client.BucketExists(context.Background(), bucket_name) if err != nil { return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") @@ -316,16 +312,10 @@ func handleV1BucketList(c echo.Context) error { } func handleV1BucketRead(c echo.Context) error { - documents_buckets := viper.GetStringSlice("server.documents.buckets") - documents_endpoint := viper.GetString("server.documents.endpoint") - documents_access_key_id := viper.GetString("server.documents.access_key_id") - documents_secret_access_key := viper.GetString("server.documents.secret_access_key") - documents_use_ssl := viper.GetBool("server.documents.use_ssl") - bucket := c.Param("bucket") allowed := false - for _, bucket_allowed := range documents_buckets { + for _, bucket_allowed := range cfg.Server.Documents.Buckets { if bucket == bucket_allowed { allowed = true } @@ -345,9 +335,9 @@ func handleV1BucketRead(c echo.Context) error { defer cancel() // Initialize minio client object - client, err := minio.New(documents_endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(documents_access_key_id, documents_secret_access_key, ""), - Secure: documents_use_ssl, + client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), + Secure: cfg.Server.Documents.UseSSL, }) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ @@ -382,12 +372,6 @@ func handleV1BucketRead(c echo.Context) error { // handleV1DocumentCreate permet d'ajouter un object dans un bucket, par multipart/form-data func handleV1DocumentCreate(c echo.Context) error { - documents_buckets := viper.GetStringSlice("server.documents.buckets") - documents_endpoint := viper.GetString("server.documents.endpoint") - documents_access_key_id := viper.GetString("server.documents.access_key_id") - documents_secret_access_key := viper.GetString("server.documents.secret_access_key") - documents_use_ssl := viper.GetBool("server.documents.use_ssl") - bucket := c.Param("bucket") form_file, err := c.FormFile("document") @@ -399,7 +383,7 @@ func handleV1DocumentCreate(c echo.Context) error { } allowed := false - for _, bucket_allowed := range documents_buckets { + for _, bucket_allowed := range cfg.Server.Documents.Buckets { if bucket == bucket_allowed { allowed = true } @@ -414,9 +398,9 @@ func handleV1DocumentCreate(c echo.Context) error { defer cancel() // Initialize minio client object - client, err := minio.New(documents_endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(documents_access_key_id, documents_secret_access_key, ""), - Secure: documents_use_ssl, + client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), + Secure: cfg.Server.Documents.UseSSL, }) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ @@ -458,17 +442,11 @@ func handleV1DocumentCreate(c echo.Context) error { // handleV1DocumentRead permet de lire le contenu d'un fichier et protentiellement de le télécharger func handleV1DocumentRead(c echo.Context) error { - documents_buckets := viper.GetStringSlice("server.documents.buckets") - documents_endpoint := viper.GetString("server.documents.endpoint") - documents_access_key_id := viper.GetString("server.documents.access_key_id") - documents_secret_access_key := viper.GetString("server.documents.secret_access_key") - documents_use_ssl := viper.GetBool("server.documents.use_ssl") - bucket := c.Param("bucket") document := c.Param("document") allowed := false - for _, bucket_allowed := range documents_buckets { + for _, bucket_allowed := range cfg.Server.Documents.Buckets { if bucket == bucket_allowed { allowed = true } @@ -488,9 +466,9 @@ func handleV1DocumentRead(c echo.Context) error { defer cancel() // Initialize minio client object - client, err := minio.New(documents_endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(documents_access_key_id, documents_secret_access_key, ""), - Secure: documents_use_ssl, + client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), + Secure: cfg.Server.Documents.UseSSL, }) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ @@ -543,17 +521,11 @@ func handleV1DocumentUpdate(c echo.Context) error { // handleV1DocumentDelete permet de supprimer un object func handleV1DocumentDelete(c echo.Context) error { - documents_buckets := viper.GetStringSlice("server.documents.buckets") - documents_endpoint := viper.GetString("server.documents.endpoint") - documents_access_key_id := viper.GetString("server.documents.access_key_id") - documents_secret_access_key := viper.GetString("server.documents.secret_access_key") - documents_use_ssl := viper.GetBool("server.documents.use_ssl") - bucket := c.Param("bucket") document := c.Param("document") allowed := false - for _, bucket_allowed := range documents_buckets { + for _, bucket_allowed := range cfg.Server.Documents.Buckets { if bucket == bucket_allowed { allowed = true } @@ -573,9 +545,9 @@ func handleV1DocumentDelete(c echo.Context) error { defer cancel() // Initialize minio client object - client, err := minio.New(documents_endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(documents_access_key_id, documents_secret_access_key, ""), - Secure: documents_use_ssl, + client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), + Secure: cfg.Server.Documents.UseSSL, }) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ @@ -648,9 +620,9 @@ func handleVieEtudianteOrganisme(c echo.Context) error { } func handleDocumentation(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"), + client, err := api.New("http", "localhost", cfg.Server.Port, api.APIOptions{ + KeyAuth: cfg.Server.Api.Auth, + Key: cfg.Server.Api.Key, }) if err != nil { return c.Render(http.StatusInternalServerError, "documentation-html", nil) @@ -720,9 +692,9 @@ func handleFormulaires(c echo.Context) error { } 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"), + client, err := api.New("http", "localhost", cfg.Server.Port, api.APIOptions{ + KeyAuth: cfg.Server.Api.Auth, + Key: cfg.Server.Api.Key, }) if err != nil { return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) @@ -756,12 +728,12 @@ func handleAdminDocumentsUpload(c echo.Context) error { } func handleAdminDocumentsUploadPOST(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"), - BasicAuth: viper.GetBool("server.admin.auth"), - Username: viper.GetString("server.admin.username"), - Password: viper.GetString("server.admin.password"), + client, err := api.New("http", "localhost", cfg.Server.Port, api.APIOptions{ + KeyAuth: cfg.Server.Api.Auth, + Key: cfg.Server.Api.Key, + BasicAuth: cfg.Server.Admin.Auth, + Username: cfg.Server.Admin.Username, + Password: cfg.Server.Admin.Password, }) if err != nil { return c.Render(http.StatusInternalServerError, "admin-upload-html", struct{ Message string }{Message: fmt.Sprintf("handleAdminDocumentsUploadPOST#api.New: %s", err)}) From 32e53546ed84d83c825af45a81d6fca075732849 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 4 Jul 2023 16:05:49 -0400 Subject: [PATCH 3/9] =?UTF-8?q?Ajouter=20configCmd=20pour=20print=20config?= =?UTF-8?q?=20=C3=A0=20l'=C3=A9cran?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/config.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 cmd/config.go diff --git a/cmd/config.go b/cmd/config.go new file mode 100644 index 0000000..a8d41f0 --- /dev/null +++ b/cmd/config.go @@ -0,0 +1,44 @@ +/* +Copyright © 2023 AGECEM +*/ +package cmd + +import ( + "encoding/json" + "fmt" + "log" + + "git.agecem.com/agecem/agecem-org/config" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// configCmd represents the config command +var configCmd = &cobra.Command{ + Use: "config", + Short: "Print the config to stdout in indented JSON format", + Run: func(cmd *cobra.Command, args []string) { + var cfg config.Config + + if err := viper.Unmarshal(&cfg); err != nil { + log.Fatal(err) + } + + printConfig(cfg) + }, +} + +func init() { + rootCmd.AddCommand(configCmd) +} + +func printConfig(config config.Config) error { + buf, err := json.MarshalIndent(config, "", " ") + if err != nil { + return err + } + + fmt.Println(string(buf)) + + return nil +} From fdc2c230960f575fd5f87ff78ce93dfa5fd99027 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 4 Jul 2023 16:22:00 -0400 Subject: [PATCH 4/9] Fix typo --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 920961a..b01018d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ ADD cmd/ cmd/ ADD api/ api/ -Add config/ config/ +ADD config/ config/ RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o agecem-org . From 4f5b4510422a9cbeeb8aa294b6bbebb190021ac2 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 4 Jul 2023 16:47:03 -0400 Subject: [PATCH 5/9] Ajouter media/ pour abstraire le client minio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajouter media/ à Dockerfile Migrer serverCmd#HandleV1Seed à utiliser media#NewMediaClientFromViper() --- Dockerfile | 2 ++ cmd/server.go | 13 +++++-------- media/media.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 media/media.go diff --git a/Dockerfile b/Dockerfile index b01018d..d91ca09 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,8 @@ ADD api/ api/ ADD config/ config/ +ADD media/ media/ + RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o agecem-org . # Alpine diff --git a/cmd/server.go b/cmd/server.go index 9d6d349..c56b1a8 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -24,6 +24,7 @@ import ( "git.agecem.com/agecem/agecem-org/api" "git.agecem.com/agecem/agecem-org/config" + "git.agecem.com/agecem/agecem-org/media" "git.agecem.com/agecem/agecem-org/public" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" @@ -233,14 +234,10 @@ func handleV1(c echo.Context) error { // handleV1Seed créé des buckets dans minio selon la liste de buckets dans server.documents.buckets // Les buckets sont créés avec paramètres par défaut, et sont ensuite visible dans /v1/bucket. func handleV1Seed(c echo.Context) error { - // Initialize minio client object - client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), - Secure: cfg.Server.Documents.UseSSL, - }) + mediaClient, err := media.NewMediaClientFromViper() if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#New", + "message": "Error during media.NewMediaClientFromViper()", "error": err.Error(), }) } @@ -248,7 +245,7 @@ func handleV1Seed(c echo.Context) error { var new_buckets []string for _, bucket := range cfg.Server.Documents.Buckets { - exists, err := client.BucketExists(context.Background(), bucket) + exists, err := mediaClient.MinioClient.BucketExists(context.Background(), bucket) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ "message": "Error during minio#BucketExists", @@ -260,7 +257,7 @@ func handleV1Seed(c echo.Context) error { continue } - if err = client.MakeBucket(context.Background(), bucket, minio.MakeBucketOptions{}); err != nil { + if err = mediaClient.MinioClient.MakeBucket(context.Background(), bucket, minio.MakeBucketOptions{}); err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ "message": "Error during minio#MakeBucket", "error": err.Error(), diff --git a/media/media.go b/media/media.go new file mode 100644 index 0000000..d204b8d --- /dev/null +++ b/media/media.go @@ -0,0 +1,44 @@ +package media + +import ( + "git.agecem.com/agecem/agecem-org/config" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/spf13/viper" +) + +func NewMediaClient(endpoint, accessKeyId, secretAccessKey string, useSSL bool) (*MediaClient, error) { + var mediaClient MediaClient + minioClient, err := minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKeyId, secretAccessKey, ""), + Secure: useSSL, + }) + if err != nil { + return &mediaClient, err + } + + mediaClient.MinioClient = *minioClient + return &mediaClient, nil +} + +func NewMediaClientFromViper() (*MediaClient, error) { + var cfg config.Config + if err := viper.Unmarshal(&cfg); err != nil { + return nil, err + } + + mediaClient, err := NewMediaClient(cfg.Server.Documents.Endpoint, cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, cfg.Server.Documents.UseSSL) + if err != nil { + return mediaClient, err + } + + return mediaClient, nil +} + +type MediaClient struct { + MinioClient minio.Client +} + +func (m *MediaClient) foo() string { + return "bar" +} From 6352224b01f7eafe714add3f13d0d438be298cff Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 4 Jul 2023 16:55:45 -0400 Subject: [PATCH 6/9] Changer tous les minio.New -> media.NewMediaClientFromViper() --- cmd/server.go | 66 +++++++++++++++++++-------------------------------- 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/cmd/server.go b/cmd/server.go index c56b1a8..e5d07f6 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -18,7 +18,6 @@ import ( "sort" "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -281,21 +280,18 @@ func handleV1Seed(c echo.Context) error { // handleV1BucketList affiche les buckets permis par server.documents.buckets, qui existent. func handleV1BucketList(c echo.Context) error { - // Initialize minio client object - client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), - Secure: cfg.Server.Documents.UseSSL, - }) + mediaClient, err := media.NewMediaClientFromViper() if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#New", + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), }) } var buckets []string for _, bucket_name := range cfg.Server.Documents.Buckets { - exists, err := client.BucketExists(context.Background(), bucket_name) + exists, err := mediaClient.MinioClient.BucketExists(context.Background(), bucket_name) if err != nil { return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") } @@ -331,18 +327,15 @@ func handleV1BucketRead(c echo.Context) error { defer cancel() - // Initialize minio client object - client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), - Secure: cfg.Server.Documents.UseSSL, - }) + mediaClient, err := media.NewMediaClientFromViper() if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#New", + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), }) } - exists, err := client.BucketExists(ctx, bucket) + exists, err := mediaClient.MinioClient.BucketExists(ctx, bucket) if err != nil { return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") } @@ -353,7 +346,7 @@ func handleV1BucketRead(c echo.Context) error { var keys []string - objectCh := client.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) + objectCh := mediaClient.MinioClient.ListObjects(ctx, bucket, minio.ListObjectsOptions{}) for object := range objectCh { if object.Err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ @@ -394,14 +387,11 @@ func handleV1DocumentCreate(c echo.Context) error { defer cancel() - // Initialize minio client object - client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), - Secure: cfg.Server.Documents.UseSSL, - }) + mediaClient, err := media.NewMediaClientFromViper() if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#New", + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), }) } @@ -418,7 +408,7 @@ func handleV1DocumentCreate(c echo.Context) error { filename_processed := reg.ReplaceAllString(form_file.Filename, "") - info, err := client.PutObject(ctx, bucket, filename_processed, src, form_file.Size, minio.PutObjectOptions{ + info, err := mediaClient.MinioClient.PutObject(ctx, bucket, filename_processed, src, form_file.Size, minio.PutObjectOptions{ ContentType: form_file.Header.Get("Content-Type"), }) if err != nil { @@ -462,18 +452,15 @@ func handleV1DocumentRead(c echo.Context) error { defer cancel() - // Initialize minio client object - client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), - Secure: cfg.Server.Documents.UseSSL, - }) + mediaClient, err := media.NewMediaClientFromViper() if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#New", + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), }) } - bucket_exists, err := client.BucketExists(ctx, bucket) + bucket_exists, err := mediaClient.MinioClient.BucketExists(ctx, bucket) if err != nil { return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") } @@ -482,7 +469,7 @@ func handleV1DocumentRead(c echo.Context) error { return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) } - document_info, err := client.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) + document_info, err := mediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) if err != nil { if err.Error() == "The specified key does not exist." { @@ -497,7 +484,7 @@ func handleV1DocumentRead(c echo.Context) error { _ = document_info - document_object, err := client.GetObject(ctx, bucket, document, minio.GetObjectOptions{}) + document_object, err := mediaClient.MinioClient.GetObject(ctx, bucket, document, minio.GetObjectOptions{}) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ "message": "Error during minio#GetObject", @@ -541,18 +528,15 @@ func handleV1DocumentDelete(c echo.Context) error { defer cancel() - // Initialize minio client object - client, err := minio.New(cfg.Server.Documents.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(cfg.Server.Documents.AccessKeyId, cfg.Server.Documents.SecretAccessKey, ""), - Secure: cfg.Server.Documents.UseSSL, - }) + mediaClient, err := media.NewMediaClientFromViper() if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ - "message": "Error during minio#New", + "message": "Error during media.NewMediaClientFromViper()", + "error": err.Error(), }) } - bucket_exists, err := client.BucketExists(ctx, bucket) + bucket_exists, err := mediaClient.MinioClient.BucketExists(ctx, bucket) if err != nil { return c.JSON(http.StatusInternalServerError, "Error during minio#BucketExists") } @@ -561,7 +545,7 @@ func handleV1DocumentDelete(c echo.Context) error { return c.JSON(http.StatusNotFound, map[string]string{"message": "Not Found"}) } - document_info, err := client.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) + document_info, err := mediaClient.MinioClient.StatObject(ctx, bucket, document, minio.StatObjectOptions{}) if err != nil { if err.Error() == "The specified key does not exist." { @@ -576,7 +560,7 @@ func handleV1DocumentDelete(c echo.Context) error { //TODO Add error validation _ = document_info - err = client.RemoveObject(ctx, bucket, document, minio.RemoveObjectOptions{}) + err = mediaClient.MinioClient.RemoveObject(ctx, bucket, document, minio.RemoveObjectOptions{}) if err != nil { return c.JSON(http.StatusInternalServerError, map[string]string{ "message": "Error during minio#RemoveObject", From aaf852198471ee8cd9a710dc56162f73f83bd709 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 4 Jul 2023 18:39:00 -0400 Subject: [PATCH 7/9] Remplacer struct tags json -> mapstructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix problèmes de config à s'attendre à du PascalCase au lieu du intended snake_case --- config/config.go | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/config/config.go b/config/config.go index 08f45e5..decfeca 100644 --- a/config/config.go +++ b/config/config.go @@ -16,21 +16,23 @@ obtenues simplement en utilisant la dot (.) notation type Config struct { Server struct { Admin struct { - Auth bool `json:"auth"` - Password string `json:"password"` - Username string `json:"username"` - } `json:"admin"` + Auth bool `mapstructure:"auth"` + Password string `mapstructure:"password"` + Username string `mapstructure:"username"` + } `mapstructure:"admin"` Api struct { - Auth bool `json:"auth"` - Key string `json:"key"` - } `json:"api"` + Auth bool `mapstructure:"auth"` + Key string `mapstructure:"key"` + } `mapstructure:"api"` Documents struct { - AccessKeyId string `json:"access_key_id"` - Buckets []string `json:"buckets"` - Endpoint string `json:"endpoint"` - SecretAccessKey string `json:"secret_access_key"` - UseSSL bool `json:"use_ssl"` - } `json:"documents"` - Port int `json:"port"` - } `json:"server"` + AccessKeyId string `mapstructure:"access_key_id"` + Buckets []string `mapstructure:"buckets"` + Endpoint string `mapstructure:"endpoint"` + SecretAccessKey string `mapstructure:"secret_access_key"` + UseSSL bool `mapstructure:"use_ssl"` + KeyId string `mapstructure:"keyid"` + KeyValue string `mapstructure:"keyvalue"` + } `mapstructure:"documents"` + Port int `mapstructure:"port"` + } `mapstructure:"server"` } From cb92f20375ffcb6393322acb37bce101105df88b Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 4 Jul 2023 18:41:27 -0400 Subject: [PATCH 8/9] Ajouter validation d'accessKeyId et secretAccessKey vides --- media/media.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/media/media.go b/media/media.go index d204b8d..2d044f0 100644 --- a/media/media.go +++ b/media/media.go @@ -1,6 +1,8 @@ package media import ( + "errors" + "git.agecem.com/agecem/agecem-org/config" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" @@ -8,6 +10,14 @@ import ( ) func NewMediaClient(endpoint, accessKeyId, secretAccessKey string, useSSL bool) (*MediaClient, error) { + if accessKeyId == "" { + return nil, errors.New("accessKeyId was found empty, but cannot be") + } + + if secretAccessKey == "" { + return nil, errors.New("secretAccessKey was found empty, but cannot be") + } + var mediaClient MediaClient minioClient, err := minio.New(endpoint, &minio.Options{ Creds: credentials.NewStaticV4(accessKeyId, secretAccessKey, ""), From 1cea7776e48809c8564c5c37ca4233159b08e194 Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Tue, 4 Jul 2023 19:43:41 -0400 Subject: [PATCH 9/9] Update README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 332a1a9..ef59413 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,18 @@ Le lifecycle du serveur est maintenu par containers, en utilisant `docker-compos ### Exemples d'utilisation de docker-compose +*L'exemple suivant assume que minio est déployé par docker-compose en même temps que le serveur web.* + +Remplir .env avec les credentials que minio utilisera pour sa configuration initiale. + +Remplacer `agecem-org` par quelque chose de sécurisé. + +``` +# .env +MINIO_ROOT_USER=agecem-org +MINIO_ROOT_PASSWORD=agecem-org +``` + Déployer le ou les containers en mode détaché, en s'assurant de rebâtir l'image. `$ docker-compose up -d --build`