Initial commit
This commit is contained in:
parent
01859cf8f5
commit
19d9ab1391
|
@ -0,0 +1,23 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
link "git.doolta.com/doolta/kit/pkg/link"
|
||||||
|
)
|
||||||
|
|
||||||
|
var validURL = "https://api.mailcape.com"
|
||||||
|
|
||||||
|
var linkService link.Service
|
||||||
|
|
||||||
|
// LoginResponse is the struct holding JWT token information
|
||||||
|
type LoginResponse struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
User string `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginData struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"intranet/internal/dependencies"
|
||||||
|
"intranet/internal/models"
|
||||||
|
"intranet/pkg/token"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
jwt "github.com/golang-jwt/jwt/v5"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Service for auth (Interface AND implementation)
|
||||||
|
// All the business logic should be here
|
||||||
|
type Service interface {
|
||||||
|
// FindByEmail(id string) ([]*models.Auth, error)
|
||||||
|
GetLoginHandler() func(http.ResponseWriter, *http.Request)
|
||||||
|
GetSignupHandler() func(http.ResponseWriter, *http.Request)
|
||||||
|
GetAdminRoleRequiredMiddleware() gin.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthService godoc
|
||||||
|
type AuthServiceImplementation struct {
|
||||||
|
DB dependencies.DB
|
||||||
|
Log dependencies.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthService returns a service to manipulate auth
|
||||||
|
func NewAuthService(db dependencies.DB, log dependencies.Logger) *AuthServiceImplementation {
|
||||||
|
return &AuthServiceImplementation{db, log}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoginHandler returns an handled for the login route
|
||||||
|
func (s *AuthServiceImplementation) GetLoginHandler() http.HandlerFunc {
|
||||||
|
log := s.Log
|
||||||
|
db := s.DB
|
||||||
|
log.Info("[LoginHandler] Installed")
|
||||||
|
//@return a http.handlerFunc that will check for loginDataand return a JWT token if login is successful
|
||||||
|
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Info("[LoginHandler] Start")
|
||||||
|
|
||||||
|
loginData := LoginData{}
|
||||||
|
|
||||||
|
// Decode the incoming note json
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&loginData)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
email := loginData.Email
|
||||||
|
password := loginData.Password
|
||||||
|
|
||||||
|
var u models.Account
|
||||||
|
db.Where("email = ?", email).First(&u)
|
||||||
|
|
||||||
|
if (u == models.Account{}) {
|
||||||
|
http.Error(w, "Invalid login/pass", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check password
|
||||||
|
log.Debug("Before check")
|
||||||
|
err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
|
||||||
|
log.Debug("After check (%s)", err)
|
||||||
|
if err != nil {
|
||||||
|
log.Info(err)
|
||||||
|
http.Error(w, "Invalid login/pass", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return JWT if password valid
|
||||||
|
// TODO: add role from database (not hardcoded)
|
||||||
|
tokenString, err := token.CreateToken(strconv.FormatInt(int64(u.ID), 9), token.TokenSecret, "admin")
|
||||||
|
if err != nil {
|
||||||
|
log.Info(err)
|
||||||
|
http.Error(w, "Unable to create token", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debug("Token generated " + tokenString)
|
||||||
|
expire := time.Now().AddDate(-1, 0, 1)
|
||||||
|
cookie := http.Cookie{Name: "token", Value: tokenString, Expires: expire}
|
||||||
|
http.SetCookie(w, &cookie)
|
||||||
|
|
||||||
|
j, err := json.Marshal(LoginResponse{UserID: u.ID, Email: u.Email, IsAdmin: true, Token: tokenString, User: u.Email})
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSignupHandler returns a handler for the signup route
|
||||||
|
func (s *AuthServiceImplementation) GetSignupHandler() http.HandlerFunc {
|
||||||
|
log := s.Log
|
||||||
|
log.Info("[SignupHandler] Installed")
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
email := r.FormValue("email")
|
||||||
|
password := r.FormValue("password")
|
||||||
|
|
||||||
|
log.Infof("In signup handler %s", email)
|
||||||
|
|
||||||
|
if email == "" {
|
||||||
|
http.Error(w, "Email missing", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 13)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can't generate hash for password: " + err.Error())
|
||||||
|
http.Error(w, "Can't generate hash for password", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := models.Account{Email: email, Password: string(bytes), Status: "pending"}
|
||||||
|
u, err = s.createUser(&u)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("Could'nt create Account = (%+v)", u)
|
||||||
|
log.Error(err)
|
||||||
|
http.Error(w, "Can't create account in database", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/", http.StatusMovedPermanently)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createUser is a method on AuthServiceImplementation that create an user
|
||||||
|
// copying the part in GetSignupHandler that create a user in database
|
||||||
|
func (s *AuthServiceImplementation) createUser(u *models.Account) (models.Account, error) {
|
||||||
|
db := s.DB
|
||||||
|
db.Create(u)
|
||||||
|
if u.ID == 0 {
|
||||||
|
return *u, fmt.Errorf("Can't create account in database")
|
||||||
|
}
|
||||||
|
return *u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAdminRoleRequiredMiddleware returns a middleware that checks if the user is an admin
|
||||||
|
func (s *AuthServiceImplementation) GetAdminRoleRequiredMiddleware() http.HandlerFunc {
|
||||||
|
log := s.Log
|
||||||
|
log.Info("Installing RoleMiddleware")
|
||||||
|
return func(next http.HandlerFunc) http.HandlerFunc {
|
||||||
|
log.Info("[RoleMiddleware]")
|
||||||
|
claims, OK := c.Get("claims")
|
||||||
|
if !OK {
|
||||||
|
http.Error(w, "No claims found", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
claimsMap := claims.(jwt.MapClaims)
|
||||||
|
|
||||||
|
log.Infof("[RoleMiddleware] claimsMap[role] = (%s)", claimsMap["Role"])
|
||||||
|
log.Infof("[RoleMiddleware] claims = (%+v)", claims)
|
||||||
|
if claimsMap["Role"] != "admin" {
|
||||||
|
http.Error(w, "Not an admin", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
14
go.mod
14
go.mod
|
@ -3,10 +3,15 @@ module git.doolta.com/doolta/go-kit
|
||||||
go 1.22.1
|
go 1.22.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.doolta.com/doolta/kit v1.2.3
|
||||||
github.com/epfl-si/go-toolbox v0.6.15
|
github.com/epfl-si/go-toolbox v0.6.15
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
|
golang.org/x/crypto v0.19.0
|
||||||
|
gorm.io/driver/mysql v1.5.4
|
||||||
|
gorm.io/driver/postgres v1.5.7
|
||||||
|
gorm.io/gorm v1.25.10
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -18,7 +23,14 @@ require (
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.18.0 // indirect
|
github.com/go-playground/validator/v10 v10.18.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.4.3 // indirect
|
||||||
|
github.com/jinzhu/gorm v1.9.16 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
@ -26,11 +38,11 @@ require (
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/arch v0.7.0 // indirect
|
golang.org/x/arch v0.7.0 // indirect
|
||||||
golang.org/x/crypto v0.19.0 // indirect
|
|
||||||
golang.org/x/net v0.21.0 // indirect
|
golang.org/x/net v0.21.0 // indirect
|
||||||
golang.org/x/sys v0.17.0 // indirect
|
golang.org/x/sys v0.17.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
|
58
go.sum
58
go.sum
|
@ -1,3 +1,7 @@
|
||||||
|
git.doolta.com/doolta/kit v1.2.3 h1:xJ+LbWDopzVEyCwEI+gUh0RtSF4wvg6CSgadvH1SX/4=
|
||||||
|
git.doolta.com/doolta/kit v1.2.3/go.mod h1:JU4jwlc7qrNZgzlKupVTd7iYIqO1odGJA3zlPxkPUQ8=
|
||||||
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
|
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||||
github.com/bytedance/sonic v1.11.0 h1:FwNNv6Vu4z2Onf1++LNzxB/QhitD8wuTdpZzMTGITWo=
|
github.com/bytedance/sonic v1.11.0 h1:FwNNv6Vu4z2Onf1++LNzxB/QhitD8wuTdpZzMTGITWo=
|
||||||
|
@ -12,8 +16,12 @@ github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLI
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/epfl-si/go-toolbox v0.6.15 h1:Bo9t4MAVr7LN6mV/BGitZAVytbMkLfPpIwSsVpva8T8=
|
github.com/epfl-si/go-toolbox v0.6.15 h1:Bo9t4MAVr7LN6mV/BGitZAVytbMkLfPpIwSsVpva8T8=
|
||||||
github.com/epfl-si/go-toolbox v0.6.15/go.mod h1:q48G3oNV2nWDTMBHfcG7qiUV8kAkLHc8AmndJZQlRIc=
|
github.com/epfl-si/go-toolbox v0.6.15/go.mod h1:q48G3oNV2nWDTMBHfcG7qiUV8kAkLHc8AmndJZQlRIc=
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
@ -28,23 +36,50 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U=
|
github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U=
|
||||||
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
|
||||||
|
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
|
||||||
|
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||||
|
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||||
|
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
@ -54,6 +89,8 @@ github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOS
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
@ -77,24 +114,43 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.5.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/driver/mysql v1.5.4 h1:igQmHfKcbaTVyAIHNhhB888vvxh8EdQ2uSUT0LPcBso=
|
||||||
|
gorm.io/driver/mysql v1.5.4/go.mod h1:9rYxJph/u9SWkWc9yY4XJ1F/+xO0S/ChOmbk3+Z5Tvs=
|
||||||
|
gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM=
|
||||||
|
gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
|
||||||
|
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
|
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
|
||||||
|
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package gormdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetGormDB returns a Gorm database connection.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - log *zap.Logger: a logger instance
|
||||||
|
// - host string: the database host
|
||||||
|
// - name string: the database name
|
||||||
|
// - user string: the database user
|
||||||
|
// - pass string: the database password
|
||||||
|
// - port string: the database port
|
||||||
|
// - param string: the specific database parameters
|
||||||
|
// - maxIdle int: the maximum number of idle connections
|
||||||
|
// - maxOpen int: the maximum number of open connections
|
||||||
|
//
|
||||||
|
// Return type(s):
|
||||||
|
// - *gorm.DB: the Gorm database connection
|
||||||
|
// - error: an error, if any, encountered during the connection
|
||||||
|
func GetMySQLDB(log *zap.Logger, host, name, user, pass, port, param string, maxIdle int, maxOpen int) (*gorm.DB, error) {
|
||||||
|
//log.Infof("[GetGormDB] Connecting to 'database' %s on host %s as user '%s' (%s)", name, host, user, param)
|
||||||
|
logLevel := logger.Silent
|
||||||
|
if os.Getenv("LOG_LEVEL") == "info" {
|
||||||
|
logLevel = logger.Info
|
||||||
|
}
|
||||||
|
|
||||||
|
var dsn string
|
||||||
|
|
||||||
|
if param != "" {
|
||||||
|
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?%s", user, pass, host, port, name, param)
|
||||||
|
} else {
|
||||||
|
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", user, pass, host, port, name)
|
||||||
|
}
|
||||||
|
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logLevel),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("GetGormDB:%s", err))
|
||||||
|
return db, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info(fmt.Sprintf("GetGormDB:successfully connected on host '%s' to database '%s' as user '%s' (%s)", host, name, user, param))
|
||||||
|
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
sqlDB.SetMaxIdleConns(maxIdle)
|
||||||
|
sqlDB.SetMaxOpenConns(maxOpen)
|
||||||
|
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||||
|
sqlDB.SetConnMaxIdleTime(2 * time.Minute)
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package gormdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetGormDB initializes a connection to the database and returns a handle
|
||||||
|
func GetPostgresqlDB(log *zap.Logger, host, name, user, pass, port string) (*gorm.DB, error) {
|
||||||
|
log.Info(fmt.Sprintf("[GetPostgresqlDB] Connecting to 'database' %s on host %s as user '%s'", name, host, user))
|
||||||
|
dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", host, port, user, pass, name)
|
||||||
|
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Info),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("[GetPostgresqlDB] err=%s\n", err))
|
||||||
|
return db, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info(fmt.Sprintf("[GetPostgresqlDB] Successfully connected on host '%s' to database '%s' as user '%s'", host, name, user))
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetLoggerConfig(logLevel string, stdouts []string, stderrs []string, encoding string) zap.Config {
|
||||||
|
level := zap.InfoLevel
|
||||||
|
if logLevel == "debug" {
|
||||||
|
level = zap.DebugLevel
|
||||||
|
}
|
||||||
|
if logLevel == "error" {
|
||||||
|
level = zap.ErrorLevel
|
||||||
|
}
|
||||||
|
if logLevel == "warn" {
|
||||||
|
level = zap.WarnLevel
|
||||||
|
}
|
||||||
|
if logLevel == "fatal" {
|
||||||
|
level = zap.FatalLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a new logger
|
||||||
|
encoderCfg := zap.NewProductionEncoderConfig()
|
||||||
|
encoderCfg.TimeKey = "timegenerated"
|
||||||
|
encoderCfg.LevelKey = "log.level"
|
||||||
|
encoderCfg.EncodeTime = zapcore.RFC3339TimeEncoder
|
||||||
|
encoderCfg.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||||
|
|
||||||
|
config := zap.Config{
|
||||||
|
Level: zap.NewAtomicLevelAt(level),
|
||||||
|
Development: false,
|
||||||
|
DisableCaller: true,
|
||||||
|
DisableStacktrace: false,
|
||||||
|
Sampling: nil,
|
||||||
|
Encoding: encoding,
|
||||||
|
EncoderConfig: encoderCfg,
|
||||||
|
OutputPaths: stdouts,
|
||||||
|
ErrorOutputPaths: stderrs,
|
||||||
|
InitialFields: map[string]interface{}{
|
||||||
|
//"pid": os.Getpid(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogger returns a new logger with the specified log level.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - logLevel: a string representing the log level ("debug", "error", "warn", "fatal")
|
||||||
|
//
|
||||||
|
// Return type(s):
|
||||||
|
// - *zap.Logger: a pointer to the logger
|
||||||
|
func GetLogger(logLevel string) *zap.Logger {
|
||||||
|
return zap.Must(GetLoggerConfig(logLevel, []string{"stdout"}, []string{"stderr"}, "json").Build())
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
// Package token handles JWT tokens manipulation
|
||||||
|
package token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
jwt "github.com/golang-jwt/jwt/v5"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Authenticater is the interface that wraps the Authenticate method
|
||||||
|
type Authenticater interface {
|
||||||
|
Authenticate(login, pass string) (CustomClaims, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomClaims is the struct that represents the claims of a JWT token in EPFL context
|
||||||
|
type CustomClaims struct {
|
||||||
|
Sciper string `json:"sciper"`
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the claims of a JWT token
|
||||||
|
func (m CustomClaims) Validate() error {
|
||||||
|
if m.Sciper == "" {
|
||||||
|
return errors.New("sciper must be set")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token is the struct that represents a JWT token
|
||||||
|
type Token struct {
|
||||||
|
JWT *jwt.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new JWT token
|
||||||
|
func New(claims CustomClaims) *Token {
|
||||||
|
jwt := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|
||||||
|
return &Token{JWT: jwt}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses a JWT token
|
||||||
|
func Parse(tokenString string, secret []byte) (*Token, error) {
|
||||||
|
t, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
// Don't forget to validate the alg is what you expect:
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
|
||||||
|
return secret, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Token{t}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign signs a JWT token
|
||||||
|
func (t *Token) Sign(secret []byte) (string, error) {
|
||||||
|
return t.JWT.SignedString([]byte(secret))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Claims returns the claims of a JWT token
|
||||||
|
func (t *Token) Claims() jwt.MapClaims {
|
||||||
|
return t.JWT.Claims.(jwt.MapClaims)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets a claim in a JWT token
|
||||||
|
func (t *Token) Set(key string, value interface{}) {
|
||||||
|
t.Claims()[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets a claim from a JWT token
|
||||||
|
func (t *Token) Get(key string) interface{} {
|
||||||
|
return t.Claims()[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetString gets a claim from a JWT token as a string
|
||||||
|
func (t *Token) GetString(key string) string {
|
||||||
|
return t.Claims()[key].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToJSON converts a JWT token to JSON
|
||||||
|
func (t *Token) ToJSON() (string, error) {
|
||||||
|
return t.JWT.Raw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostLoginHandler is the handler that checks the login and password and returns a JWT token
|
||||||
|
func PostLoginHandler(log *zap.Logger, auth Authenticater, secret []byte) gin.HandlerFunc {
|
||||||
|
log.Info("Creating login handler")
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
login := c.PostForm("login")
|
||||||
|
pass := c.PostForm("pass")
|
||||||
|
|
||||||
|
log.Info("Login attempt", zap.String("login", login))
|
||||||
|
|
||||||
|
claims, err := auth.Authenticate(login, pass)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := New(claims)
|
||||||
|
encoded, err := t.Sign(secret)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"access_token": encoded})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GinMiddleware is the middleware that checks the JWT token
|
||||||
|
func GinMiddleware(secret []byte) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
authorizationHeaderString := c.GetHeader("Authorization")
|
||||||
|
if authorizationHeaderString == "" {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "No token provided"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the authorization header starts with "Bearer"
|
||||||
|
if len(authorizationHeaderString) < 7 || authorizationHeaderString[:7] != "Bearer " {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the token from the authorization header
|
||||||
|
tokenString := authorizationHeaderString[7:]
|
||||||
|
|
||||||
|
t, err := Parse(tokenString, secret)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("token", t)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package token_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/epfl-si/go-toolbox/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_creating() {
|
||||||
|
t := token.New(token.CustomClaims{Sciper: "321014"})
|
||||||
|
encoded, err := t.Sign([]byte("secret"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(encoded)
|
||||||
|
// Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY2lwZXIiOiIzMjEwMTQifQ.7Nf7BUmLmN2RGXwf2nr-cOwkcsCkWO2i6YgLZdItrek
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_decoding() {
|
||||||
|
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY2lwZXIiOiIzMjEwMTQifQ.7Nf7BUmLmN2RGXwf2nr-cOwkcsCkWO2i6YgLZdItrek"
|
||||||
|
decoded, err := token.Parse(tokenString, []byte("secret"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(decoded.JWT.Raw)
|
||||||
|
fmt.Println(decoded.JWT.Header["alg"])
|
||||||
|
fmt.Println(decoded.JWT.Header["typ"])
|
||||||
|
// fmt.Printf("%+v", decoded.JWT)
|
||||||
|
// Output:
|
||||||
|
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY2lwZXIiOiIzMjEwMTQifQ.7Nf7BUmLmN2RGXwf2nr-cOwkcsCkWO2i6YgLZdItrek
|
||||||
|
// HS256
|
||||||
|
// JWT
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Package web provides HTTP server related functions and structures.
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewHTTPServer takes a database connection and a logger as parameters
|
||||||
|
// It returns a pointer to a Server struct
|
||||||
|
func NewHTTPServer(log *zap.Logger) *Server {
|
||||||
|
router := http.NewServeMux()
|
||||||
|
services := make(map[string]interface{})
|
||||||
|
|
||||||
|
return &Server{
|
||||||
|
Log: log,
|
||||||
|
Router: router,
|
||||||
|
Services: services,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run starts the HTTP server on port passed as parameter (":12345").
|
||||||
|
func (s *Server) Run(port string) {
|
||||||
|
http.ListenAndServe(port, s.Router)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoute adds a new route to the server's router.
|
||||||
|
// Adding server resources in context
|
||||||
|
func (s *Server) AddRoute(pattern string, handlefunc http.HandlerFunc) {
|
||||||
|
s.Router.Handle(pattern, s.WithServerContext(handlefunc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithServerContext adds the logger and services to the request context.
|
||||||
|
func (s *Server) WithServerContext(next http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.WithValue(r.Context(), "log", s.Log)
|
||||||
|
ctx = context.WithValue(ctx, "services", s.Services)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetServerContext(r *http.Request) (*zap.Logger, map[string]interface{}) {
|
||||||
|
services := r.Context().Value("services").(map[string]interface{})
|
||||||
|
log := r.Context().Value("log").(*zap.Logger)
|
||||||
|
return log, services
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Package web provides the web server for the application.
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Routes sets up the routes for the server.
|
||||||
|
func (s *Server) Routes() {
|
||||||
|
s.Log.Info("Health check handler installed")
|
||||||
|
s.AddRoute("GET /health", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.Log.Debug("Health check handler called")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(w, "Hello, World!")
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Package web provides HTTP server related functions and structures.
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Server represents a HTTP server with logging and routing capabilities.
|
||||||
|
type Server struct {
|
||||||
|
Log *zap.Logger
|
||||||
|
Router *http.ServeMux
|
||||||
|
Services map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddService adds a service to the server's service map.
|
||||||
|
func (s *Server) AddService(name string, service interface{}) {
|
||||||
|
s.Services[name] = service
|
||||||
|
}
|
Loading…
Reference in New Issue