feat: update article files upload and setup ci/cd
This commit is contained in:
parent
4795dbdfac
commit
aa2f46b410
|
|
@ -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
|
||||||
|
|
@ -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"]
|
||||||
|
|
@ -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"))
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -20,8 +20,9 @@ import (
|
||||||
|
|
||||||
// ArticleFilesService
|
// ArticleFilesService
|
||||||
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{
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
2
main.go
2
main.go
|
|
@ -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),
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue