Init
This commit is contained in:
32
pkg/cache/redis.go
vendored
Normal file
32
pkg/cache/redis.go
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"history-api/pkg/config"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
var RI *redis.Client
|
||||
|
||||
func Connect() error {
|
||||
connectionURI, err := config.GetConfig("REDIS_CONNECTION_URI")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: connectionURI,
|
||||
Password: "",
|
||||
DB: 0,
|
||||
})
|
||||
|
||||
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
||||
return fmt.Errorf("Could not connect to Redis: %v", err)
|
||||
}
|
||||
|
||||
RI = rdb
|
||||
return nil
|
||||
}
|
||||
36
pkg/config/config.go
Normal file
36
pkg/config/config.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"history-api/assets"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func LoadEnv() error {
|
||||
envData, err := assets.GetFileContent("resources/.env")
|
||||
if err != nil {
|
||||
return errors.New("error read .env file")
|
||||
}
|
||||
envMap, err := godotenv.Parse(strings.NewReader(envData))
|
||||
if err != nil {
|
||||
return errors.New("error parsing .env content")
|
||||
}
|
||||
|
||||
for key, value := range envMap {
|
||||
os.Setenv(key, value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetConfig(config string) (string, error) {
|
||||
var data string = os.Getenv(config)
|
||||
if data == "" {
|
||||
return "", fmt.Errorf("config (%s) dose not exit", config)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
27
pkg/constant/role.go
Normal file
27
pkg/constant/role.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package constant
|
||||
|
||||
type Role string
|
||||
|
||||
const (
|
||||
ADMIN Role = "ADMIN"
|
||||
MOD Role = "MOD"
|
||||
USER Role = "USER"
|
||||
HISTORIAN Role = "HISTORIAN"
|
||||
BANNED Role = "BANNED"
|
||||
)
|
||||
|
||||
func (r Role) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
func (r Role) Compare(other Role) bool {
|
||||
return r == other
|
||||
}
|
||||
|
||||
func CheckValidRole(r Role) bool {
|
||||
return r == ADMIN || r == MOD || r == HISTORIAN || r == USER || r == BANNED
|
||||
}
|
||||
|
||||
func (r Role) ToSlice() []string {
|
||||
return []string{r.String()}
|
||||
}
|
||||
35
pkg/constant/status.go
Normal file
35
pkg/constant/status.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package constant
|
||||
|
||||
type StatusType int16
|
||||
|
||||
const (
|
||||
StatusPending StatusType = 1
|
||||
StatusApproved StatusType = 2
|
||||
StatusRejected StatusType = 3
|
||||
)
|
||||
|
||||
func (t StatusType) String() string {
|
||||
switch t {
|
||||
case StatusPending:
|
||||
return "PENDING"
|
||||
case StatusApproved:
|
||||
return "APPROVED"
|
||||
case StatusRejected:
|
||||
return "REJECT"
|
||||
default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
func ParseStatusType(v int16) StatusType {
|
||||
switch v {
|
||||
case 1:
|
||||
return StatusPending
|
||||
case 2:
|
||||
return StatusApproved
|
||||
case 3:
|
||||
return StatusRejected
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
40
pkg/constant/token.go
Normal file
40
pkg/constant/token.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package constant
|
||||
|
||||
type TokenType int16
|
||||
|
||||
const (
|
||||
TokenPasswordReset TokenType = 1
|
||||
TokenEmailVerify TokenType = 2
|
||||
TokenMagicLink TokenType = 3
|
||||
TokenRefreshToken TokenType = 4
|
||||
)
|
||||
|
||||
func (t TokenType) String() string {
|
||||
switch t {
|
||||
case TokenPasswordReset:
|
||||
return "PASSWORD_RESET"
|
||||
case TokenEmailVerify:
|
||||
return "EMAIL_VERIFY"
|
||||
case TokenMagicLink:
|
||||
return "LOGIN_MAGIC_LINK"
|
||||
case TokenRefreshToken:
|
||||
return "REFRESH_TOKEN"
|
||||
default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
func ParseTokenType(v int16) TokenType {
|
||||
switch v {
|
||||
case 1:
|
||||
return TokenPasswordReset
|
||||
case 2:
|
||||
return TokenEmailVerify
|
||||
case 3:
|
||||
return TokenMagicLink
|
||||
case 4:
|
||||
return TokenRefreshToken
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
35
pkg/constant/verify.go
Normal file
35
pkg/constant/verify.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package constant
|
||||
|
||||
type VerifyType int16
|
||||
|
||||
const (
|
||||
VerifyIdCard VerifyType = 1
|
||||
VerifyEducation VerifyType = 2
|
||||
VerifyExpert VerifyType = 3
|
||||
)
|
||||
|
||||
func (t VerifyType) String() string {
|
||||
switch t {
|
||||
case VerifyIdCard:
|
||||
return "ID_CARD"
|
||||
case VerifyEducation:
|
||||
return "EDUCATION"
|
||||
case VerifyExpert:
|
||||
return "EXPERT"
|
||||
default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
func ParseVerifyType(v int16) VerifyType {
|
||||
switch v {
|
||||
case 1:
|
||||
return VerifyIdCard
|
||||
case 2:
|
||||
return VerifyEducation
|
||||
case 3:
|
||||
return VerifyExpert
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
32
pkg/convert/convert.go
Normal file
32
pkg/convert/convert.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package convert
|
||||
|
||||
import "github.com/jackc/pgx/v5/pgtype"
|
||||
|
||||
func UUIDToString(v pgtype.UUID) string {
|
||||
if v.Valid {
|
||||
return v.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func TextToString(v pgtype.Text) string {
|
||||
if v.Valid {
|
||||
return v.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func BoolVal(v pgtype.Bool) bool {
|
||||
if v.Valid {
|
||||
return v.Bool
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TimeToPtr(v pgtype.Timestamptz) *string {
|
||||
if v.Valid {
|
||||
t := v.Time.Format("2006-01-02T15:04:05Z07:00")
|
||||
return &t
|
||||
}
|
||||
return nil
|
||||
}
|
||||
32
pkg/database/db.go
Normal file
32
pkg/database/db.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"history-api/pkg/config"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
func Connect() (*pgxpool.Pool, error) {
|
||||
ctx := context.Background()
|
||||
connectionURI, err := config.GetConfig("PGX_CONNECTION_URI")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
poolConfig, err := pgxpool.ParseConfig(connectionURI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pool, err := pgxpool.NewWithConfig(ctx, poolConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := pool.Ping(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
13
pkg/dtos/request/auth.go
Normal file
13
pkg/dtos/request/auth.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package request
|
||||
|
||||
type SignUpDto struct {
|
||||
Password string `json:"password" validate:"required"`
|
||||
DiscordUserId string `json:"discord_user_id" validate:"required"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
}
|
||||
|
||||
type LoginDto struct {
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
21
pkg/dtos/request/media.go
Normal file
21
pkg/dtos/request/media.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package request
|
||||
|
||||
type PreSignedDto struct {
|
||||
FileName string `json:"fileName" validate:"required"`
|
||||
ContentType string `json:"contentType" validate:"required"`
|
||||
}
|
||||
|
||||
type PreSignedCompleteDto struct {
|
||||
FileName string `json:"fileName" validate:"required"`
|
||||
MediaId string `json:"mediaId" validate:"required"`
|
||||
PublicUrl string `json:"publicUrl" validate:"required"`
|
||||
}
|
||||
|
||||
type SearchMediaDto struct {
|
||||
MediaId string `query:"media_id" validate:"omitempty"`
|
||||
FileName string `query:"file_name" validate:"omitempty"`
|
||||
SortBy string `query:"sort_by" default:"created_at" validate:"oneof=created_at updated_at"`
|
||||
Order string `query:"order" default:"desc" validate:"oneof=asc desc"`
|
||||
Page int `query:"page" default:"1" validate:"min=1"`
|
||||
Limit int `query:"limit" default:"10" validate:"min=1,max=100"`
|
||||
}
|
||||
26
pkg/dtos/request/user.go
Normal file
26
pkg/dtos/request/user.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package request
|
||||
|
||||
import "history-api/pkg/constant"
|
||||
|
||||
type CreateUserDto struct {
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
DiscordUserId string `json:"discord_user_id" validate:"required"`
|
||||
Role []constant.Role `json:"role" validate:"required"`
|
||||
}
|
||||
|
||||
type UpdateUserDto struct {
|
||||
Password *string `json:"password" validate:"omitempty"`
|
||||
DiscordUserId *string `json:"discord_user_id" validate:"omitempty"`
|
||||
Role *[]constant.Role `json:"role" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type SearchUserDto struct {
|
||||
Username *string `query:"username" validate:"omitempty"`
|
||||
DiscordUserId *string `query:"discord_user_id" validate:"omitempty"`
|
||||
Role *[]constant.Role `query:"role" validate:"omitempty"`
|
||||
SortBy string `query:"sort_by" default:"created_at" validate:"oneof=created_at updated_at"`
|
||||
Order string `query:"order" default:"desc" validate:"oneof=asc desc"`
|
||||
Page int `query:"page" default:"1" validate:"min=1"`
|
||||
Limit int `query:"limit" default:"10" validate:"min=1,max=100"`
|
||||
}
|
||||
6
pkg/dtos/response/auth.go
Normal file
6
pkg/dtos/response/auth.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package response
|
||||
|
||||
type AuthResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
19
pkg/dtos/response/common.go
Normal file
19
pkg/dtos/response/common.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"history-api/pkg/constant"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
type CommonResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Data any `json:"data"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type JWTClaims struct {
|
||||
UId string `json:"uid"`
|
||||
Roles []constant.Role `json:"roles"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
9
pkg/dtos/response/media.go
Normal file
9
pkg/dtos/response/media.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package response
|
||||
|
||||
type PreSignedResponse struct {
|
||||
UploadUrl string `json:"uploadUrl"`
|
||||
PublicUrl string `json:"publicUrl"`
|
||||
FileName string `json:"fileName"`
|
||||
MediaId string `json:"mediaId"`
|
||||
SignedHeaders map[string]string `json:"signedHeaders"`
|
||||
}
|
||||
14
pkg/dtos/response/role.go
Normal file
14
pkg/dtos/response/role.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package response
|
||||
|
||||
type RoleSimpleResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type RoleResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
CreatedAt *string `json:"created_at"`
|
||||
UpdatedAt *string `json:"updated_at"`
|
||||
}
|
||||
9
pkg/dtos/response/token.go
Normal file
9
pkg/dtos/response/token.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package response
|
||||
|
||||
type TokenResponse struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
TokenType int16 `json:"token_type"`
|
||||
ExpiresAt *string `json:"expires_at"`
|
||||
CreatedAt *string `json:"created_at"`
|
||||
}
|
||||
15
pkg/dtos/response/user.go
Normal file
15
pkg/dtos/response/user.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package response
|
||||
|
||||
type UserResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
IsActive bool `json:"is_active"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
TokenVersion int32 `json:"token_version"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
CreatedAt *string `json:"created_at"`
|
||||
UpdatedAt *string `json:"updated_at"`
|
||||
Roles []*RoleSimpleResponse `json:"roles"`
|
||||
}
|
||||
17
pkg/log/log.go
Normal file
17
pkg/log/log.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func init() {
|
||||
output := zerolog.ConsoleWriter{
|
||||
Out: os.Stdout,
|
||||
PartsOrder: []string{"level", "message"},
|
||||
}
|
||||
|
||||
log.Logger = zerolog.New(output).With().Logger()
|
||||
}
|
||||
54
pkg/models/role.go
Normal file
54
pkg/models/role.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"history-api/pkg/dtos/response"
|
||||
"history-api/pkg/convert"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type RoleSimple struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (r *RoleSimple) ToResponse() *response.RoleSimpleResponse {
|
||||
return &response.RoleSimpleResponse{
|
||||
ID: convert.UUIDToString(r.ID),
|
||||
Name: r.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func RolesToResponse(rs []*RoleSimple) []*response.RoleSimpleResponse {
|
||||
out := make([]*response.RoleSimpleResponse, len(rs))
|
||||
for i := range rs {
|
||||
out[i] = rs[i].ToResponse()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type RoleEntity struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IsDeleted pgtype.Bool `json:"is_deleted"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (r *RoleEntity) ToResponse() *response.RoleResponse {
|
||||
return &response.RoleResponse{
|
||||
ID: convert.UUIDToString(r.ID),
|
||||
Name: r.Name,
|
||||
IsDeleted: convert.BoolVal(r.IsDeleted),
|
||||
CreatedAt: convert.TimeToPtr(r.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(r.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func RolesEntityToResponse(rs []*RoleEntity) []*response.RoleResponse {
|
||||
out := make([]*response.RoleResponse, len(rs))
|
||||
for i := range rs {
|
||||
out[i] = rs[i].ToResponse()
|
||||
}
|
||||
return out
|
||||
}
|
||||
35
pkg/models/token.go
Normal file
35
pkg/models/token.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"history-api/pkg/convert"
|
||||
"history-api/pkg/dtos/response"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type TokenEntity struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
UserID pgtype.UUID `json:"user_id"`
|
||||
Token string `json:"token"`
|
||||
TokenType int16 `json:"token_type"`
|
||||
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
func (t *TokenEntity) ToResponse() *response.TokenResponse {
|
||||
return &response.TokenResponse{
|
||||
ID: convert.UUIDToString(t.ID),
|
||||
UserID: convert.UUIDToString(t.UserID),
|
||||
TokenType: t.TokenType,
|
||||
ExpiresAt: convert.TimeToPtr(t.ExpiresAt),
|
||||
CreatedAt: convert.TimeToPtr(t.CreatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func TokensEntityToResponse(ts []*TokenEntity) []*response.TokenResponse {
|
||||
out := make([]*response.TokenResponse, len(ts))
|
||||
for i := range ts {
|
||||
out[i] = ts[i].ToResponse()
|
||||
}
|
||||
return out
|
||||
}
|
||||
58
pkg/models/user.go
Normal file
58
pkg/models/user.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"history-api/pkg/convert"
|
||||
"history-api/pkg/dtos/response"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type UserEntity struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
PasswordHash string `json:"password_hash"`
|
||||
AvatarUrl pgtype.Text `json:"avatar_url"`
|
||||
IsActive pgtype.Bool `json:"is_active"`
|
||||
IsVerified pgtype.Bool `json:"is_verified"`
|
||||
TokenVersion int32 `json:"token_version"`
|
||||
RefreshToken pgtype.Text `json:"refresh_token"`
|
||||
IsDeleted pgtype.Bool `json:"is_deleted"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
Roles []*RoleSimple `json:"roles"`
|
||||
}
|
||||
|
||||
func (u *UserEntity) ParseRoles(data []byte) error {
|
||||
if len(data) == 0 {
|
||||
u.Roles = []*RoleSimple{}
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(data, &u.Roles)
|
||||
}
|
||||
|
||||
func (u *UserEntity) ToResponse() *response.UserResponse {
|
||||
return &response.UserResponse{
|
||||
ID: convert.UUIDToString(u.ID),
|
||||
Name: u.Name,
|
||||
Email: u.Email,
|
||||
AvatarUrl: convert.TextToString(u.AvatarUrl),
|
||||
IsActive: convert.BoolVal(u.IsActive),
|
||||
IsVerified: convert.BoolVal(u.IsVerified),
|
||||
TokenVersion: u.TokenVersion,
|
||||
IsDeleted: convert.BoolVal(u.IsDeleted),
|
||||
CreatedAt: convert.TimeToPtr(u.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(u.UpdatedAt),
|
||||
Roles: RolesToResponse(u.Roles),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func UsersEntityToResponse(rs []*UserEntity) []*response.UserResponse {
|
||||
out := make([]*response.UserResponse, len(rs))
|
||||
for i := range rs {
|
||||
out[i] = rs[i].ToResponse()
|
||||
}
|
||||
return out
|
||||
}
|
||||
82
pkg/validator/validator.go
Normal file
82
pkg/validator/validator.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
var validate = validator.New()
|
||||
|
||||
func init() {
|
||||
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
|
||||
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
|
||||
if name == "-" {
|
||||
return ""
|
||||
}
|
||||
if name == "" {
|
||||
name = strings.SplitN(fld.Tag.Get("query"), ",", 2)[0]
|
||||
}
|
||||
return name
|
||||
})
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
FailedField string `json:"failed_field"`
|
||||
Tag string `json:"tag"`
|
||||
Value string `json:"value"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func formatValidationError(err error) []ErrorResponse {
|
||||
var validationErrors validator.ValidationErrors
|
||||
var errorsList []ErrorResponse
|
||||
|
||||
if errors.As(err, &validationErrors) {
|
||||
for _, fieldError := range validationErrors {
|
||||
var element ErrorResponse
|
||||
element.FailedField = fieldError.Field()
|
||||
element.Tag = fieldError.Tag()
|
||||
element.Value = fieldError.Param()
|
||||
element.Message = "Field " + fieldError.Field() + " failed validation on tag '" + fieldError.Tag() + "'"
|
||||
|
||||
errorsList = append(errorsList, element)
|
||||
}
|
||||
}
|
||||
return errorsList
|
||||
}
|
||||
|
||||
func ValidateQueryDto(c fiber.Ctx, dto any) error {
|
||||
if err := c.Bind().Query(dto); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Failed to parse query parameters: " + err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
if err := validate.Struct(dto); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"errors": formatValidationError(err),
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateBodyDto(c fiber.Ctx, dto any) error {
|
||||
if err := c.Bind().Body(dto); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Invalid request body: " + err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
if err := validate.Struct(dto); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"errors": formatValidationError(err),
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user