feat: update article files upload and setup ci/cd

This commit is contained in:
hanif salafi 2024-03-06 21:00:43 +07:00
parent 4795dbdfac
commit aa2f46b410
12 changed files with 20644 additions and 32 deletions

22
.gitlab-ci.yml Normal file
View File

@ -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

24
Dockerfile Normal file
View File

@ -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"]

View File

@ -181,17 +181,19 @@ func (_i *articleFilesController) Delete(c *fiber.Ctx) error {
}) })
} }
// Upload create ArticleFiles // Uploader upload ArticleFiles
// @Summary Create ArticleFiles // @Summary Upload ArticleFiles
// @Description API for create ArticleFiles // @Description API for create ArticleFiles
// @Tags Task // @Tags Task
// @Security Bearer // @Security Bearer
// @Produce json
// @Param files formData file true "Upload file"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 401 {object} response.Response // @Failure 401 {object} response.Response
// @Failure 404 {object} response.Response // @Failure 404 {object} response.Response
// @Failure 422 {object} response.Response // @Failure 422 {object} response.Response
// @Failure 500 {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 { func (_i *articleFilesController) Uploader(c *fiber.Ctx) error {
err := _i.articleFilesService.Uploader(c, 1) err := _i.articleFilesService.Uploader(c, 1)
@ -209,12 +211,13 @@ func (_i *articleFilesController) Uploader(c *fiber.Ctx) error {
// @Description API for create ArticleFiles // @Description API for create ArticleFiles
// @Tags Task // @Tags Task
// @Security Bearer // @Security Bearer
// @Param id path string true "Filename"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 401 {object} response.Response // @Failure 401 {object} response.Response
// @Failure 404 {object} response.Response // @Failure 404 {object} response.Response
// @Failure 422 {object} response.Response // @Failure 422 {object} response.Response
// @Failure 500 {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 { func (_i *articleFilesController) Viewer(c *fiber.Ctx) error {
return _i.articleFilesService.Viewer(c, c.Params("id")) return _i.articleFilesService.Viewer(c, c.Params("id"))

View File

@ -9,7 +9,7 @@ import (
"go-humas-be/app/module/article_files/repository" "go-humas-be/app/module/article_files/repository"
"go-humas-be/app/module/article_files/request" "go-humas-be/app/module/article_files/request"
"go-humas-be/app/module/article_files/response" "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" "go-humas-be/utils/paginator"
"io" "io"
"log" "log"
@ -22,6 +22,7 @@ import (
type articleFilesService struct { type articleFilesService struct {
Repo repository.ArticleFilesRepository Repo repository.ArticleFilesRepository
Log zerolog.Logger Log zerolog.Logger
MinioStorage *minioStorage.MinioStorage
} }
// ArticleFilesService define interface of IArticleFilesService // ArticleFilesService define interface of IArticleFilesService
@ -36,11 +37,12 @@ type ArticleFilesService interface {
} }
// NewArticleFilesService init ArticleFilesService // 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{ return &articleFilesService{
Repo: repo, Repo: repo,
Log: log, 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) { func (_i *articleFilesService) Uploader(c *fiber.Ctx, id uint) (err error) {
bucketName := "humas" bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
form, err := c.MultipartForm() form, err := c.MultipartForm()
if err != nil { if err != nil {
@ -92,7 +94,7 @@ func (_i *articleFilesService) Uploader(c *fiber.Ctx, id uint) (err error) {
files := form.File["files"] files := form.File["files"]
// Create minio connection. // Create minio connection.
minioClient, err := minioUpload.MinioConnection() minioClient, err := _i.MinioStorage.ConnectMinio()
if err != nil { if err != nil {
// Return status 500 and minio connection error. // Return status 500 and minio connection error.
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ 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) { func (_i *articleFilesService) Viewer(c *fiber.Ctx, id string) (err error) {
ctx := context.Background() ctx := context.Background()
bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
objectName := id objectName := id
bucketName := "humas"
_i.Log.Info().Str("timestamp", time.Now(). _i.Log.Info().Str("timestamp", time.Now().
Format(time.RFC3339)).Str("Service:Resource", "Article:Uploads"). Format(time.RFC3339)).Str("Service:Resource", "Article:Uploads").
Interface("data", objectName).Msg("") Interface("data", objectName).Msg("")
// Create minio connection. // Create minio connection.
minioClient, err := minioUpload.MinioConnection() minioClient, err := _i.MinioStorage.ConnectMinio()
if err != nil { if err != nil {
// Return status 500 and minio connection error. // Return status 500 and minio connection error.
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{

View File

@ -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 { type Config struct {
App app App app
DB db DB db
Logger logger Logger logger
Middleware middleware Middleware middleware
ObjectStorage objectStorage
} }
// NewConfig : initialize config // NewConfig : initialize config

View File

@ -1,4 +1,4 @@
package minio package config
import ( import (
"context" "context"
@ -7,13 +7,29 @@ import (
"log" "log"
) )
// MinioConnection membuka koneksi ke Minio. // MinioSetup struct
func MinioConnection() (*minio.Client, error) { 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() ctx := context.Background()
endpoint := "localhost:9004" endpoint := _minio.Cfg.ObjectStorage.MinioStorage.Endpoint
accessKeyID := "sc6ZiC6dpWMZWmfwukSF" accessKeyID := _minio.Cfg.ObjectStorage.MinioStorage.AccessKeyID
secretAccessKey := "Q6MsoOhYFmRctWJLMrPXdV0vwKns8d9tgUJyu1ec" secretAccessKey := _minio.Cfg.ObjectStorage.MinioStorage.SecretAccessKey
useSSL := false useSSL := _minio.Cfg.ObjectStorage.MinioStorage.UseSSL
bucketName := _minio.Cfg.ObjectStorage.MinioStorage.BucketName
location := _minio.Cfg.ObjectStorage.MinioStorage.Location
// Initialize minio client object. // Initialize minio client object.
minioClient, errInit := minio.New(endpoint, &minio.Options{ minioClient, errInit := minio.New(endpoint, &minio.Options{
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
@ -23,10 +39,6 @@ func MinioConnection() (*minio.Client, error) {
log.Fatalln(errInit) 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}) err := minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: location})
if err != nil { if err != nil {
// Check to see if we already own this bucket (which happens if you run this twice) // Check to see if we already own this bucket (which happens if you run this twice)

View File

@ -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 level = 0 # panic -> 5, fatal -> 4, error -> 3, warn -> 2, info -> 1, debug -> 0, trace -> -1
prettier = true 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] [middleware.compress]
enable = true enable = true
level = 1 level = 1

20349
debug.log

File diff suppressed because it is too large Load Diff

View File

@ -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": { "get": {
"security": [ "security": [
{ {
@ -639,6 +697,15 @@ const docTemplate = `{
"Task" "Task"
], ],
"summary": "Create ArticleFiles", "summary": "Create ArticleFiles",
"parameters": [
{
"type": "string",
"description": "Filename",
"name": "id",
"in": "path",
"required": true
}
],
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",

View File

@ -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": { "get": {
"security": [ "security": [
{ {
@ -628,6 +686,15 @@
"Task" "Task"
], ],
"summary": "Create ArticleFiles", "summary": "Create ArticleFiles",
"parameters": [
{
"type": "string",
"description": "Filename",
"name": "id",
"in": "path",
"required": true
}
],
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",

View File

@ -567,9 +567,52 @@ paths:
summary: update ArticleFiles summary: update ArticleFiles
tags: tags:
- Task - 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: get:
description: API for create ArticleFiles description: API for create ArticleFiles
parameters:
- description: Filename
in: path
name: id
required: true
type: string
responses: responses:
"200": "200":
description: OK description: OK

View File

@ -42,6 +42,8 @@ func main() {
fx.Provide(database.NewDatabase), fx.Provide(database.NewDatabase),
// middleware // middleware
fx.Provide(middleware.NewMiddleware), fx.Provide(middleware.NewMiddleware),
// data storage
fx.Provide(config.NewMinio),
// router // router
fx.Provide(router.NewRouter), fx.Provide(router.NewRouter),