init project

This commit is contained in:
aditya.siregar
2024-05-28 14:14:55 +07:00
commit 67f1dbc850
141 changed files with 16879 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
package database
type Config interface {
ConnString() string
}
+52
View File
@@ -0,0 +1,52 @@
package db
import (
"fmt"
_ "github.com/lib/pq"
"go.uber.org/zap"
_ "gopkg.in/yaml.v3"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"furtuna-be/config"
)
func NewPostgres(c config.Database) (*gorm.DB, error) {
dialector := postgres.New(postgres.Config{
DSN: c.DSN(),
})
db, err := gorm.Open(dialector, &gorm.Config{})
//db, err := gorm.Open(dialector, &gorm.Config{
// Logger: logger.Default.LogMode(logger.Info), // Enable GORM logging
//})
if err != nil {
return nil, err
}
zapCfg := zap.NewProductionConfig()
zapCfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel) // whatever minimum level
zapCfg.DisableCaller = true
// logger, _ := zapCfg.Build()
// db = gorm.Open(sqldblogger.New(logger), db)
// ping the database to test the connection
sqlDB, err := db.DB()
if err != nil {
return nil, err
}
if err := sqlDB.Ping(); err != nil {
return nil, err
}
sqlDB.SetMaxIdleConns(c.MaxIdleConnectionsInSecond)
sqlDB.SetMaxOpenConns(c.MaxOpenConnectionsInSecond)
sqlDB.SetConnMaxLifetime(c.ConnectionMaxLifetime())
fmt.Println("Successfully connected to PostgreSQL database")
return db, nil
}
+49
View File
@@ -0,0 +1,49 @@
package errors
import "net/http"
const (
Success Code = "20000"
ServerError Code = "50000"
BadRequest Code = "40000"
InvalidRequest Code = "40001"
Unauthorized Code = "40100"
Forbidden Code = "40300"
Timeout Code = "50400"
)
type Code string
var (
codeMap = map[Code]string{
Success: "Success",
BadRequest: "Bad or invalid request",
Unauthorized: "Unauthorized Token",
Timeout: "Gateway Timeout",
ServerError: "Internal Server Error",
Forbidden: "Forbidden",
InvalidRequest: "Invalid Request",
}
codeHTTPMap = map[Code]int{
Success: http.StatusOK,
BadRequest: http.StatusBadRequest,
Unauthorized: http.StatusUnauthorized,
Timeout: http.StatusGatewayTimeout,
ServerError: http.StatusInternalServerError,
Forbidden: http.StatusForbidden,
InvalidRequest: http.StatusUnprocessableEntity,
}
)
func (c Code) GetMessage() string {
return codeMap[c]
}
func (c Code) GetHTTPCode() int {
return codeHTTPMap[c]
}
func (c Code) GetCode() string {
return string(c)
}
+95
View File
@@ -0,0 +1,95 @@
package errors
import "net/http"
type ErrType string
const (
errRequestTimeOut ErrType = "Request Timeout to 3rd Party"
errConnectTimeOut ErrType = "Connect Timeout to 3rd Party"
errFailedExternalCall ErrType = "Failed response from 3rd Party call"
errExternalCall ErrType = "error on 3rd Party call"
errInvalidRequest ErrType = "Invalid Request"
errBadRequest ErrType = "Bad Request"
errOrderNotFound ErrType = "Astria order is not found"
errCheckoutIDNotDefined ErrType = "Checkout client id not found"
errInternalServer ErrType = "Internal Server error"
errUserIsNotFound ErrType = "User is not found"
errInvalidLogin ErrType = "User email or password is invalid"
errUnauthorized ErrType = "Unauthorized"
)
var (
ErrorBadRequest = NewServiceException(errBadRequest)
ErrorInvalidRequest = NewServiceException(errInvalidRequest)
ErrorUnauthorized = NewServiceException(errUnauthorized)
ErrorOrderNotFound = NewServiceException(errOrderNotFound)
ErrorClientIDNotDefined = NewServiceException(errCheckoutIDNotDefined)
ErrorRequestTimeout = NewServiceException(errRequestTimeOut)
ErrorExternalCall = NewServiceException(errExternalCall)
ErrorFailedExternalCall = NewServiceException(errFailedExternalCall)
ErrorConnectionTimeOut = NewServiceException(errConnectTimeOut)
ErrorInternalServer = NewServiceException(errInternalServer)
ErrorUserIsNotFound = NewServiceException(errUserIsNotFound)
ErrorUserInvalidLogin = NewServiceException(errInvalidLogin)
)
type Error interface {
ErrorType() ErrType
MapErrorsToHTTPCode() int
MapErrorsToCode() Code
error
}
type ServiceException struct {
errorType ErrType
message string
}
func NewServiceException(errType ErrType) *ServiceException {
return &ServiceException{
errorType: errType,
message: string(errType),
}
}
func (s *ServiceException) ErrorType() ErrType {
return s.errorType
}
func (s *ServiceException) Error() string {
return s.message
}
func (s *ServiceException) MapErrorsToHTTPCode() int {
switch s.ErrorType() {
case errBadRequest:
return http.StatusBadRequest
case errInvalidRequest:
return http.StatusBadRequest
case errInvalidLogin:
return http.StatusBadRequest
default:
return http.StatusInternalServerError
}
}
func (s *ServiceException) MapErrorsToCode() Code {
switch s.ErrorType() {
case errUnauthorized:
return Unauthorized
case errConnectTimeOut:
return Timeout
case errBadRequest:
return BadRequest
default:
return ServerError
}
}
+46
View File
@@ -0,0 +1,46 @@
package http
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"time"
"go.uber.org/zap"
"furtuna-be/internal/common/logger"
)
type HttpClient struct {
Client *http.Client
}
func NewHttpClient() *HttpClient {
return &HttpClient{
Client: &http.Client{},
}
}
func (c *HttpClient) Do(ctx context.Context, req *http.Request) (int, []byte, error) {
start := time.Now()
logger.ContextLogger(ctx).Info(fmt.Sprintf("Sending request: %v %v", req.Method, req.URL))
resp, err := c.Client.Do(req)
if err != nil {
logger.ContextLogger(ctx).Error(" Failed to send request:", zap.Error(err))
return 0, nil, err
}
logger.ContextLogger(ctx).Info(fmt.Sprintf("Received Response: : %v", resp.StatusCode))
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logger.ContextLogger(ctx).Error(" Failed to read response:", zap.Error(err))
return 0, nil, err
}
logger.ContextLogger(ctx).Info(fmt.Sprintf("Latency : %v", time.Since(start)))
return resp.StatusCode, body, nil
}
+56
View File
@@ -0,0 +1,56 @@
package logger
import (
"context"
"fmt"
"furtuna-be/internal/constants"
"sync"
"go.uber.org/zap"
)
var mainLogger *zap.Logger = nil
var mainLoggerInit sync.Once
func NewMainLoggerSingleton() *zap.Logger {
mainLoggerInit.Do(func() {
logger, err := zap.NewProduction()
if err != nil {
logger.Error("logger initialization failed", zap.Any("error", err))
panic(fmt.Sprintf("logger initialization failed %v", err))
}
logger.Info("logger started")
mainLogger = logger
})
return mainLogger
}
func NewMainNoOpLoggerSingleton() *zap.Logger {
mainLoggerInit.Do(func() {
logger := zap.NewNop()
logger.Info("logger started")
mainLogger = logger
})
return mainLogger
}
func NewNoOp() *zap.Logger {
return zap.NewNop()
}
func GetLogger() *zap.Logger {
return mainLogger
}
func ContextLogger(ctx context.Context) *zap.Logger {
logger := GetLogger()
if ctxRqID, ok := ctx.Value(constants.ContextRequestID).(string); ok {
return logger.With(zap.String(constants.ContextRequestID, ctxRqID))
}
return logger
}
+48
View File
@@ -0,0 +1,48 @@
package mycontext
import (
"context"
"furtuna-be/internal/constants/role"
"furtuna-be/internal/entity"
)
type ContextKey string
type Context interface {
context.Context
RequestedBy() int64
IsSuperAdmin() bool
}
type MyContextImpl struct {
context.Context
requestedBy int64
requestID string
branchID int64
roleID int
}
func (m *MyContextImpl) RequestedBy() int64 {
return m.requestedBy
}
func (m *MyContextImpl) IsSuperAdmin() bool {
return m.roleID == int(role.SuperAdmin)
}
func NewMyContext(parent context.Context, claims *entity.JWTAuthClaims) (*MyContextImpl, error) {
return &MyContextImpl{
Context: parent,
requestedBy: claims.UserID,
branchID: claims.BranchID,
roleID: claims.Role,
}, nil
}
func NewContext(parent context.Context) *MyContextImpl {
return &MyContextImpl{
Context: parent,
}
}
+53
View File
@@ -0,0 +1,53 @@
package request
import (
"context"
"github.com/gin-gonic/gin"
)
const (
ReqInfoKey reqInfoKeyType = "request-info"
)
func SetTraceId(c *gin.Context, traceId string) {
info, exists := c.Get(ReqInfoKey)
if exists {
parsedInfo := info.(RequestInfo)
parsedInfo.TraceId = traceId
c.Set(ReqInfoKey, parsedInfo)
return
}
c.Set(ReqInfoKey, RequestInfo{TraceId: traceId})
}
func SetUserId(c *gin.Context, userId int64) {
info, exists := c.Get(ReqInfoKey)
if exists {
parsedInfo := info.(RequestInfo)
parsedInfo.UserId = userId
c.Set(ReqInfoKey, parsedInfo)
return
}
c.Set(ReqInfoKey, RequestInfo{UserId: userId})
}
func SetUserContext(c *gin.Context, payload map[string]interface{}) {
c.Set(ReqInfoKey, RequestInfo{
UserId: int64(payload["userId"].(float64)),
Role: payload["role"].(string),
})
}
func ContextWithReqInfo(c *gin.Context) context.Context {
info, ok := c.Get(ReqInfoKey)
if ok {
return WithRequestInfo(c, info.(RequestInfo))
}
return WithRequestInfo(c, RequestInfo{})
}
+40
View File
@@ -0,0 +1,40 @@
package request
import "context"
type requestInfoKey int
const (
key requestInfoKey = iota
)
type RequestInfo struct {
UserId int64
TraceId string
Permissions map[string]bool
Role string
}
func WithRequestInfo(ctx context.Context, info RequestInfo) context.Context {
return context.WithValue(ctx, key, info)
}
func GetRequestInfo(ctx context.Context) (requestInfo RequestInfo, ok bool) {
requestInfo, ok = ctx.Value(key).(RequestInfo)
return
}
type reqInfoKeyType = string
const (
reqInfoKey reqInfoKeyType = "request-info"
)
func GetReqInfo(c context.Context) RequestInfo {
info := c.Value(reqInfoKey)
if info != nil {
return info.(RequestInfo)
}
return RequestInfo{}
}