Skip to content

Commit fed3728

Browse files
committedJul 12, 2023
Project structure created and Creating the necessary files
1 parent 2aa72dc commit fed3728

29 files changed

+2002
-0
lines changed
 

‎.gitignore

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Output of the go coverage tool, specifically when used with LiteIDE
15+
*.out
16+
17+
# Dependency directories (remove the comment below to include it)
18+
# vendor/
19+
20+
# Go workspace file
21+
go.work
22+
*.DS_store
23+
*.log
24+
*.log.gz
25+
__debug_bin
26+
*/cmd/uploads

‎api/api.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package api
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/Asrez/GoAPIBlog/config"
7+
"github.com/gin-gonic/gin"
8+
)
9+
10+
func InitServer(){
11+
config := config.GetConfig()
12+
r := gin.New()
13+
r.Use(gin.Logger() ,gin.Recovery())
14+
15+
// v1 := r.Group("api/v1")
16+
// {
17+
18+
// }
19+
20+
r.Run(fmt.Sprintf(":%s", config.Server.InternalPort))
21+
}

‎api/dto/auth.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package dto
2+
3+
type TokenDetail struct {
4+
AccessToken string `json:"accessToken"`
5+
RefreshToken string `json:"refreshToken"`
6+
AccessTokenExpireTime int64 `json:"accessTokenExpireTime"`
7+
RefreshTokenExpireTime int64 `json:"refreshTokenExpireTime"`
8+
}
9+
10+
type RegisterUserByUsernameRequest struct {
11+
FirstName string `json:"firstName" binding:"required,min=3"`
12+
LastName string `json:"lastName" binding:"required,min=6"`
13+
Username string `json:"username" binding:"required,min=5"`
14+
Email string `json:"email" binding:"min=6,email"`
15+
Password string `json:"password" binding:"required,password,min=6"`
16+
}
17+
18+
type LoginByUsernameRequest struct {
19+
Username string `json:"username" binding:"required,min=5"`
20+
Password string `json:"password" binding:"required,min=6"`
21+
}

‎api/helpers/base_response.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package helper
2+
3+
import (
4+
validation "github.com/Asrez/GoAPIBlog/api/validations"
5+
)
6+
7+
8+
type BaseHttpResponse struct {
9+
Result any `json:"result"`
10+
Success bool `json:"success"`
11+
ResultCode ResultCode `json:"resultCode"`
12+
ValidationErrors *[]validation.ValidationError `json:"validationErrors"`
13+
Error any `json:"error"`
14+
}
15+
16+
func GenerateBaseResponse(result any, success bool, resultCode ResultCode) *BaseHttpResponse {
17+
return &BaseHttpResponse{Result: result,
18+
Success: success,
19+
ResultCode: resultCode,
20+
}
21+
}
22+
23+
func GenerateBaseResponseWithError(result any, success bool, resultCode ResultCode, err error) *BaseHttpResponse {
24+
return &BaseHttpResponse{Result: result,
25+
Success: success,
26+
ResultCode: resultCode,
27+
Error: err.Error(),
28+
}
29+
30+
}
31+
32+
func GenerateBaseResponseWithAnyError(result any, success bool, resultCode ResultCode, err any) *BaseHttpResponse {
33+
return &BaseHttpResponse{Result: result,
34+
Success: success,
35+
ResultCode: resultCode,
36+
Error: err,
37+
}
38+
}
39+
40+
func GenerateBaseResponseWithValidationError(result any, success bool, resultCode ResultCode, err error) *BaseHttpResponse {
41+
return &BaseHttpResponse{Result: result,
42+
Success: success,
43+
ResultCode: resultCode,
44+
ValidationErrors: validation.GetValidationErrors(err),
45+
}
46+
}

‎api/helpers/result_code.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package helper
2+
3+
type ResultCode int
4+
5+
const (
6+
Success ResultCode = 0
7+
ValidationError ResultCode = 40001
8+
AuthError ResultCode = 40101
9+
ForbiddenError ResultCode = 40301
10+
NotFoundError ResultCode = 40401
11+
LimiterError ResultCode = 42901
12+
OtpLimiterError ResultCode = 42902
13+
CustomRecovery ResultCode = 50001
14+
InternalError ResultCode = 50002
15+
)

‎api/helpers/status_code_mapping.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package helper
2+
3+
import (
4+
"net/http"
5+
"github.com/Asrez/GoAPIBlog/pkg/service_errors"
6+
)
7+
8+
var StatusCodeMapping = map[string]int{
9+
// User
10+
service_errors.EmailExists: 409,
11+
service_errors.UsernameExists: 409,
12+
service_errors.RecordNotFound: 404,
13+
service_errors.PermissionDenied: 403,
14+
}
15+
16+
func TranslateErrorToStatusCode(err error) int {
17+
value, ok := StatusCodeMapping[err.Error()]
18+
if !ok {
19+
return http.StatusInternalServerError
20+
}
21+
return value
22+
}

‎api/middlewares/auth.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package middlewares
2+
3+
import (
4+
"net/http"
5+
"strings"
6+
helper "github.com/Asrez/GoAPIBlog/api/helpers"
7+
"github.com/Asrez/GoAPIBlog/config"
8+
"github.com/Asrez/GoAPIBlog/constants"
9+
"github.com/Asrez/GoAPIBlog/pkg/service_errors"
10+
"github.com/Asrez/GoAPIBlog/services"
11+
"github.com/gin-gonic/gin"
12+
"github.com/golang-jwt/jwt"
13+
)
14+
15+
func Authentication(cfg *config.Config) gin.HandlerFunc {
16+
var tokenService = services.NewTokenService(cfg)
17+
18+
return func(c *gin.Context) {
19+
var err error
20+
claimMap := map[string]interface{}{}
21+
auth := c.GetHeader(constants.AuthorizationHeaderKey)
22+
token := strings.Split(auth, " ")
23+
if auth == "" {
24+
err = &service_errors.ServiceError{EndUserMessage: service_errors.TokenRequired}
25+
} else {
26+
claimMap, err = tokenService.GetClaims(token[1])
27+
if err != nil {
28+
switch err.(*jwt.ValidationError).Errors {
29+
case jwt.ValidationErrorExpired:
30+
err = &service_errors.ServiceError{EndUserMessage: service_errors.TokenExpired}
31+
default:
32+
err = &service_errors.ServiceError{EndUserMessage: service_errors.TokenInvalid}
33+
}
34+
}
35+
}
36+
if err != nil {
37+
c.AbortWithStatusJSON(http.StatusUnauthorized, helper.GenerateBaseResponseWithError(
38+
nil, false, helper.AuthError, err,
39+
))
40+
return
41+
}
42+
43+
c.Set(constants.UserIdKey, claimMap[constants.UserIdKey])
44+
c.Set(constants.FirstNameKey, claimMap[constants.FirstNameKey])
45+
c.Set(constants.LastNameKey, claimMap[constants.LastNameKey])
46+
c.Set(constants.UsernameKey, claimMap[constants.UsernameKey])
47+
c.Set(constants.EmailKey, claimMap[constants.EmailKey])
48+
c.Set(constants.ExpireTimeKey, claimMap[constants.ExpireTimeKey])
49+
50+
c.Next()
51+
}
52+
}

‎api/validations/custom.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package validation
2+
3+
import (
4+
"errors"
5+
"github.com/go-playground/validator/v10"
6+
)
7+
8+
type ValidationError struct {
9+
Property string `json:"property"`
10+
Tag string `json:"tag"`
11+
Value string `json:"value"`
12+
Message string `json:"message"`
13+
}
14+
15+
func GetValidationErrors(err error) *[]ValidationError {
16+
var validationErrors []ValidationError
17+
var ve validator.ValidationErrors
18+
if errors.As(err,&ve){
19+
for _, err := range err.(validator.ValidationErrors){
20+
var el ValidationError
21+
el.Property = err.Field()
22+
el.Tag = err.Tag()
23+
el.Value = err.Param()
24+
validationErrors = append(validationErrors, el)
25+
}
26+
return &validationErrors
27+
}
28+
return nil
29+
}

‎api/validations/passwod.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package validation
2+
3+
import (
4+
"github.com/Asrez/GoAPIBlog/common"
5+
"github.com/go-playground/validator/v10"
6+
)
7+
8+
func PasswordValidator(fld validator.FieldLevel) bool {
9+
value, ok := fld.Field().Interface().(string)
10+
if !ok {
11+
fld.Param()
12+
return false
13+
}
14+
15+
return common.CheckPassword(value)
16+
}

‎cmd/main.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package main
2+
3+
import (
4+
"github.com/Asrez/GoAPIBlog/api"
5+
"github.com/Asrez/GoAPIBlog/config"
6+
"github.com/Asrez/GoAPIBlog/data/db"
7+
"github.com/Asrez/GoAPIBlog/data/migration"
8+
"github.com/Asrez/GoAPIBlog/pkg/logging"
9+
)
10+
11+
func main(){
12+
cfg := config.GetConfig()
13+
logger := logging.NewLogger(cfg)
14+
err := db.InitDb(cfg)
15+
defer db.CloseDb()
16+
17+
if err != nil {
18+
logger.Fatal(logging.Postgres , logging.Startup , err.Error(),nil)
19+
}
20+
21+
migration.Up()
22+
api.InitServer()
23+
}

‎common/string.go

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package common
2+
3+
import (
4+
"math/rand"
5+
"regexp"
6+
"strings"
7+
"unicode"
8+
"github.com/Asrez/GoAPIBlog/config"
9+
)
10+
11+
var (
12+
lowerCharSet = "abcdedfghijklmnopqrst"
13+
upperCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
14+
specialCharSet = "!@#$%&*"
15+
numberSet = "0123456789"
16+
allCharSet = lowerCharSet + upperCharSet + specialCharSet + numberSet
17+
)
18+
19+
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
20+
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
21+
22+
func CheckPassword(password string) bool {
23+
cfg := config.GetConfig()
24+
if len(password) < cfg.Password.MinLength {
25+
return false
26+
}
27+
28+
if cfg.Password.IncludeChars && !HasLetter(password) {
29+
return false
30+
}
31+
32+
if cfg.Password.IncludeDigits && !HasDigits(password) {
33+
return false
34+
}
35+
36+
if cfg.Password.IncludeLowercase && !HasLower(password) {
37+
return false
38+
}
39+
40+
if cfg.Password.IncludeUppercase && !HasUpper(password) {
41+
return false
42+
}
43+
44+
return true
45+
}
46+
47+
func GeneratePassword() string {
48+
var password strings.Builder
49+
50+
cfg := config.GetConfig()
51+
passwordLength := cfg.Password.MinLength + 2
52+
minSpecialChar := 2
53+
minNum := 3
54+
if !cfg.Password.IncludeDigits {
55+
minNum = 0
56+
}
57+
58+
minUpperCase := 3
59+
if !cfg.Password.IncludeUppercase {
60+
minUpperCase = 0
61+
}
62+
63+
minLowerCase := 3
64+
if !cfg.Password.IncludeLowercase {
65+
minLowerCase = 0
66+
}
67+
68+
//Set special character
69+
for i := 0; i < minSpecialChar; i++ {
70+
random := rand.Intn(len(specialCharSet))
71+
password.WriteString(string(specialCharSet[random]))
72+
}
73+
74+
//Set numeric
75+
for i := 0; i < minNum; i++ {
76+
random := rand.Intn(len(numberSet))
77+
password.WriteString(string(numberSet[random]))
78+
}
79+
80+
//Set uppercase
81+
for i := 0; i < minUpperCase; i++ {
82+
random := rand.Intn(len(upperCharSet))
83+
password.WriteString(string(upperCharSet[random]))
84+
}
85+
86+
//Set lowercase
87+
for i := 0; i < minLowerCase; i++ {
88+
random := rand.Intn(len(lowerCharSet))
89+
password.WriteString(string(lowerCharSet[random]))
90+
}
91+
92+
remainingLength := passwordLength - minSpecialChar - minNum - minUpperCase
93+
for i := 0; i < remainingLength; i++ {
94+
random := rand.Intn(len(allCharSet))
95+
password.WriteString(string(allCharSet[random]))
96+
}
97+
inRune := []rune(password.String())
98+
rand.Shuffle(len(inRune), func(i, j int) {
99+
inRune[i], inRune[j] = inRune[j], inRune[i]
100+
})
101+
return string(inRune)
102+
}
103+
104+
func HasUpper(s string) bool {
105+
for _, r := range s {
106+
if unicode.IsUpper(r) && unicode.IsLetter(r) {
107+
return true
108+
}
109+
}
110+
return false
111+
}
112+
113+
func HasLower(s string) bool {
114+
for _, r := range s {
115+
if unicode.IsLower(r) && unicode.IsLetter(r) {
116+
return true
117+
}
118+
}
119+
return false
120+
}
121+
122+
func HasLetter(s string) bool {
123+
for _, r := range s {
124+
if unicode.IsLetter(r) {
125+
return true
126+
}
127+
}
128+
return false
129+
}
130+
131+
func HasDigits(s string) bool {
132+
for _, r := range s {
133+
if unicode.IsDigit(r) {
134+
return true
135+
}
136+
}
137+
return false
138+
}
139+
140+
func ToSnakeCase(str string) string {
141+
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
142+
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
143+
return strings.ToLower(snake)
144+
}

