package main import ( "log/slog" "net/http" "os" "time" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" "gorm.io/driver/postgres" "gorm.io/gorm" ) type User struct { ID uint `gorm:"primaryKey"` // Standard field for the primary key Username string // A regular string field Password *[]byte Email string // A pointer to a string, allowing for null values Updated int64 `gorm:"autoUpdateTime:milli"` // Use unix milli seconds as updating time Created int64 `gorm:"autoCreateTime"` // Use unix seconds as creating time Deleted gorm.DeletedAt `gorm:"index"` } type Login struct { Email string `form:"email" json:"email" xml:"email" binding:"required"` Password string `form:"password" json:"password" xml:"password" binding:"required"` } type Register struct { Email string `form:"email" json:"email" xml:"email" binding:"required"` Password string `form:"password" json:"password" xml:"password" binding:"required"` PasswordConfirmation string `form:"password_confirm" json:"password_confirm" xml:"password_confirm" binding:"required"` Username string `form:"username" json:"username" xml:"username" binding:"required"` } func main() { jsonHandler := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ Level: slog.LevelDebug, AddSource: true, }) myslog := slog.New(jsonHandler) dsn := "host=localhost user=postgres password=asd dbname=postgres port=5432" db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { myslog.Error("Could not establish connection to database!") panic("Could not establish connection to database!") } db.AutoMigrate(&User{}) sqlDB, err := db.DB() if err != nil { myslog.Error("Could not create connection pool for database!") panic("Could not create connection pool for database!") } // SetMaxIdleConns sets the maximum number of connections in the idle connection pool. sqlDB.SetMaxIdleConns(10) // SetMaxOpenConns sets the maximum number of open connections to the database. sqlDB.SetMaxOpenConns(100) // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. sqlDB.SetConnMaxLifetime(time.Hour) password := "asd" // Replace this with the password you want to encrypt hashedPassword, err := hashPassword(password) if err != nil { myslog.Info("Encryption failed:", "err", err) return } myslog.Info("Hashed Password:", "password", string(hashedPassword)) router := gin.Default() router.POST("/login", func(c *gin.Context) { var json Login if err := c.ShouldBindJSON(&json); err != nil { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized: payload mismatch"}) myslog.Info("Payload:", "json", &c.Request.Body) return } var user *User user, exists := userExists(json.Email, "", db) if exists || !doPasswordsMatch(*user.Password, json.Password) { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized: email or password incorrect"}) return } c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) }) router.POST("/register", func(c *gin.Context) { var json Register if err := c.ShouldBindJSON(&json); err != nil { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized: payload mismatch"}) myslog.Info("Payload:", "json", &c.Request.Body) return } c.JSON(http.StatusOK, gin.H{"status": "registration complete"}) }) // Listen and serve on 0.0.0.0:8080 router.Run(":8080") } func doPasswordsMatch(hashedPassword []byte, currPassword string) bool { err := bcrypt.CompareHashAndPassword(hashedPassword, []byte(currPassword)) return err == nil } func hashPassword(password string) ([]byte, error) { return bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) } func userExists(username string, email string, db *gorm.DB) (*User, bool) { var user = User{Username: username, Email: email} result := db.Where("username = ? OR email = ?", username, email).First(&user) if result.Error != nil { return &user, true } return nil, false }