From c5563655a9b2af65cdc3593d3d0b926a2b9033b4 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Wed, 6 Mar 2024 21:00:43 +0700 Subject: [PATCH] feat: update article files upload and setup ci/cd --- .gitlab-ci.yml | 22 ++++++ Dockerfile | 24 +++++++ .../controller/article_files.controller.go | 11 +-- .../service/article_files.service.go | 22 +++--- .../{index.config.main.go => index.config.go} | 21 ++++-- .../config/minio.config.go | 34 ++++++--- config/toml/config.toml | 8 +++ docs/swagger/docs.go | 69 ++++++++++++++++++- docs/swagger/swagger.json | 69 ++++++++++++++++++- docs/swagger/swagger.yaml | 45 +++++++++++- main.go | 2 + 11 files changed, 295 insertions(+), 32 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 Dockerfile rename config/config/{index.config.main.go => index.config.go} (82%) rename utils/minio/index.minio.go => config/config/minio.config.go (57%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..82f28cc --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,22 @@ +stages: + - build + - deploy + +build: + stage: build + image: golang:alpine + script: + - go build -o main . + artifacts: + paths: + - main + +deploy: + stage: deploy + image: docker:stable + services: + - docker:dind + script: + - docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN registry.gitlab.com + - docker build -t registry.gitlab.com/hanifsalafi/web-humas-be:dev . + - docker push registry.gitlab.com/hanifsalafi/web-humas-be:dev diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6710b8e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# Menggunakan Go image resmi dari Docker Hub sebagai base image +FROM golang:alpine AS builder + +# Menentukan direktori kerja +WORKDIR /app + +# Menyalin file Go mod dan Go sum untuk mendownload dependensi +COPY go.mod ./ +COPY go.sum ./ + +# Mendownload dependensi menggunakan Go module +RUN go mod download + +# Menyalin seluruh file ke dalam image +COPY build . + +# Mengcompile aplikasi Go +RUN go build -o main . + +# Memindahkan binary hasil compile ke direktori yang lebih kecil +FROM alpine:latest +WORKDIR /root/ +COPY --from=builder /app/main . +CMD ["./main"] diff --git a/app/module/article_files/controller/article_files.controller.go b/app/module/article_files/controller/article_files.controller.go index 5670cd7..2c54b2c 100644 --- a/app/module/article_files/controller/article_files.controller.go +++ b/app/module/article_files/controller/article_files.controller.go @@ -181,17 +181,19 @@ func (_i *articleFilesController) Delete(c *fiber.Ctx) error { }) } -// Upload create ArticleFiles -// @Summary Create ArticleFiles +// Uploader upload ArticleFiles +// @Summary Upload ArticleFiles // @Description API for create ArticleFiles // @Tags Task // @Security Bearer +// @Produce json +// @Param files formData file true "Upload file" // @Success 200 {object} response.Response // @Failure 401 {object} response.Response // @Failure 404 {object} response.Response // @Failure 422 {object} response.Response // @Failure 500 {object} response.Response -// @Router /article-files [post] +// @Router /article-files/uploader [post] func (_i *articleFilesController) Uploader(c *fiber.Ctx) error { err := _i.articleFilesService.Uploader(c, 1) @@ -209,12 +211,13 @@ func (_i *articleFilesController) Uploader(c *fiber.Ctx) error { // @Description API for create ArticleFiles // @Tags Task // @Security Bearer +// @Param id path string true "Filename" // @Success 200 {object} response.Response // @Failure 401 {object} response.Response // @Failure 404 {object} response.Response // @Failure 422 {object} response.Response // @Failure 500 {object} response.Response -// @Router /article-files/viewer [get] +// @Router /article-files/viewer/{id} [get] func (_i *articleFilesController) Viewer(c *fiber.Ctx) error { return _i.articleFilesService.Viewer(c, c.Params("id")) diff --git a/app/module/article_files/service/article_files.service.go b/app/module/article_files/service/article_files.service.go index 04d75f0..861ddd3 100644 --- a/app/module/article_files/service/article_files.service.go +++ b/app/module/article_files/service/article_files.service.go @@ -9,7 +9,7 @@ import ( "go-humas-be/app/module/article_files/repository" "go-humas-be/app/module/article_files/request" "go-humas-be/app/module/article_files/response" - minioUpload "go-humas-be/utils/minio" + minioStorage "go-humas-be/config/config" "go-humas-be/utils/paginator" "io" "log" @@ -20,8 +20,9 @@ import ( // ArticleFilesService type articleFilesService struct { - Repo repository.ArticleFilesRepository - Log zerolog.Logger + Repo repository.ArticleFilesRepository + Log zerolog.Logger + MinioStorage *minioStorage.MinioStorage } // ArticleFilesService define interface of IArticleFilesService @@ -36,11 +37,12 @@ type ArticleFilesService interface { } // NewArticleFilesService init ArticleFilesService -func NewArticleFilesService(repo repository.ArticleFilesRepository, log zerolog.Logger) ArticleFilesService { +func NewArticleFilesService(repo repository.ArticleFilesRepository, log zerolog.Logger, minioStorage *minioStorage.MinioStorage) ArticleFilesService { return &articleFilesService{ - Repo: repo, - Log: log, + Repo: repo, + Log: log, + MinioStorage: minioStorage, } } @@ -83,7 +85,7 @@ func (_i *articleFilesService) Delete(id uint) error { } func (_i *articleFilesService) Uploader(c *fiber.Ctx, id uint) (err error) { - bucketName := "humas" + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName form, err := c.MultipartForm() if err != nil { @@ -92,7 +94,7 @@ func (_i *articleFilesService) Uploader(c *fiber.Ctx, id uint) (err error) { files := form.File["files"] // Create minio connection. - minioClient, err := minioUpload.MinioConnection() + minioClient, err := _i.MinioStorage.ConnectMinio() if err != nil { // Return status 500 and minio connection error. return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ @@ -130,15 +132,15 @@ func (_i *articleFilesService) Uploader(c *fiber.Ctx, id uint) (err error) { func (_i *articleFilesService) Viewer(c *fiber.Ctx, id string) (err error) { ctx := context.Background() + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName objectName := id - bucketName := "humas" _i.Log.Info().Str("timestamp", time.Now(). Format(time.RFC3339)).Str("Service:Resource", "Article:Uploads"). Interface("data", objectName).Msg("") // Create minio connection. - minioClient, err := minioUpload.MinioConnection() + minioClient, err := _i.MinioStorage.ConnectMinio() if err != nil { // Return status 500 and minio connection error. return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ diff --git a/config/config/index.config.main.go b/config/config/index.config.go similarity index 82% rename from config/config/index.config.main.go rename to config/config/index.config.go index d4a2c84..a2b4d3e 100644 --- a/config/config/index.config.main.go +++ b/config/config/index.config.go @@ -65,11 +65,24 @@ type middleware = struct { } } +// minio struct config +type objectStorage = struct { + MinioStorage struct { + Endpoint string `toml:"endpoint"` + AccessKeyID string `toml:"access-key-id"` + SecretAccessKey string `toml:"secret-access-key"` + UseSSL bool `toml:"use-ssl"` + BucketName string `toml:"bucket-name"` + Location string `toml:"location"` + } +} + type Config struct { - App app - DB db - Logger logger - Middleware middleware + App app + DB db + Logger logger + Middleware middleware + ObjectStorage objectStorage } // NewConfig : initialize config diff --git a/utils/minio/index.minio.go b/config/config/minio.config.go similarity index 57% rename from utils/minio/index.minio.go rename to config/config/minio.config.go index c4ee4b4..883b257 100644 --- a/utils/minio/index.minio.go +++ b/config/config/minio.config.go @@ -1,4 +1,4 @@ -package minio +package config import ( "context" @@ -7,13 +7,29 @@ import ( "log" ) -// MinioConnection membuka koneksi ke Minio. -func MinioConnection() (*minio.Client, error) { +// MinioSetup struct +type MinioStorage struct { + Cfg *Config +} + +func NewMinio(cfg *Config) *MinioStorage { + minioSetup := &MinioStorage{ + Cfg: cfg, + } + + return minioSetup +} + +func (_minio *MinioStorage) ConnectMinio() (*minio.Client, error) { ctx := context.Background() - endpoint := "localhost:9004" - accessKeyID := "sc6ZiC6dpWMZWmfwukSF" - secretAccessKey := "Q6MsoOhYFmRctWJLMrPXdV0vwKns8d9tgUJyu1ec" - useSSL := false + endpoint := _minio.Cfg.ObjectStorage.MinioStorage.Endpoint + accessKeyID := _minio.Cfg.ObjectStorage.MinioStorage.AccessKeyID + secretAccessKey := _minio.Cfg.ObjectStorage.MinioStorage.SecretAccessKey + useSSL := _minio.Cfg.ObjectStorage.MinioStorage.UseSSL + + bucketName := _minio.Cfg.ObjectStorage.MinioStorage.BucketName + location := _minio.Cfg.ObjectStorage.MinioStorage.Location + // Initialize minio client object. minioClient, errInit := minio.New(endpoint, &minio.Options{ Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), @@ -23,10 +39,6 @@ func MinioConnection() (*minio.Client, error) { log.Fatalln(errInit) } - // Make a new bucket called dev-minio. - bucketName := "humas" - location := "us-east-1" - err := minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: location}) if err != nil { // Check to see if we already own this bucket (which happens if you run this twice) diff --git a/config/toml/config.toml b/config/toml/config.toml index b8d7f91..09dce99 100644 --- a/config/toml/config.toml +++ b/config/toml/config.toml @@ -18,6 +18,14 @@ time-format = "" # https://pkg.go.dev/time#pkg-constants, https://github.com/rs/ level = 0 # panic -> 5, fatal -> 4, error -> 3, warn -> 2, info -> 1, debug -> 0, trace -> -1 prettier = true +[objectstorage.miniostorage] +endpoint = "103.37.125.216:9009" +access-key-id = "mnwWy87WrNiY4V7n63Nt" +secret-access-key = "OXG4Pzy5QaTJCbQmmA2aXt2jakxQVNfHA34kWzp5" +use-ssl = false +bucket-name = "humas" +location = "us-east-1" + [middleware.compress] enable = true level = 1 diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index 7ca9d2c..befed6a 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -627,7 +627,65 @@ const docTemplate = `{ } } }, - "/article-files/viewer": { + "/article-files/uploader": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleFiles", + "produces": [ + "application/json" + ], + "tags": [ + "Task" + ], + "summary": "Upload ArticleFiles", + "parameters": [ + { + "type": "file", + "description": "Upload file", + "name": "files", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/article-files/viewer/{id}": { "get": { "security": [ { @@ -639,6 +697,15 @@ const docTemplate = `{ "Task" ], "summary": "Create ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Filename", + "name": "id", + "in": "path", + "required": true + } + ], "responses": { "200": { "description": "OK", diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index a502729..66b26e5 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -616,7 +616,65 @@ } } }, - "/article-files/viewer": { + "/article-files/uploader": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleFiles", + "produces": [ + "application/json" + ], + "tags": [ + "Task" + ], + "summary": "Upload ArticleFiles", + "parameters": [ + { + "type": "file", + "description": "Upload file", + "name": "files", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/article-files/viewer/{id}": { "get": { "security": [ { @@ -628,6 +686,15 @@ "Task" ], "summary": "Create ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Filename", + "name": "id", + "in": "path", + "required": true + } + ], "responses": { "200": { "description": "OK", diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index efec28e..9fa60f4 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -567,9 +567,52 @@ paths: summary: update ArticleFiles tags: - Task - /article-files/viewer: + /article-files/uploader: + post: + description: API for create ArticleFiles + parameters: + - description: Upload file + in: formData + name: files + required: true + type: file + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Upload ArticleFiles + tags: + - Task + /article-files/viewer/{id}: get: description: API for create ArticleFiles + parameters: + - description: Filename + in: path + name: id + required: true + type: string responses: "200": description: OK diff --git a/main.go b/main.go index 24c6939..a20d156 100644 --- a/main.go +++ b/main.go @@ -42,6 +42,8 @@ func main() { fx.Provide(database.NewDatabase), // middleware fx.Provide(middleware.NewMiddleware), + // data storage + fx.Provide(config.NewMinio), // router fx.Provide(router.NewRouter),