‎config/config-development.yml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
server:
2+
internalPort: 8000
3+
externalPort: 8000
4+
runMode: debug
5+
6+
logger:
7+
filePath: ../logs/
8+
encoding: json
9+
level: debug
10+
logger: zap
11+
12+
postgres :
13+
host : localhost
14+
port : 5432
15+
user : postgres
16+
password : sH1382@@
17+
dbName : weather
18+
sslMode : disable
19+
maxIdleConns: 15
20+
maxOpenConns: 100
21+
connMaxLifetime: 5
22+
23+
24+
password:
25+
includeChars: true
26+
includeDigits: true
27+
minLength: 6
28+
maxLength: 64
29+
includeUppercase: true
30+
includeLowercase: true
31+
32+
jwt:
33+
secret: "mySecretKey"
34+
refreshSecret: "mySecretKey"
35+
accessTokenExpireDuration: 1440
36+
refreshTokenExpireDuration: 60

‎config/config.go

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package config
2+
3+
import (
4+
"errors"
5+
"log"
6+
"os"
7+
"time"
8+
"github.com/spf13/viper"
9+
)
10+
11+
type Config struct {
12+
Server ServerConfig
13+
Postgres PostgresConfig
14+
Password PasswordConfig
15+
Logger LoggerConfig
16+
JWT JWTConfig
17+
API APIConfig
18+
}
19+
20+
type ServerConfig struct {
21+
InternalPort string
22+
ExternalPort string
23+
RunMode string
24+
}
25+
26+
type LoggerConfig struct {
27+
FilePath string
28+
Encoding string
29+
Level string
30+
Logger string
31+
}
32+
33+
type PostgresConfig struct {
34+
Host string
35+
Port string
36+
User string
37+
Password string
38+
DbName string
39+
SSLMode string
40+
MaxIdleConns int
41+
MaxOpenConns int
42+
ConnMaxLifetime time.Duration
43+
}
44+
45+
46+
type PasswordConfig struct {
47+
IncludeChars bool
48+
IncludeDigits bool
49+
MinLength int
50+
MaxLength int
51+
IncludeUppercase bool
52+
IncludeLowercase bool
53+
}
54+
55+
56+
57+
type JWTConfig struct {
58+
AccessTokenExpireDuration time.Duration
59+
RefreshTokenExpireDuration time.Duration
60+
Secret string
61+
RefreshSecret string
62+
}
63+
64+
type APIConfig struct {
65+
BaseUrl string
66+
Token string
67+
68+
}
69+
70+
func GetConfig() *Config {
71+
cfgPath := getConfigPath(os.Getenv("APP_ENV"))
72+
v, err := LoadConfig(cfgPath, "yml")
73+
if err != nil {
74+
log.Fatalf("Error in load config %v", err)
75+
}
76+
77+
cfg, err := ParseConfig(v)
78+
envPort := os.Getenv("PORT")
79+
if envPort != ""{
80+
cfg.Server.ExternalPort = envPort
81+
log.Printf("Set external port from environment -> %s", cfg.Server.ExternalPort)
82+
}else{
83+
cfg.Server.ExternalPort = cfg.Server.InternalPort
84+
log.Printf("Set external port from environment -> %s", cfg.Server.ExternalPort)
85+
}
86+
if err != nil {
87+
log.Fatalf("Error in parse config %v", err)
88+
}
89+
90+
return cfg
91+
}
92+
93+
func ParseConfig(v *viper.Viper) (*Config, error) {
94+
var cfg Config
95+
err := v.Unmarshal(&cfg)
96+
if err != nil {
97+
log.Printf("Unable to parse config: %v", err)
98+
return nil, err
99+
}
100+
return &cfg, nil
101+
}
102+
func LoadConfig(filename string, fileType string) (*viper.Viper, error) {
103+
v := viper.New()
104+
v.SetConfigType(fileType)
105+
v.SetConfigName(filename)
106+
v.AddConfigPath(".")
107+
v.AutomaticEnv()
108+
109+
err := v.ReadInConfig()
110+
if err != nil {
111+
log.Printf("Unable to read config: %v", err)
112+
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
113+
return nil, errors.New("config file not found")
114+
}
115+
return nil, err
116+
}
117+
return v, nil
118+
}
119+
120+
func getConfigPath(env string) string {
121+
return "../config/config-development"
122+
}

