feat: update article files async, magazine, and magazine files

This commit is contained in:
hanif salafi 2025-01-17 14:27:10 +07:00
parent 6c3858bff6
commit 5eeebdcdf3
19 changed files with 1410 additions and 534 deletions

View File

@ -3,15 +3,22 @@ package entity
import "time"
type MagazineFiles struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Title string `json:"title" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"`
MagazineId int `json:"magazine_id" gorm:"type:int4"`
DownloadCount int `json:"download_count" gorm:"type:int4"`
StatusId int `json:"status_id" gorm:"type:int4"`
IsPublish bool `json:"is_publish" gorm:"type:bool"`
PublishedAt time.Time `json:"published_at" gorm:"type:timestamp"`
IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
}
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Title string `json:"title" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"`
MagazineId uint `json:"magazine_id" gorm:"type:int4"`
DownloadCount *int `json:"download_count" gorm:"type:int4"`
StatusId int `json:"status_id" gorm:"type:int4"`
IsPublish *bool `json:"is_publish" gorm:"type:bool"`
FilePath *string `json:"file_path" gorm:"type:varchar"`
FileUrl *string `json:"file_url" gorm:"type:varchar"`
FileName *string `json:"file_name" gorm:"type:varchar"`
FileAlt *string `json:"file_alt" gorm:"type:varchar"`
WidthPixel *string `json:"width_pixel" gorm:"type:varchar"`
HeightPixel *string `json:"height_pixel" gorm:"type:varchar"`
Size *string `json:"size" gorm:"type:varchar"`
PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"`
IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
}

View File

@ -3,17 +3,17 @@ package entity
import "time"
type Magazines struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Title string `json:"title" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"`
ThumbnailPath string `json:"thumbnail_path" gorm:"type:varchar"`
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Title string `json:"title" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"`
ThumbnailPath string `json:"thumbnail_path" gorm:"type:varchar"`
ThumbnailUrl string `json:"thumbnail_url" gorm:"type:varchar"`
PageUrl string `json:"page_url" gorm:"type:varchar"`
CreatedById int `json:"created_by_id" gorm:"type:int4"`
StatusId int `json:"status_id" gorm:"type:int4"`
IsPublish bool `json:"is_publish" gorm:"type:bool"`
PublishedAt time.Time `json:"published_at" gorm:"type:timestamp"`
IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
}
PageUrl string `json:"page_url" gorm:"type:varchar"`
CreatedById *uint `json:"created_by_id" gorm:"type:int4"`
StatusId int `json:"status_id" gorm:"type:int4"`
IsPublish bool `json:"is_publish" gorm:"type:bool"`
PublishedAt time.Time `json:"published_at" gorm:"type:timestamp"`
IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
}

View File

@ -37,6 +37,7 @@ type ArticleFilesService interface {
All(req request.ArticleFilesQueryRequest) (articleFiles []*response.ArticleFilesResponse, paging paginator.Pagination, err error)
Show(id uint) (articleFiles *response.ArticleFilesResponse, err error)
Save(c *fiber.Ctx, id uint) error
SaveAsync(c *fiber.Ctx, id uint) error
Update(id uint, req request.ArticleFilesUpdateRequest) (err error)
GetUploadStatus(c *fiber.Ctx) (progress int, err error)
Delete(id uint) error
@ -87,7 +88,7 @@ func (_i *articleFilesService) Show(id uint) (articleFiles *response.ArticleFile
return mapper.ArticleFilesResponseMapper(result), nil
}
func (_i *articleFilesService) Save(c *fiber.Ctx, id uint) (err error) {
func (_i *articleFilesService) SaveAsync(c *fiber.Ctx, id uint) (err error) {
bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
ctx := context.Background()
@ -185,7 +186,7 @@ func (_i *articleFilesService) Save(c *fiber.Ctx, id uint) (err error) {
return
}
func (_i *articleFilesService) SaveSync(c *fiber.Ctx, id uint) (err error) {
func (_i *articleFilesService) Save(c *fiber.Ctx, id uint) (err error) {
bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
form, err := c.MultipartForm()

View File

@ -21,6 +21,7 @@ type MagazineFilesController interface {
Save(c *fiber.Ctx) error
Update(c *fiber.Ctx) error
Delete(c *fiber.Ctx) error
Viewer(c *fiber.Ctx) error
}
func NewMagazineFilesController(magazineFilesService service.MagazineFilesService) MagazineFilesController {
@ -35,10 +36,9 @@ func NewMagazineFilesController(magazineFilesService service.MagazineFilesServic
// @Tags Magazine Files
// @Security Bearer
// @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
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazine-files [get]
func (_i *magazineFilesController) All(c *fiber.Ctx) error {
paginate, err := paginator.Paginate(c)
@ -68,10 +68,9 @@ func (_i *magazineFilesController) All(c *fiber.Ctx) error {
// @Security Bearer
// @Param id path int true "MagazineFiles ID"
// @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
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazine-files/{id} [get]
func (_i *magazineFilesController) Show(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
@ -95,25 +94,26 @@ func (_i *magazineFilesController) Show(c *fiber.Ctx) error {
// @Description API for create MagazineFiles
// @Tags Magazine Files
// @Security Bearer
// @Body request.MagazineFilesCreateRequest
// @Param files formData file true "Upload file" multiple true
// @Param magazineId path int true "Magazine ID"
// @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 /magazine-files [post]
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazine-files/{magazineId} [post]
func (_i *magazineFilesController) Save(c *fiber.Ctx) error {
req := new(request.MagazineFilesCreateRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
id, err := strconv.ParseUint(c.Params("articleId"), 10, 0)
if err != nil {
return err
}
err := _i.magazineFilesService.Save(*req)
err = _i.magazineFilesService.Save(c, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"MagazineFiles successfully created"},
})
}
@ -126,10 +126,9 @@ func (_i *magazineFilesController) Save(c *fiber.Ctx) error {
// @Body request.MagazineFilesUpdateRequest
// @Param id path int true "MagazineFiles ID"
// @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
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazine-files/{id} [put]
func (_i *magazineFilesController) Update(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
@ -159,10 +158,9 @@ func (_i *magazineFilesController) Update(c *fiber.Ctx) error {
// @Security Bearer
// @Param id path int true "MagazineFiles ID"
// @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
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazine-files/{id} [delete]
func (_i *magazineFilesController) Delete(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
@ -179,3 +177,18 @@ func (_i *magazineFilesController) Delete(c *fiber.Ctx) error {
Messages: utilRes.Messages{"MagazineFiles successfully deleted"},
})
}
// Viewer MagazineFiles
// @Summary Create MagazineFiles
// @Description API for create MagazineFiles
// @Tags Magazine Files
// @Security Bearer
// @Param filename path string true "Magazine File Name"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazine-files/viewer/{filename} [get]
func (_i *magazineFilesController) Viewer(c *fiber.Ctx) error {
return _i.magazineFilesService.Viewer(c)
}

View File

@ -46,8 +46,9 @@ func (_i *MagazineFilesRouter) RegisterMagazineFilesRoutes() {
_i.App.Route("/magazine-files", func(router fiber.Router) {
router.Get("/", magazineFilesController.All)
router.Get("/:id", magazineFilesController.Show)
router.Post("/", magazineFilesController.Save)
router.Post("/:magazineId", magazineFilesController.Save)
router.Put("/:id", magazineFilesController.Update)
router.Delete("/:id", magazineFilesController.Delete)
router.Get("/viewer/:filename", magazineFilesController.Viewer)
})
}

View File

@ -15,6 +15,8 @@ type magazineFilesRepository struct {
type MagazineFilesRepository interface {
GetAll(req request.MagazineFilesQueryRequest) (magazineFiless []*entity.MagazineFiles, paging paginator.Pagination, err error)
FindOne(id uint) (magazineFiles *entity.MagazineFiles, err error)
FindByArticle(magazineId uint) (magazineFiles []*entity.MagazineFiles, err error)
FindByFilename(filename string) (magazineFiles *entity.MagazineFiles, err error)
Create(magazineFiles *entity.MagazineFiles) (err error)
Update(id uint, magazineFiles *entity.MagazineFiles) (err error)
Delete(id uint) (err error)
@ -46,6 +48,23 @@ func (_i *magazineFilesRepository) GetAll(req request.MagazineFilesQueryRequest)
return
}
func (_i *magazineFilesRepository) FindByArticle(magazineId uint) (magazineFiles []*entity.MagazineFiles, err error) {
if err := _i.DB.DB.Where("magazine_id = ?", magazineId).Find(&magazineFiles).Error; err != nil {
return nil, err
}
return magazineFiles, nil
}
func (_i *magazineFilesRepository) FindByFilename(magazineFilename string) (magazineFiles *entity.MagazineFiles, err error) {
if err := _i.DB.DB.Where("file_name = ?", magazineFilename).First(&magazineFiles).Error; err != nil {
return nil, err
}
return magazineFiles, nil
}
func (_i *magazineFilesRepository) FindOne(id uint) (magazineFiles *entity.MagazineFiles, err error) {
if err := _i.DB.DB.First(&magazineFiles, id).Error; err != nil {
return nil, err
@ -66,4 +85,4 @@ func (_i *magazineFilesRepository) Update(id uint, magazineFiles *entity.Magazin
func (_i *magazineFilesRepository) Delete(id uint) error {
return _i.DB.DB.Delete(&entity.MagazineFiles{}, id).Error
}
}

View File

@ -23,14 +23,20 @@ type MagazineFilesQueryRequest struct {
}
type MagazineFilesCreateRequest struct {
Title string `json:"title" validate:"required"`
Description string `json:"description" validate:"required"`
MagazineId int `json:"magazine_id" validate:"required"`
DownloadCount int `json:"download_count" validate:"required"`
StatusId int `json:"status_id" validate:"required"`
IsPublish bool `json:"is_publish" validate:"required"`
PublishedAt time.Time `json:"published_at" validate:"required"`
IsActive bool `json:"is_active" validate:"required"`
Title string `json:"title" validate:"required"`
Description string `json:"description" validate:"required"`
MagazineId uint `json:"magazine_id" validate:"required"`
StatusId int `json:"status_id" validate:"required"`
DownloadCount *int `json:"download_count"`
FilePath *string `json:"filePath"`
FileUrl *string `json:"fileUrl"`
FileName *string `json:"fileName"`
FileAlt *string `json:"fileAlt"`
WidthPixel *string `json:"widthPixel"`
HeightPixel *string `json:"heightPixel"`
Size *string `json:"size"`
IsPublish *bool `json:"is_publish"`
PublishedAt *time.Time `json:"published_at"`
}
func (req MagazineFilesCreateRequest) ToEntity() *entity.MagazineFiles {
@ -39,25 +45,35 @@ func (req MagazineFilesCreateRequest) ToEntity() *entity.MagazineFiles {
Description: req.Description,
MagazineId: req.MagazineId,
DownloadCount: req.DownloadCount,
FilePath: req.FilePath,
FileUrl: req.FileUrl,
FileName: req.FileName,
FileAlt: req.FileAlt,
WidthPixel: req.WidthPixel,
HeightPixel: req.HeightPixel,
Size: req.Size,
StatusId: req.StatusId,
IsPublish: req.IsPublish,
PublishedAt: req.PublishedAt,
IsActive: req.IsActive,
}
}
type MagazineFilesUpdateRequest struct {
ID uint `json:"id" validate:"required"`
Title string `json:"title" validate:"required"`
Description string `json:"description" validate:"required"`
MagazineId int `json:"magazine_id" validate:"required"`
DownloadCount int `json:"download_count" validate:"required"`
StatusId int `json:"status_id" validate:"required"`
IsPublish bool `json:"is_publish" validate:"required"`
PublishedAt time.Time `json:"published_at" validate:"required"`
IsActive bool `json:"is_active" validate:"required"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID uint `json:"id" validate:"required"`
Title string `json:"title" validate:"required"`
Description string `json:"description" validate:"required"`
MagazineId uint `json:"magazine_id" validate:"required"`
DownloadCount *int `json:"download_count" validate:"required"`
StatusId int `json:"status_id" validate:"required"`
FilePath *string `json:"filePath"`
FileUrl *string `json:"fileUrl"`
FileName *string `json:"fileName"`
FileAlt *string `json:"fileAlt"`
WidthPixel *string `json:"widthPixel"`
HeightPixel *string `json:"heightPixel"`
Size *string `json:"size"`
IsPublish *bool `json:"is_publish"`
PublishedAt *time.Time `json:"published_at"`
}
func (req MagazineFilesUpdateRequest) ToEntity() *entity.MagazineFiles {
@ -67,11 +83,15 @@ func (req MagazineFilesUpdateRequest) ToEntity() *entity.MagazineFiles {
Description: req.Description,
MagazineId: req.MagazineId,
DownloadCount: req.DownloadCount,
FilePath: req.FilePath,
FileUrl: req.FileUrl,
FileName: req.FileName,
FileAlt: req.FileAlt,
WidthPixel: req.WidthPixel,
HeightPixel: req.HeightPixel,
Size: req.Size,
StatusId: req.StatusId,
IsPublish: req.IsPublish,
PublishedAt: req.PublishedAt,
IsActive: req.IsActive,
CreatedAt: req.CreatedAt,
UpdatedAt: req.UpdatedAt,
}
}

View File

@ -1,35 +1,50 @@
package service
import (
"context"
"github.com/gofiber/fiber/v2"
"github.com/minio/minio-go/v7"
"github.com/rs/zerolog"
"go-humas-be/app/module/magazine_files/mapper"
"go-humas-be/app/module/magazine_files/repository"
"go-humas-be/app/module/magazine_files/request"
"go-humas-be/app/module/magazine_files/response"
minioStorage "go-humas-be/config/config"
"go-humas-be/utils/paginator"
"io"
"log"
"math/rand"
"mime"
"path/filepath"
"strconv"
"strings"
"time"
)
// MagazineFilesService
type magazineFilesService struct {
Repo repository.MagazineFilesRepository
Log zerolog.Logger
Repo repository.MagazineFilesRepository
Log zerolog.Logger
MinioStorage *minioStorage.MinioStorage
}
// MagazineFilesService define interface of IMagazineFilesService
type MagazineFilesService interface {
All(req request.MagazineFilesQueryRequest) (magazineFiles []*response.MagazineFilesResponse, paging paginator.Pagination, err error)
Show(id uint) (magazineFiles *response.MagazineFilesResponse, err error)
Save(req request.MagazineFilesCreateRequest) (err error)
Save(c *fiber.Ctx, id uint) (err error)
Update(id uint, req request.MagazineFilesUpdateRequest) (err error)
Delete(id uint) error
Viewer(c *fiber.Ctx) error
}
// NewMagazineFilesService init MagazineFilesService
func NewMagazineFilesService(repo repository.MagazineFilesRepository, log zerolog.Logger) MagazineFilesService {
func NewMagazineFilesService(repo repository.MagazineFilesRepository, log zerolog.Logger, minioStorage *minioStorage.MinioStorage) MagazineFilesService {
return &magazineFilesService{
Repo: repo,
Log: log,
Repo: repo,
Log: log,
MinioStorage: minioStorage,
}
}
@ -56,10 +71,145 @@ func (_i *magazineFilesService) Show(id uint) (magazineFiles *response.MagazineF
return mapper.MagazineFilesResponseMapper(result), nil
}
func (_i *magazineFilesService) Save(req request.MagazineFilesCreateRequest) (err error) {
_i.Log.Info().Interface("data", req).Msg("")
func (_i *magazineFilesService) Save(c *fiber.Ctx, id uint) (err error) {
bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
return _i.Repo.Create(req.ToEntity())
form, err := c.MultipartForm()
if err != nil {
return err
}
//filess := form.File["files"]
// Create minio connection.
minioClient, err := _i.MinioStorage.ConnectMinio()
if err != nil {
// Return status 500 and minio connection error.
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": true,
"msg": err.Error(),
})
}
for _, files := range form.File {
_i.Log.Info().Str("timestamp", time.Now().
Format(time.RFC3339)).Str("Service:Resource", "Uploader:: top").
Interface("files", files).Msg("")
for _, fileHeader := range files {
_i.Log.Info().Str("timestamp", time.Now().
Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop").
Interface("data", fileHeader).Msg("")
src, err := fileHeader.Open()
if err != nil {
return err
}
defer src.Close()
filename := filepath.Base(fileHeader.Filename)
filenameAlt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))])
filename = strings.ReplaceAll(filename, " ", "")
filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))])
extension := filepath.Ext(fileHeader.Filename)[1:]
rand.New(rand.NewSource(time.Now().UnixNano()))
randUniqueId := rand.Intn(1000000)
newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId)
newFilename := newFilenameWithoutExt + "." + extension
objectName := "magazines/upload/" + newFilename
fileSize := strconv.FormatInt(fileHeader.Size, 10)
req := request.MagazineFilesCreateRequest{
MagazineId: id,
Title: newFilename,
FilePath: &objectName,
FileName: &newFilename,
FileAlt: &filenameAlt,
Size: &fileSize,
}
err = _i.Repo.Create(req.ToEntity())
if err != nil {
return err
}
// Upload file ke MinIO
_, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, fileHeader.Size, minio.PutObjectOptions{})
if err != nil {
return err
}
}
}
_i.Log.Info().Str("timestamp", time.Now().
Format(time.RFC3339)).Str("Service:Resource", "User:All").
Interface("data", "Successfully uploaded").Msg("")
return
}
func (_i *magazineFilesService) Viewer(c *fiber.Ctx) (err error) {
filename := c.Params("filename")
result, err := _i.Repo.FindByFilename(filename)
if err != nil {
return err
}
ctx := context.Background()
bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
objectName := *result.FilePath
_i.Log.Info().Str("timestamp", time.Now().
Format(time.RFC3339)).Str("Service:Resource", "Article:Uploads").
Interface("data", objectName).Msg("")
// Create minio connection.
minioClient, err := _i.MinioStorage.ConnectMinio()
if err != nil {
// Return status 500 and minio connection error.
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": true,
"msg": err.Error(),
})
}
fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
if err != nil {
log.Fatalln(err)
}
defer fileContent.Close()
// Tentukan Content-Type berdasarkan ekstensi file
contentType := mime.TypeByExtension("." + getFileExtension(objectName))
if contentType == "" {
contentType = "application/octet-stream" // fallback jika tidak ada tipe MIME yang cocok
}
c.Set("Content-Type", contentType)
if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil {
return err
}
return
}
func getFileExtension(filename string) string {
// split file name
parts := strings.Split(filename, ".")
// jika tidak ada ekstensi, kembalikan string kosong
if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") {
return ""
}
// ambil ekstensi terakhir
return parts[len(parts)-1]
}
func (_i *magazineFilesService) Update(id uint, req request.MagazineFilesUpdateRequest) (err error) {

View File

@ -35,10 +35,9 @@ func NewMagazinesController(magazinesService service.MagazinesService) Magazines
// @Tags Magazines
// @Security Bearer
// @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
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazines [get]
func (_i *magazinesController) All(c *fiber.Ctx) error {
paginate, err := paginator.Paginate(c)
@ -68,10 +67,9 @@ func (_i *magazinesController) All(c *fiber.Ctx) error {
// @Security Bearer
// @Param id path int true "Magazines ID"
// @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
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazines/{id} [get]
func (_i *magazinesController) Show(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
@ -95,12 +93,12 @@ func (_i *magazinesController) Show(c *fiber.Ctx) error {
// @Description API for create Magazines
// @Tags Magazines
// @Security Bearer
// @Body request.MagazinesCreateRequest
// @Param Authorization header string true "Insert your access token" default(Bearer <Add access token here>)
// @Param payload body request.MagazinesCreateRequest true "Required payload"
// @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
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazines [post]
func (_i *magazinesController) Save(c *fiber.Ctx) error {
req := new(request.MagazinesCreateRequest)
@ -108,7 +106,8 @@ func (_i *magazinesController) Save(c *fiber.Ctx) error {
return err
}
err := _i.magazinesService.Save(*req)
authToken := c.Get("Authorization")
err := _i.magazinesService.Save(*req, authToken)
if err != nil {
return err
}
@ -123,13 +122,12 @@ func (_i *magazinesController) Save(c *fiber.Ctx) error {
// @Description API for update Magazines
// @Tags Magazines
// @Security Bearer
// @Body request.MagazinesUpdateRequest
// @Param id path int true "Magazines ID"
// @Param payload body request.MagazinesUpdateRequest true "Required payload"
// @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
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazines/{id} [put]
func (_i *magazinesController) Update(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
@ -159,10 +157,9 @@ func (_i *magazinesController) Update(c *fiber.Ctx) error {
// @Security Bearer
// @Param id path int true "Magazines ID"
// @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
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /magazines/{id} [delete]
func (_i *magazinesController) Delete(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 0)

View File

@ -6,30 +6,34 @@ import (
"go-humas-be/app/module/magazines/repository"
"go-humas-be/app/module/magazines/request"
"go-humas-be/app/module/magazines/response"
usersRepository "go-humas-be/app/module/users/repository"
"go-humas-be/utils/paginator"
utilSvc "go-humas-be/utils/service"
)
// MagazinesService
type magazinesService struct {
Repo repository.MagazinesRepository
Log zerolog.Logger
Repo repository.MagazinesRepository
UsersRepo usersRepository.UsersRepository
Log zerolog.Logger
}
// MagazinesService define interface of IMagazinesService
type MagazinesService interface {
All(req request.MagazinesQueryRequest) (magazines []*response.MagazinesResponse, paging paginator.Pagination, err error)
Show(id uint) (magazines *response.MagazinesResponse, err error)
Save(req request.MagazinesCreateRequest) (err error)
Save(req request.MagazinesCreateRequest, authToken string) (err error)
Update(id uint, req request.MagazinesUpdateRequest) (err error)
Delete(id uint) error
}
// NewMagazinesService init MagazinesService
func NewMagazinesService(repo repository.MagazinesRepository, log zerolog.Logger) MagazinesService {
func NewMagazinesService(repo repository.MagazinesRepository, usersRepo usersRepository.UsersRepository, log zerolog.Logger) MagazinesService {
return &magazinesService{
Repo: repo,
Log: log,
Repo: repo,
UsersRepo: usersRepo,
Log: log,
}
}
@ -56,10 +60,14 @@ func (_i *magazinesService) Show(id uint) (magazines *response.MagazinesResponse
return mapper.MagazinesResponseMapper(result), nil
}
func (_i *magazinesService) Save(req request.MagazinesCreateRequest) (err error) {
func (_i *magazinesService) Save(req request.MagazinesCreateRequest, authToken string) (err error) {
_i.Log.Info().Interface("data", req).Msg("")
newReq := req.ToEntity()
return _i.Repo.Create(req.ToEntity())
createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
newReq.CreatedById = &createdBy.ID
return _i.Repo.Create(newReq)
}
func (_i *magazinesService) Update(id uint, req request.MagazinesUpdateRequest) (err error) {

View File

@ -23,6 +23,7 @@ type UsersController interface {
Save(c *fiber.Ctx) error
Update(c *fiber.Ctx) error
Login(c *fiber.Ctx) error
ParetoLogin(c *fiber.Ctx) error
Delete(c *fiber.Ctx) error
}
@ -233,6 +234,39 @@ func (_i *usersController) Login(c *fiber.Ctx) error {
})
}
// ParetoLogin Users
// @Summary ParetoLogin Users
// @Description API for ParetoLogin Users
// @Tags Users
// @Security Bearer
// @Param payload body request.UserLogin true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /users/pareto-login [post]
func (_i *usersController) ParetoLogin(c *fiber.Ctx) error {
req := new(request.UserLogin)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
loginResponse, err := _i.usersService.ParetoLogin(*req)
if err != nil {
return utilRes.Resp(c, utilRes.Response{
Success: false,
Code: 401,
Messages: utilRes.Messages{err.Error()},
})
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Users successfully login"},
Data: loginResponse,
})
}
// Delete Users
// @Summary delete Users
// @Description API for delete Users

View File

@ -26,3 +26,7 @@ type UsersResponse struct {
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type ParetoLoginResponse struct {
AccessToken string `json:"accessToken"`
}

View File

@ -1,6 +1,10 @@
package service
import (
paseto "aidanwoods.dev/go-paseto"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/Nerzal/gocloak/v13"
"github.com/rs/zerolog"
"go-humas-be/app/database/entity"
@ -12,6 +16,8 @@ import (
"go-humas-be/config/config"
"go-humas-be/utils/paginator"
utilSvc "go-humas-be/utils/service"
"strings"
"time"
)
// UsersService
@ -29,6 +35,7 @@ type UsersService interface {
ShowUserInfo(authToken string) (users *response.UsersResponse, err error)
Save(req request.UsersCreateRequest, authToken string) (userReturn *entity.Users, err error)
Login(req request.UserLogin) (res *gocloak.JWT, err error)
ParetoLogin(req request.UserLogin) (res *response.ParetoLoginResponse, err error)
Update(id uint, req request.UsersUpdateRequest) (err error)
Delete(id uint) error
}
@ -110,6 +117,81 @@ func (_i *usersService) Login(req request.UserLogin) (res *gocloak.JWT, err erro
return loginResponse, nil
}
func (_i *usersService) ParetoLogin(req request.UserLogin) (res *response.ParetoLoginResponse, err error) {
_i.Log.Info().Interface("data", req).Msg("")
var loginResponse *gocloak.JWT
token := paseto.NewToken()
secretKeyHex := "bdc42b1a0ba2bac3e27ba84241f9de06dee71b70f838af8d1beb0417f03d1d00"
secretKey, _ := paseto.V4SymmetricKeyFromHex(secretKeyHex)
// secretKey := paseto.NewV4SymmetricKey() // to change the secretKey periodically
if req.RefreshToken == nil {
loginResponse, err = _i.Keycloak.Login(*req.Username, *req.Password)
} else {
// Retrieve Refresh Token
parser := paseto.NewParser()
verifiedToken, err := parser.ParseV4Local(secretKey, *req.RefreshToken, nil)
if err != nil {
panic(err)
}
refreshToken, _ := verifiedToken.GetString("refresh_token")
_i.Log.Info().Interface("Pareto parse refresh token", refreshToken).Msg("")
loginResponse, err = _i.Keycloak.RefreshToken(refreshToken)
}
_i.Log.Info().Interface("loginResponse", loginResponse).Msg("")
if err != nil {
return nil, err
}
parseToken, err := ParseJWTToken(loginResponse.AccessToken)
if err != nil {
return nil, err
}
issuedAt := parseToken["iat"].(float64)
expirationTime := parseToken["exp"].(float64)
issuer := parseToken["iss"].(string)
jti := parseToken["jti"].(string)
subject := parseToken["sub"].(string)
token.SetIssuedAt(time.Unix(int64(issuedAt), 0))
token.SetNotBefore(time.Unix(int64(issuedAt), 0))
token.SetExpiration(time.Unix(int64(expirationTime), 0))
token.SetIssuer(issuer)
token.SetJti(jti)
token.SetSubject(subject)
token.SetString("access_token", loginResponse.AccessToken)
token.SetString("refresh_token", loginResponse.RefreshToken)
_i.Log.Info().Interface("Pareto Generated Key", secretKey.ExportHex()).Msg("")
tokenEncrypted := token.V4Encrypt(secretKey, nil)
parser := paseto.NewParser()
verifiedToken, err := parser.ParseV4Local(secretKey, tokenEncrypted, nil)
if err != nil {
panic(err)
}
tokenParsing, _ := verifiedToken.GetString("access_token")
_i.Log.Info().Interface("Pareto parse token", tokenParsing).Msg("")
resLogin := &response.ParetoLoginResponse{
AccessToken: tokenEncrypted,
}
if err != nil {
return nil, err
}
return resLogin, nil
}
func (_i *usersService) Update(id uint, req request.UsersUpdateRequest) (err error) {
_i.Log.Info().Interface("data", req).Msg("")
newReq := req.ToEntity()
@ -137,3 +219,25 @@ func (_i *usersService) Delete(id uint) error {
result.IsActive = &isActive
return _i.Repo.Update(id, result)
}
func ParseJWTToken(token string) (map[string]interface{}, error) {
// Pisahkan JWT menjadi 3 bagian: header, payload, dan signature
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("Invalid JWT token")
}
// Decode bagian payload (index ke-1)
payloadData, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("Failed to decode payload: %v", err)
}
// Ubah payload menjadi map[string]interface{}
var payload map[string]interface{}
if err := json.Unmarshal(payloadData, &payload); err != nil {
return nil, fmt.Errorf("Failed to parse payload JSON: %v", err)
}
return payload, nil
}

View File

@ -50,6 +50,7 @@ func (_i *UsersRouter) RegisterUsersRoutes() {
router.Post("/", usersController.Save)
router.Put("/:id", usersController.Update)
router.Post("/login", usersController.Login)
router.Post("/pareto-login", usersController.ParetoLogin)
router.Delete("/:id", usersController.Delete)
})
}

View File

@ -2813,33 +2813,29 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
},
"post": {
}
},
"/magazine-files/viewer/{filename}": {
"get": {
"security": [
{
"Bearer": []
@ -2850,6 +2846,15 @@ const docTemplate = `{
"Magazine Files"
],
"summary": "Create MagazineFiles",
"parameters": [
{
"type": "string",
"description": "Magazine File Name",
"name": "filename",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
@ -2857,28 +2862,22 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -2912,28 +2911,22 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -2965,28 +2958,22 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3018,29 +3005,79 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.InternalServerError"
}
}
}
}
},
"/magazine-files/{magazineId}": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "API for create MagazineFiles",
"tags": [
"Magazine Files"
],
"summary": "Create MagazineFiles",
"parameters": [
{
"type": "file",
"description": "Upload file",
"name": "files",
"in": "formData",
"required": true
},
{
"type": "integer",
"description": "Magazine ID",
"name": "magazineId",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.InternalServerError"
}
}
}
}
@ -3064,28 +3101,22 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3101,6 +3132,25 @@ const docTemplate = `{
"Magazines"
],
"summary": "Create Magazines",
"parameters": [
{
"type": "string",
"default": "Bearer \u003cAdd access token here\u003e",
"description": "Insert your access token",
"name": "Authorization",
"in": "header",
"required": true
},
{
"description": "Required payload",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.MagazinesCreateRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
@ -3108,28 +3158,22 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3163,28 +3207,22 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3207,6 +3245,15 @@ const docTemplate = `{
"name": "id",
"in": "path",
"required": true
},
{
"description": "Required payload",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.MagazinesUpdateRequest"
}
}
],
"responses": {
@ -3216,28 +3263,22 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3269,28 +3310,22 @@ const docTemplate = `{
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -5886,6 +5921,57 @@ const docTemplate = `{
}
}
},
"/users/pareto-login": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "API for ParetoLogin Users",
"tags": [
"Users"
],
"summary": "ParetoLogin Users",
"parameters": [
{
"description": "Required payload",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.UserLogin"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.InternalServerError"
}
}
}
}
},
"/users/{id}": {
"put": {
"security": [
@ -6376,6 +6462,110 @@ const docTemplate = `{
}
}
},
"request.MagazinesCreateRequest": {
"type": "object",
"required": [
"created_by_id",
"description",
"is_active",
"is_publish",
"page_url",
"published_at",
"status_id",
"thumbnail_path",
"thumbnail_url",
"title"
],
"properties": {
"created_by_id": {
"type": "integer"
},
"description": {
"type": "string"
},
"is_active": {
"type": "boolean"
},
"is_publish": {
"type": "boolean"
},
"page_url": {
"type": "string"
},
"published_at": {
"type": "string"
},
"status_id": {
"type": "integer"
},
"thumbnail_path": {
"type": "string"
},
"thumbnail_url": {
"type": "string"
},
"title": {
"type": "string"
}
}
},
"request.MagazinesUpdateRequest": {
"type": "object",
"required": [
"created_by_id",
"description",
"id",
"is_active",
"is_publish",
"page_url",
"published_at",
"status_id",
"thumbnail_path",
"thumbnail_url",
"title"
],
"properties": {
"created_at": {
"type": "string"
},
"created_by_id": {
"type": "integer"
},
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"is_active": {
"type": "boolean"
},
"is_publish": {
"type": "boolean"
},
"page_url": {
"type": "string"
},
"published_at": {
"type": "string"
},
"status_id": {
"type": "integer"
},
"thumbnail_path": {
"type": "string"
},
"thumbnail_url": {
"type": "string"
},
"title": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"request.MasterMenusCreateRequest": {
"type": "object",
"required": [

View File

@ -2802,33 +2802,29 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
},
"post": {
}
},
"/magazine-files/viewer/{filename}": {
"get": {
"security": [
{
"Bearer": []
@ -2839,6 +2835,15 @@
"Magazine Files"
],
"summary": "Create MagazineFiles",
"parameters": [
{
"type": "string",
"description": "Magazine File Name",
"name": "filename",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
@ -2846,28 +2851,22 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -2901,28 +2900,22 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -2954,28 +2947,22 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3007,29 +2994,79 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.InternalServerError"
}
}
}
}
},
"/magazine-files/{magazineId}": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "API for create MagazineFiles",
"tags": [
"Magazine Files"
],
"summary": "Create MagazineFiles",
"parameters": [
{
"type": "file",
"description": "Upload file",
"name": "files",
"in": "formData",
"required": true
},
{
"type": "integer",
"description": "Magazine ID",
"name": "magazineId",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.InternalServerError"
}
}
}
}
@ -3053,28 +3090,22 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3090,6 +3121,25 @@
"Magazines"
],
"summary": "Create Magazines",
"parameters": [
{
"type": "string",
"default": "Bearer \u003cAdd access token here\u003e",
"description": "Insert your access token",
"name": "Authorization",
"in": "header",
"required": true
},
{
"description": "Required payload",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.MagazinesCreateRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
@ -3097,28 +3147,22 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3152,28 +3196,22 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3196,6 +3234,15 @@
"name": "id",
"in": "path",
"required": true
},
{
"description": "Required payload",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.MagazinesUpdateRequest"
}
}
],
"responses": {
@ -3205,28 +3252,22 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -3258,28 +3299,22 @@
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"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"
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
"$ref": "#/definitions/response.InternalServerError"
}
}
}
@ -5875,6 +5910,57 @@
}
}
},
"/users/pareto-login": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "API for ParetoLogin Users",
"tags": [
"Users"
],
"summary": "ParetoLogin Users",
"parameters": [
{
"description": "Required payload",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.UserLogin"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.BadRequestError"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.UnauthorizedError"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.InternalServerError"
}
}
}
}
},
"/users/{id}": {
"put": {
"security": [
@ -6365,6 +6451,110 @@
}
}
},
"request.MagazinesCreateRequest": {
"type": "object",
"required": [
"created_by_id",
"description",
"is_active",
"is_publish",
"page_url",
"published_at",
"status_id",
"thumbnail_path",
"thumbnail_url",
"title"
],
"properties": {
"created_by_id": {
"type": "integer"
},
"description": {
"type": "string"
},
"is_active": {
"type": "boolean"
},
"is_publish": {
"type": "boolean"
},
"page_url": {
"type": "string"
},
"published_at": {
"type": "string"
},
"status_id": {
"type": "integer"
},
"thumbnail_path": {
"type": "string"
},
"thumbnail_url": {
"type": "string"
},
"title": {
"type": "string"
}
}
},
"request.MagazinesUpdateRequest": {
"type": "object",
"required": [
"created_by_id",
"description",
"id",
"is_active",
"is_publish",
"page_url",
"published_at",
"status_id",
"thumbnail_path",
"thumbnail_url",
"title"
],
"properties": {
"created_at": {
"type": "string"
},
"created_by_id": {
"type": "integer"
},
"description": {
"type": "string"
},
"id": {
"type": "integer"
},
"is_active": {
"type": "boolean"
},
"is_publish": {
"type": "boolean"
},
"page_url": {
"type": "string"
},
"published_at": {
"type": "string"
},
"status_id": {
"type": "integer"
},
"thumbnail_path": {
"type": "string"
},
"thumbnail_url": {
"type": "string"
},
"title": {
"type": "string"
},
"updated_at": {
"type": "string"
}
}
},
"request.MasterMenusCreateRequest": {
"type": "object",
"required": [

View File

@ -265,6 +265,81 @@ definitions:
- slug
- title
type: object
request.MagazinesCreateRequest:
properties:
created_by_id:
type: integer
description:
type: string
is_active:
type: boolean
is_publish:
type: boolean
page_url:
type: string
published_at:
type: string
status_id:
type: integer
thumbnail_path:
type: string
thumbnail_url:
type: string
title:
type: string
required:
- created_by_id
- description
- is_active
- is_publish
- page_url
- published_at
- status_id
- thumbnail_path
- thumbnail_url
- title
type: object
request.MagazinesUpdateRequest:
properties:
created_at:
type: string
created_by_id:
type: integer
description:
type: string
id:
type: integer
is_active:
type: boolean
is_publish:
type: boolean
page_url:
type: string
published_at:
type: string
status_id:
type: integer
thumbnail_path:
type: string
thumbnail_url:
type: string
title:
type: string
updated_at:
type: string
required:
- created_by_id
- description
- id
- is_active
- is_publish
- page_url
- published_at
- status_id
- thumbnail_path
- thumbnail_url
- title
type: object
request.MasterMenusCreateRequest:
properties:
description:
@ -2381,55 +2456,23 @@ paths:
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"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'
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Get all MagazineFiles
tags:
- Magazine Files
post:
description: API for create MagazineFiles
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: Create MagazineFiles
tags:
- Magazine Files
/magazine-files/{id}:
delete:
description: API for delete MagazineFiles
@ -2444,22 +2487,18 @@ paths:
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"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'
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: delete MagazineFiles
@ -2478,22 +2517,18 @@ paths:
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"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'
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Get one MagazineFiles
@ -2512,27 +2547,90 @@ paths:
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"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'
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Update MagazineFiles
tags:
- Magazine Files
/magazine-files/{magazineId}:
post:
description: API for create MagazineFiles
parameters:
- description: Upload file
in: formData
name: files
required: true
type: file
- description: Magazine ID
in: path
name: magazineId
required: true
type: integer
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Create MagazineFiles
tags:
- Magazine Files
/magazine-files/viewer/{filename}:
get:
description: API for create MagazineFiles
parameters:
- description: Magazine File Name
in: path
name: filename
required: true
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Create MagazineFiles
tags:
- Magazine Files
/magazines:
get:
description: API for getting all Magazines
@ -2541,22 +2639,18 @@ paths:
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"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'
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Get all Magazines
@ -2564,27 +2658,36 @@ paths:
- Magazines
post:
description: API for create Magazines
parameters:
- default: Bearer <Add access token here>
description: Insert your access token
in: header
name: Authorization
required: true
type: string
- description: Required payload
in: body
name: payload
required: true
schema:
$ref: '#/definitions/request.MagazinesCreateRequest'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"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'
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Create Magazines
@ -2604,22 +2707,18 @@ paths:
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"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'
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Delete Magazines
@ -2638,22 +2737,18 @@ paths:
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"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'
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Get one Magazines
@ -2667,27 +2762,29 @@ paths:
name: id
required: true
type: integer
- description: Required payload
in: body
name: payload
required: true
schema:
$ref: '#/definitions/request.MagazinesUpdateRequest'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"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'
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: Update Magazines
@ -4394,4 +4491,36 @@ paths:
summary: Login Users
tags:
- Users
/users/pareto-login:
post:
description: API for ParetoLogin Users
parameters:
- description: Required payload
in: body
name: payload
required: true
schema:
$ref: '#/definitions/request.UserLogin'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.BadRequestError'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.UnauthorizedError'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.InternalServerError'
security:
- Bearer: []
summary: ParetoLogin Users
tags:
- Users
swagger: "2.0"

12
go.mod
View File

@ -3,6 +3,7 @@ module go-humas-be
go 1.21.6
require (
aidanwoods.dev/go-paseto v1.5.3
github.com/Nerzal/gocloak/v13 v13.9.0
github.com/arsmn/fiber-swagger/v2 v2.31.1
github.com/efectn/fx-zerolog v1.1.0
@ -21,6 +22,7 @@ require (
)
require (
aidanwoods.dev/go-result v0.1.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
@ -65,11 +67,11 @@ require (
go.uber.org/dig v1.17.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

30
go.sum
View File

@ -1,3 +1,7 @@
aidanwoods.dev/go-paseto v1.5.3 h1:y3pRY9MLWBhfO9VuCN0Bkyxa7Xmkt5coipYJfaOZgOs=
aidanwoods.dev/go-paseto v1.5.3/go.mod h1://T4uDrCXnzls7pKeCXaQ/zC3xv0KtgGMk4wnlOAHSs=
aidanwoods.dev/go-result v0.1.0 h1:y/BMIRX6q3HwaorX1Wzrjo3WUdiYeyWbvGe18hKS3K8=
aidanwoods.dev/go-result v0.1.0/go.mod h1:yridkWghM7AXSFA6wzx0IbsurIm1Lhuro3rYef8FBHM=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
@ -191,13 +195,13 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -210,12 +214,14 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -232,8 +238,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
@ -244,16 +250,16 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=