initial commit

This commit is contained in:
Hafizh A. Y
2025-09-25 10:46:46 +07:00
parent c43544e5e8
commit 10506238ae
64 changed files with 3564 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
package utils
// -------------------------------------------------------------------
// FlagType
// -------------------------------------------------------------------
type FlagType string
const (
FlagIsActive FlagType = "IS_ACTIVE"
)
// -------------------------------------------------------------------
// Validators
// -------------------------------------------------------------------
func IsValidFlagType(v string) bool {
switch FlagType(v) {
case FlagIsActive:
return true
}
return false
}
// example use
/**
if !utils.IsValidFlagType(req.FlagName) {
return fiber.NewError(fiber.StatusBadRequest, "invalid flag type")
}
*/
+27
View File
@@ -0,0 +1,27 @@
package utils
import (
"errors"
"github.com/hafizhproject45/Golang-Boilerplate.git/internal/response"
"github.com/hafizhproject45/Golang-Boilerplate.git/internal/validation"
"github.com/gofiber/fiber/v2"
)
func ErrorHandler(c *fiber.Ctx, err error) error {
if errorsMap := validation.CustomErrorMessages(err); len(errorsMap) > 0 {
return response.Error(c, fiber.StatusBadRequest, "Bad Request", errorsMap)
}
var fiberErr *fiber.Error
if errors.As(err, &fiberErr) {
return response.Error(c, fiberErr.Code, fiberErr.Message, nil)
}
return response.Error(c, fiber.StatusInternalServerError, "Internal Server Error", nil)
}
func NotFoundHandler(c *fiber.Ctx) error {
return response.Error(c, fiber.StatusNotFound, "Endpoint Not Found", nil)
}
+28
View File
@@ -0,0 +1,28 @@
package utils
import (
"os"
"github.com/sirupsen/logrus"
)
type CustomFormatter struct {
logrus.TextFormatter
}
var Log *logrus.Logger
func init() {
Log = logrus.New()
// Set logger to use the custom text formatter
Log.SetFormatter(&CustomFormatter{
TextFormatter: logrus.TextFormatter{
TimestampFormat: "15:04:05.000",
FullTimestamp: true,
ForceColors: true,
},
})
Log.SetOutput(os.Stdout)
}
+22
View File
@@ -0,0 +1,22 @@
package utils
import "encoding/json"
type NullString struct {
Set bool
Value *string
}
func (ns *NullString) UnmarshalJSON(b []byte) error {
ns.Set = true
if string(b) == "null" {
ns.Value = nil
return nil
}
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
ns.Value = &s
return nil
}
+59
View File
@@ -0,0 +1,59 @@
package secure
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"errors"
"fmt"
"strings"
"golang.org/x/crypto/argon2"
)
type Params struct {
Memory uint32
Time uint32
Threads uint8
SaltLen uint32
KeyLen uint32
}
var Default = &Params{
Memory: 64 * 1024,
Time: 3,
Threads: 2,
SaltLen: 16,
KeyLen: 32,
}
func Hash(plain string, p *Params) (string, error) {
if strings.TrimSpace(plain) == "" {
return "", errors.New("empty password")
}
if p == nil { p = Default }
salt := make([]byte, p.SaltLen)
if _, err := rand.Read(salt); err != nil { return "", err }
key := argon2.IDKey([]byte(plain), salt, p.Time, p.Memory, p.Threads, p.KeyLen)
return fmt.Sprintf("$argon2id$v=19$m=%d,t=%d,p=%d$%s$%s",
p.Memory, p.Time, p.Threads,
base64.RawStdEncoding.EncodeToString(salt),
base64.RawStdEncoding.EncodeToString(key),
), nil
}
func Verify(encoded, plain string) bool {
parts := strings.Split(encoded, "$")
if len(parts) != 6 { return false }
var m uint32; var t uint32; var p uint8
if _, err := fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &m, &t, &p); err != nil { return false }
salt, err := base64.RawStdEncoding.DecodeString(parts[4]); if err != nil { return false }
want, err := base64.RawStdEncoding.DecodeString(parts[5]); if err != nil { return false }
got := argon2.IDKey([]byte(plain), salt, t, m, p, uint32(len(want)))
return subtle.ConstantTimeCompare(want, got) == 1
}
+13
View File
@@ -0,0 +1,13 @@
package secure
import (
"crypto/rand"
"encoding/base64"
)
func RandomToken(n int) (string, error) {
// n = bytes, 32 → 256-bit
b := make([]byte, n)
if _, err := rand.Read(b); err != nil { return "", err }
return base64.RawURLEncoding.EncodeToString(b), nil
}
+11
View File
@@ -0,0 +1,11 @@
package secure
import (
"crypto/sha256"
"encoding/hex"
)
func SHA256Hex(s string) string {
h := sha256.Sum256([]byte(s))
return hex.EncodeToString(h[:])
}
+45
View File
@@ -0,0 +1,45 @@
package utils
import (
"errors"
"strconv"
"github.com/golang-jwt/jwt/v5"
)
func VerifyToken(tokenStr, secret, tokenType string) (uint, error) {
token, err := jwt.Parse(tokenStr, func(_ *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if err != nil || !token.Valid {
return 0, err
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return 0, errors.New("invalid token claims")
}
jwtType, ok := claims["type"].(string)
if !ok || jwtType != tokenType {
return 0, errors.New("invalid token type")
}
sub, ok := claims["sub"]
if !ok {
return 0, errors.New("invalid token sub")
}
switch v := sub.(type) {
case float64:
return uint(v), nil
case string:
id, err := strconv.Atoi(v)
if err != nil {
return 0, errors.New("invalid sub format")
}
return uint(id), nil
default:
return 0, errors.New("unsupported sub type")
}
}