‎constants/constants.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package constants
2+
3+
const (
4+
AuthorizationHeaderKey string = "Authorization"
5+
UserIdKey string = "UserId"
6+
FirstNameKey string = "FirstName"
7+
LastNameKey string = "LastName"
8+
UsernameKey string = "Username"
9+
EmailKey string = "Email"
10+
ExpireTimeKey string = "Exp"
11+
)

‎data/db/postgres.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package db
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"github.com/Asrez/GoAPIBlog/config"
7+
"gorm.io/driver/postgres"
8+
"gorm.io/gorm"
9+
)
10+
11+
var dbClient *gorm.DB
12+
13+
func InitDb(cfg *config.Config) error {
14+
var err error
15+
cnn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=Asia/Tehran",
16+
cfg.Postgres.Host, cfg.Postgres.Port, cfg.Postgres.User, cfg.Postgres.Password,
17+
cfg.Postgres.DbName, cfg.Postgres.SSLMode)
18+
19+
dbClient, err = gorm.Open(postgres.Open(cnn), &gorm.Config{})
20+
if err != nil {
21+
return err
22+
}
23+
24+
sqlDb, _ := dbClient.DB()
25+
err = sqlDb.Ping()
26+
if err != nil {
27+
return err
28+
}
29+
30+
sqlDb.SetMaxIdleConns(10)
31+
sqlDb.SetMaxOpenConns(100)
32+
33+
log.Println("Db connection established")
34+
return nil
35+
}
36+
37+
func GetDb() *gorm.DB {
38+
return dbClient
39+
}
40+
41+
func CloseDb() {
42+
con, _ := dbClient.DB()
43+
con.Close()
44+
}

‎data/migration/init.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package migration
2+
3+
import (
4+
"github.com/Asrez/GoAPIBlog/config"
5+
"github.com/Asrez/GoAPIBlog/data/db"
6+
"github.com/Asrez/GoAPIBlog/pkg/logging"
7+
"gorm.io/gorm"
8+
)
9+
10+
11+
var logger = logging.NewLogger(config.GetConfig())
12+
13+
14+
func Up(){
15+
database := db.GetDb()
16+
createTables(database)
17+
logger.Info(logging.Postgres, logging.Migration, "UP", nil)
18+
19+
}
20+
21+
22+
func addNewTable(database *gorm.DB, model interface{}, tables []interface{}) []interface{} {
23+
if !database.Migrator().HasTable(model) {
24+
tables = append(tables, model)
25+
}
26+
return tables
27+
}
28+
29+
func createTables(database *gorm.DB){
30+
tables := []interface{}{}
31+
32+
// tables = addNewTable(database , models.Categories{} , tables)
33+
34+
35+
err := database.Migrator().CreateTable(tables...)
36+
if err != nil {
37+
logger.Error(logging.Postgres, logging.Migration, err.Error(), nil)
38+
}
39+
logger.Info(logging.Postgres, logging.Migration, "tables created", nil)
40+
41+
}

‎data/models/base_models.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package models
2+
3+
import (
4+
"database/sql"
5+
"time"
6+
"gorm.io/gorm"
7+
)
8+
9+
type BaseModel struct {
10+
Id int `gorm:"primarykey"`
11+
12+
CreatedAt time.Time `gorm:"type:TIMESTAMP with time zone;not null"`
13+
ModifiedAt sql.NullTime `gorm:"type:TIMESTAMP with time zone;null"`
14+
DeletedAt sql.NullTime `gorm:"type:TIMESTAMP with time zone;null"`
15+
16+
CreatedBy int `gorm:"not null"`
17+
ModifiedBy *sql.NullInt64 `gorm:"null"`
18+
DeletedBy *sql.NullInt64 `gorm:"null"`
19+
}
20+
21+
func (m *BaseModel) BeforeCreate(tx *gorm.DB) (err error) {
22+
value := tx.Statement.Context.Value("UserId")
23+
var userId = -1
24+
// TODO: check userId type
25+
if value != nil {
26+
userId = int(value.(float64))
27+
}
28+
m.CreatedAt = time.Now().UTC()
29+
m.CreatedBy = userId
30+
return
31+
}
32+
33+
func (m *BaseModel) BeforeUpdate(tx *gorm.DB) (err error) {
34+
value := tx.Statement.Context.Value("UserId")
35+
var userId = &sql.NullInt64{Valid: false}
36+
// TODO: check userId type
37+
if value != nil {
38+
userId = &sql.NullInt64{Valid: true, Int64: int64(value.(float64))}
39+
}
40+
m.ModifiedAt = sql.NullTime{Time: time.Now().UTC(), Valid: true}
41+
m.ModifiedBy = userId
42+
return
43+
}
44+
45+
func (m *BaseModel) BeforeDelete(tx *gorm.DB) (err error) {
46+
value := tx.Statement.Context.Value("UserId")
47+
var userId = &sql.NullInt64{Valid: false}
48+
// TODO: check userId type
49+
if value != nil {
50+
userId = &sql.NullInt64{Valid: true, Int64: int64(value.(float64))}
51+
}
52+
m.DeletedAt = sql.NullTime{Time: time.Now().UTC(), Valid: true}
53+
m.DeletedBy = userId
54+
return
55+
}

‎data/models/models.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package models
2+
3+
4+
type (
5+
BaseTable struct {
6+
Id int `gorm:"primarykey"`
7+
Name string `gorm:"not null"`
8+
}
9+
)

‎go.mod

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
module github.com/Asrez/GoAPIBlog
2+
3+
go 1.20
4+
5+
require github.com/gin-gonic/gin v1.9.1
6+
7+
require (
8+
github.com/fsnotify/fsnotify v1.6.0 // indirect
9+
github.com/hashicorp/hcl v1.0.0 // indirect
10+
github.com/jackc/pgpassfile v1.0.0 // indirect
11+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
12+
github.com/jackc/pgx/v5 v5.3.1 // indirect
13+
github.com/jinzhu/inflection v1.0.0 // indirect
14+
github.com/jinzhu/now v1.1.5 // indirect
15+
github.com/magiconair/properties v1.8.7 // indirect
16+
github.com/mattn/go-colorable v0.1.12 // indirect
17+
github.com/mitchellh/mapstructure v1.5.0 // indirect
18+
github.com/pkg/errors v0.9.1 // indirect
19+
github.com/spf13/afero v1.9.5 // indirect
20+
github.com/spf13/cast v1.5.1 // indirect
21+
github.com/spf13/jwalterweatherman v1.1.0 // indirect
22+
github.com/spf13/pflag v1.0.5 // indirect
23+
github.com/subosito/gotenv v1.4.2 // indirect
24+
go.uber.org/atomic v1.9.0 // indirect
25+
go.uber.org/multierr v1.8.0 // indirect
26+
gopkg.in/ini.v1 v1.67.0 // indirect
27+
)
28+
29+
require (
30+
github.com/bxcodec/faker/v3 v3.8.1
31+
github.com/bytedance/sonic v1.9.1 // indirect
32+
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
33+
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
34+
github.com/gin-contrib/sse v0.1.0 // indirect
35+
github.com/go-playground/locales v0.14.1 // indirect
36+
github.com/go-playground/universal-translator v0.18.1 // indirect
37+
github.com/go-playground/validator/v10 v10.14.0 // indirect
38+
github.com/goccy/go-json v0.10.2 // indirect
39+
github.com/golang-jwt/jwt v3.2.2+incompatible
40+
github.com/google/uuid v1.3.0
41+
github.com/json-iterator/go v1.1.12 // indirect
42+
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
43+
github.com/leodido/go-urn v1.2.4 // indirect
44+
github.com/mattn/go-isatty v0.0.19 // indirect
45+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
46+
github.com/modern-go/reflect2 v1.0.2 // indirect
47+
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
48+
github.com/rs/zerolog v1.29.1
49+
github.com/spf13/viper v1.16.0
50+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
51+
github.com/ugorji/go/codec v1.2.11 // indirect
52+
go.uber.org/zap v1.24.0
53+
golang.org/x/arch v0.3.0 // indirect
54+
golang.org/x/crypto v0.9.0 // indirect
55+
golang.org/x/net v0.10.0 // indirect
56+
golang.org/x/sys v0.8.0 // indirect
57+
golang.org/x/text v0.9.0 // indirect
58+
google.golang.org/protobuf v1.30.0 // indirect
59+
gopkg.in/natefinch/lumberjack.v2 v2.2.1
60+
gopkg.in/yaml.v3 v3.0.1 // indirect
61+
gorm.io/driver/postgres v1.5.2
62+
gorm.io/gorm v1.25.2
63+
)

‎go.sum

+572
Large diffs are not rendered by default.

‎pkg/logging/category.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package logging
2+
3+
4+
type Category string
5+
type SubCategory string
6+
type ExtraKey string
7+
8+
9+
const (
10+
General Category = "General"
11+
Internal Category = "Internal"
12+
Postgres Category = "Postgres"
13+
Redis Category = "Redis"
14+
Validation Category = "Validation"
15+
RequestResponse Category = "RequestResponse"
16+
)
17+
const (
18+
// General
19+
Startup SubCategory = "Startup"
20+
ExternalService SubCategory = "ExternalService"
21+
22+
// Postgres
23+
Migration SubCategory = "Migration"
24+
Select SubCategory = "Select"
25+
Rollback SubCategory = "Rollback"
26+
Update SubCategory = "Update"
27+
Delete SubCategory = "Delete"
28+
Insert SubCategory = "Insert"
29+
30+
// Internal
31+
Api SubCategory = "Api"
32+
)
33+
34+
const (
35+
AppName ExtraKey = "AppName"
36+
LoggerName ExtraKey = "Logger"
37+
ClientIp ExtraKey = "ClientIp"
38+
HostIp ExtraKey = "HostIp"
39+
Method ExtraKey = "Method"
40+
StatusCode ExtraKey = "StatusCode"
41+
BodySize ExtraKey = "BodySize"
42+
Path ExtraKey = "Path"
43+
Latency ExtraKey = "Latency"
44+
RequestBody ExtraKey = "RequestBody"
45+
ResponseBody ExtraKey = "ResponseBody"
46+
ErrorMessage ExtraKey = "ErrorMessage"
47+
)

‎pkg/logging/log_helpers.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package logging
2+
3+
func logParamsToZapParams(keys map[ExtraKey]interface{}) []interface{} {
4+
params := make([]interface{}, 0, len(keys))
5+
6+
for k, v := range keys {
7+
params = append(params, string(k))
8+
params = append(params, v)
9+
}
10+
11+
return params
12+
}
13+
14+
func logParamsToZeroParams(keys map[ExtraKey]interface{}) map[string]interface{} {
15+
params := map[string]interface{}{}
16+
17+
for k, v := range keys {
18+
params[string(k)] = v
19+
}
20+
21+
return params
22+
}

‎pkg/logging/logger.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package logging
2+
3+
import "github.com/Asrez/GoAPIBlog/config"
4+
5+
type Logger interface {
6+
Init()
7+
8+
Debug(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{})
9+
Debugf(template string, args ...interface{})
10+
11+
Info(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{})
12+
Infof(template string, args ...interface{})
13+
14+
Warn(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{})
15+
Warnf(template string, args ...interface{})
16+
17+
Error(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{})
18+
Errorf(template string, args ...interface{})
19+
20+
Fatal(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{})
21+
Fatalf(template string, args ...interface{})
22+
}
23+
24+
func NewLogger(cfg *config.Config) Logger {
25+
if cfg.Logger.Logger == "zap" {
26+
return newZapLogger(cfg)
27+
} else if cfg.Logger.Logger == "zerolog" {
28+
return newZeroLogger(cfg)
29+
}
30+
panic("logger not supported")
31+
}

‎pkg/logging/zap_logger.go

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package logging
2+
3+
import (
4+
"github.com/Asrez/GoAPIBlog/config"
5+
"go.uber.org/zap"
6+
"go.uber.org/zap/zapcore"
7+
"gopkg.in/natefinch/lumberjack.v2"
8+
"fmt"
9+
"github.com/google/uuid"
10+
"time"
11+
)
12+
13+
var zapSinLogger *zap.SugaredLogger
14+
15+
type zapLogger struct {
16+
cfg *config.Config
17+
logger *zap.SugaredLogger
18+
}
19+
20+
var zapLogLevelMapping = map[string]zapcore.Level{
21+
"debug": zapcore.DebugLevel,
22+
"info": zapcore.InfoLevel,
23+
"warn": zapcore.WarnLevel,
24+
"error": zapcore.ErrorLevel,
25+
"fatal": zapcore.FatalLevel,
26+
}
27+
28+
func newZapLogger(cfg *config.Config) *zapLogger {
29+
logger := &zapLogger{cfg: cfg}
30+
logger.Init()
31+
return logger
32+
}
33+
34+
func (l *zapLogger) getLogLevel() zapcore.Level {
35+
level, exists := zapLogLevelMapping[l.cfg.Logger.Level]
36+
if !exists {
37+
return zapcore.DebugLevel
38+
}
39+
return level
40+
}
41+
42+
func (l *zapLogger) Init() {
43+
44+
fileName := fmt.Sprintf("%s%s-%s.%s",l.cfg.Logger.FilePath,time.Now().Format("2006-01-02"),uuid.New(),"log")
45+
w := zapcore.AddSync(&lumberjack.Logger{
46+
Filename: fileName,
47+
MaxSize: 1,
48+
MaxAge: 20,
49+
LocalTime: true,
50+
MaxBackups: 5,
51+
Compress: true,
52+
})
53+
54+
config := zap.NewProductionEncoderConfig()
55+
config.EncodeTime = zapcore.ISO8601TimeEncoder
56+
57+
core := zapcore.NewCore(
58+
zapcore.NewJSONEncoder(config),
59+
w,
60+
l.getLogLevel(),
61+
)
62+
63+
logger := zap.New(core, zap.AddCaller(),
64+
zap.AddCallerSkip(1),
65+
zap.AddStacktrace(zapcore.ErrorLevel),
66+
).Sugar()
67+
68+
zapSinLogger = logger.With("AppName", "MyApp", "LoggerName", "Zaplog")
69+
70+
71+
l.logger = zapSinLogger
72+
}
73+
74+
func (l *zapLogger) Debug(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
75+
params := prepareLogInfo(cat, sub, extra)
76+
77+
l.logger.Debugw(msg, params...)
78+
}
79+
80+
func (l *zapLogger) Debugf(template string, args ...interface{}) {
81+
l.logger.Debugf(template, args)
82+
}
83+
84+
func (l *zapLogger) Info(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
85+
params := prepareLogInfo(cat, sub, extra)
86+
l.logger.Infow(msg, params...)
87+
}
88+
89+
func (l *zapLogger) Infof(template string, args ...interface{}) {
90+
l.logger.Infof(template, args)
91+
}
92+
93+
func (l *zapLogger) Warn(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
94+
params := prepareLogInfo(cat, sub, extra)
95+
l.logger.Warnw(msg, params...)
96+
}
97+
98+
func (l *zapLogger) Warnf(template string, args ...interface{}) {
99+
l.logger.Warnf(template, args)
100+
}
101+
102+
func (l *zapLogger) Error(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
103+
params := prepareLogInfo(cat, sub, extra)
104+
l.logger.Errorw(msg, params...)
105+
}
106+
107+
func (l *zapLogger) Errorf(template string, args ...interface{}) {
108+
l.logger.Errorf(template, args)
109+
}
110+
111+
func (l *zapLogger) Fatal(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
112+
params := prepareLogInfo(cat, sub, extra)
113+
l.logger.Fatalw(msg, params...)
114+
}
115+
116+
func (l *zapLogger) Fatalf(template string, args ...interface{}) {
117+
l.logger.Fatalf(template, args)
118+
}
119+
120+
func prepareLogInfo(cat Category, sub SubCategory, extra map[ExtraKey]interface{}) []interface{} {
121+
if extra == nil {
122+
extra = make(map[ExtraKey]interface{})
123+
}
124+
extra["Category"] = cat
125+
extra["SubCategory"] = sub
126+
127+
return logParamsToZapParams(extra)
128+
}

‎pkg/logging/zero_logger.go

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package logging
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"sync"
7+
"time"
8+
"github.com/google/uuid"
9+
"github.com/Asrez/GoAPIBlog/config"
10+
"github.com/rs/zerolog"
11+
"github.com/rs/zerolog/pkgerrors"
12+
)
13+
14+
var once sync.Once
15+
var zeroSinLogger *zerolog.Logger
16+
17+
type zeroLogger struct {
18+
cfg *config.Config
19+
logger *zerolog.Logger
20+
}
21+
22+
var zeroLogLevelMapping = map[string]zerolog.Level{
23+
"debug": zerolog.DebugLevel,
24+
"info": zerolog.InfoLevel,
25+
"warn": zerolog.WarnLevel,
26+
"error": zerolog.ErrorLevel,
27+
"fatal": zerolog.FatalLevel,
28+
}
29+
30+
func newZeroLogger(cfg *config.Config) *zeroLogger {
31+
logger := &zeroLogger{cfg: cfg}
32+
logger.Init()
33+
return logger
34+
}
35+
36+
func (l *zeroLogger) getLogLevel() zerolog.Level {
37+
level, exists := zeroLogLevelMapping[l.cfg.Logger.Level]
38+
if !exists {
39+
return zerolog.DebugLevel
40+
}
41+
return level
42+
}
43+
44+
func (l *zeroLogger) Init() {
45+
once.Do(func() {
46+
47+
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
48+
fileName := fmt.Sprintf("%s%s-%s.%s",l.cfg.Logger.FilePath,time.Now().Format("2006-01-02"),uuid.New(),"log")
49+
50+
file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
51+
if err != nil {
52+
panic("could not open log file")
53+
}
54+
55+
var logger = zerolog.New(file).
56+
With().
57+
Timestamp().
58+
Str("AppName", "MyApp").
59+
Str("LoggerName", "Zerolog").
60+
Logger()
61+
zerolog.SetGlobalLevel(l.getLogLevel())
62+
zeroSinLogger = &logger
63+
})
64+
l.logger = zeroSinLogger
65+
}
66+
67+
func (l *zeroLogger) Debug(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
68+
69+
l.logger.
70+
Debug().
71+
Str("Category", string(cat)).
72+
Str("SubCategory", string(sub)).
73+
Fields(logParamsToZeroParams(extra)).
74+
Msg(msg)
75+
}
76+
77+
func (l *zeroLogger) Debugf(template string, args ...interface{}) {
78+
l.logger.
79+
Debug().
80+
Msgf(template, args...)
81+
}
82+
83+
func (l *zeroLogger) Info(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
84+
85+
l.logger.
86+
Info().
87+
Str("Category", string(cat)).
88+
Str("SubCategory", string(sub)).
89+
Fields(logParamsToZeroParams(extra)).
90+
Msg(msg)
91+
}
92+
93+
func (l *zeroLogger) Infof(template string, args ...interface{}) {
94+
l.logger.
95+
Info().
96+
Msgf(template, args...)
97+
}
98+
99+
func (l *zeroLogger) Warn(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
100+
101+
l.logger.
102+
Warn().
103+
Str("Category", string(cat)).
104+
Str("SubCategory", string(sub)).
105+
Fields(logParamsToZeroParams(extra)).
106+
Msg(msg)
107+
}
108+
109+
func (l *zeroLogger) Warnf(template string, args ...interface{}) {
110+
l.logger.
111+
Warn().
112+
Msgf(template, args...)
113+
}
114+
115+
func (l *zeroLogger) Error(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
116+
117+
l.logger.
118+
Error().
119+
Str("Category", string(cat)).
120+
Str("SubCategory", string(sub)).
121+
Fields(logParamsToZeroParams(extra)).
122+
Msg(msg)
123+
}
124+
125+
func (l *zeroLogger) Errorf(template string, args ...interface{}) {
126+
l.logger.
127+
Error().
128+
Msgf(template, args...)
129+
}
130+
131+
func (l *zeroLogger) Fatal(cat Category, sub SubCategory, msg string, extra map[ExtraKey]interface{}) {
132+
133+
l.logger.
134+
Fatal().
135+
Str("Category", string(cat)).
136+
Str("SubCategory", string(sub)).
137+
Fields(logParamsToZeroParams(extra)).
138+
Msg(msg)
139+
}
140+
141+
func (l *zeroLogger) Fatalf(template string, args ...interface{}) {
142+
l.logger.
143+
Fatal().
144+
Msgf(template, args...)
145+
}

‎pkg/service_errors/error_code.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package service_errors
2+
3+
const (
4+
// Token
5+
UnExpectedError = "Expected error"
6+
ClaimsNotFound = "Claims not found"
7+
TokenRequired = "token required"
8+
TokenExpired = "token expired"
9+
TokenInvalid = "token invalid"
10+
11+
// OTP
12+
OptExists = "Otp exists"
13+
OtpUsed = "Otp used"
14+
OtpNotValid = "Otp invalid"
15+
16+
// User
17+
EmailExists = "Email exists"
18+
UsernameExists = "Username exists"
19+
PermissionDenied = "Permission denied"
20+
21+
// DB
22+
RecordNotFound = "record not found"
23+
)

‎pkg/service_errors/service_error.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package service_errors
2+
3+
type ServiceError struct {
4+
EndUserMessage string `json:"endUserMessage"`
5+
TechnicalMessage string `json:"technicalMessage"`
6+
Err error
7+
}
8+
9+
func (s *ServiceError) Error() string {
10+
return s.EndUserMessage
11+
}

‎services/token_servies.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package services
2+
3+
import (
4+
"time"
5+
6+
"github.com/Asrez/GoAPIBlog/api/dto"
7+
"github.com/Asrez/GoAPIBlog/config"
8+
"github.com/Asrez/GoAPIBlog/constants"
9+
"github.com/Asrez/GoAPIBlog/pkg/logging"
10+
"github.com/Asrez/GoAPIBlog/pkg/service_errors"
11+
"github.com/golang-jwt/jwt"
12+
)
13+
14+
type TokenService struct {
15+
logger logging.Logger
16+
cfg *config.Config
17+
}
18+
19+
type tokenDto struct {
20+
UserId int
21+
FirstName string
22+
LastName string
23+
Username string
24+
MobileNumber string
25+
Email string
26+
Roles []string
27+
}
28+
29+
func NewTokenService(cfg *config.Config) *TokenService {
30+
logger := logging.NewLogger(cfg)
31+
return &TokenService{
32+
cfg: cfg,
33+
logger: logger,
34+
}
35+
}
36+
37+
func (s *TokenService) GenerateToken(token *tokenDto) (*dto.TokenDetail, error) {
38+
td := &dto.TokenDetail{}
39+
td.AccessTokenExpireTime = time.Now().Add(s.cfg.JWT.AccessTokenExpireDuration * time.Minute).Unix()
40+
td.RefreshTokenExpireTime = time.Now().Add(s.cfg.JWT.RefreshTokenExpireDuration * time.Minute).Unix()
41+
42+
atc := jwt.MapClaims{}
43+
44+
atc[constants.UserIdKey] = token.UserId
45+
atc[constants.FirstNameKey] = token.FirstName
46+
atc[constants.LastNameKey] = token.LastName
47+
atc[constants.UsernameKey] = token.Username
48+
atc[constants.EmailKey] = token.Email
49+
atc[constants.ExpireTimeKey] = td.AccessTokenExpireTime
50+
51+
at := jwt.NewWithClaims(jwt.SigningMethodHS256, atc)
52+
53+
var err error
54+
td.AccessToken, err = at.SignedString([]byte(s.cfg.JWT.Secret))
55+
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
rtc := jwt.MapClaims{}
61+
62+
rtc[constants.UserIdKey] = token.UserId
63+
rtc[constants.ExpireTimeKey] = td.RefreshTokenExpireTime
64+
65+
rt := jwt.NewWithClaims(jwt.SigningMethodHS256, rtc)
66+
67+
td.RefreshToken, err = rt.SignedString([]byte(s.cfg.JWT.RefreshSecret))
68+
69+
if err != nil {
70+
return nil, err
71+
}
72+
73+
return td, nil
74+
}
75+
76+
func (s *TokenService) VerifyToken(token string) (*jwt.Token, error) {
77+
at, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
78+
_, ok := token.Method.(*jwt.SigningMethodHMAC)
79+
if !ok {
80+
return nil, &service_errors.ServiceError{EndUserMessage: service_errors.UnExpectedError}
81+
}
82+
return []byte(s.cfg.JWT.Secret), nil
83+
})
84+
if err != nil {
85+
return nil, err
86+
}
87+
return at, nil
88+
}
89+
90+
func (s *TokenService) GetClaims(token string) (claimMap map[string]interface{}, err error) {
91+
claimMap = map[string]interface{}{}
92+
93+
verifyToken, err := s.VerifyToken(token)
94+
if err != nil {
95+
return nil, err
96+
}
97+
claims, ok := verifyToken.Claims.(jwt.MapClaims)
98+
if ok && verifyToken.Valid {
99+
for k, v := range claims {
100+
claimMap[k] = v
101+
}
102+
return claimMap, nil
103+
}
104+
return nil, &service_errors.ServiceError{EndUserMessage: service_errors.ClaimsNotFound}
105+
}

‎services/user_services.go

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package services
2+
3+
import (
4+
// "github.com/Asrez/GoAPIBlog/api/dto"
5+
"github.com/Asrez/GoAPIBlog/config"
6+
"github.com/Asrez/GoAPIBlog/pkg/logging"
7+
"github.com/Asrez/GoAPIBlog/data/db"
8+
"gorm.io/gorm"
9+
)
10+
11+
type UserService struct {
12+
logger logging.Logger
13+
cfg *config.Config
14+
tokenService *TokenService
15+
database *gorm.DB
16+
}
17+
18+
func NewUserService(cfg *config.Config) *UserService {
19+
database := db.GetDb()
20+
logger := logging.NewLogger(cfg)
21+
return &UserService{
22+
cfg: cfg,
23+
database: database,
24+
logger: logger,
25+
tokenService: NewTokenService(cfg),
26+
}
27+
}
28+
29+
// // Login by username
30+
// func (s *UserService) LoginByUsername(req *dto.LoginByUsernameRequest) (*dto.TokenDetail, error) {
31+
// var user models.User
32+
// err := s.database.
33+
// Model(&models.User{}).
34+
// Where("username = ?", req.Username).
35+
// Find(&user).Error
36+
// if err != nil {
37+
// return nil, err
38+
// }
39+
// err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
40+
// if err != nil {
41+
// return nil, err
42+
// }
43+
// tdto := tokenDto{UserId: user.Id, FirstName: user.FirstName, LastName: user.LastName,
44+
// Email: user.Email, MobileNumber: user.MobileNumber}
45+
46+
// token, err := s.tokenService.GenerateToken(&tdto)
47+
// if err != nil {
48+
// return nil, err
49+
// }
50+
// return token, nil
51+
52+
// }
53+
54+
// // Register by username
55+
// func (s *UserService) RegisterByUsername(req *dto.RegisterUserByUsernameRequest) error {
56+
// u := models.User{Username: req.Username, FirstName: req.FirstName, LastName: req.LastName, Email: req.Email}
57+
58+
// exists, err := s.existsByEmail(req.Email)
59+
// if err != nil {
60+
// return err
61+
// }
62+
// if exists {
63+
// return &service_errors.ServiceError{EndUserMessage: service_errors.EmailExists}
64+
// }
65+
// exists, err = s.existsByUsername(req.Username)
66+
// if err != nil {
67+
// return err
68+
// }
69+
// if exists {
70+
// return &service_errors.ServiceError{EndUserMessage: service_errors.UsernameExists}
71+
// }
72+
73+
// bp := []byte(req.Password)
74+
// hp, err := bcrypt.GenerateFromPassword(bp, bcrypt.DefaultCost)
75+
// if err != nil {
76+
// s.logger.Error(logging.General, logging.HashPassword, err.Error(), nil)
77+
// return err
78+
// }
79+
// u.Password = string(hp)
80+
// if err != nil {
81+
// s.logger.Error(logging.Postgres, logging.DefaultRoleNotFound, err.Error(), nil)
82+
// return err
83+
// }
84+
85+
// tx := s.database.Begin()
86+
// err = tx.Create(&u).Error
87+
// if err != nil {
88+
// tx.Rollback()
89+
// s.logger.Error(logging.Postgres, logging.Rollback, err.Error(), nil)
90+
// return err
91+
// }
92+
// tx.Commit()
93+
// return nil
94+
95+
// }
96+
97+
98+
// func (s *UserService) existsByEmail(email string) (bool, error) {
99+
// var exists bool
100+
// if err := s.database.Model(&models.User{}).
101+
// Select("count(*) > 0").
102+
// Where("email = ?", email).
103+
// Find(&exists).
104+
// Error; err != nil {
105+
// s.logger.Error(logging.Postgres, logging.Select, err.Error(), nil)
106+
// return false, err
107+
// }
108+
// return exists, nil
109+
// }
110+
111+
// func (s *UserService) existsByUsername(username string) (bool, error) {
112+
// var exists bool
113+
// if err := s.database.Model(&models.User{}).
114+
// Select("count(*) > 0").
115+
// Where("username = ?", username).
116+
// Find(&exists).
117+
// Error; err != nil {
118+
// s.logger.Error(logging.Postgres, logging.Select, err.Error(), nil)
119+
// return false, err
120+
// }
121+
// return exists, nil
122+
// }

0 commit comments

Comments
 (0)
Please sign in to comment.