diff --git a/backend/internal/db/db.go b/backend/internal/db/db.go index f20911d..a6a2db9 100644 --- a/backend/internal/db/db.go +++ b/backend/internal/db/db.go @@ -20,6 +20,8 @@ func Init() bool { // Run migrations err = database.AutoMigrate( &models.Album{}, + &models.User{}, + &models.AccessToken{}, ) if err != nil { log.Fatal("failed to migrate database: ", err) diff --git a/backend/internal/models/accessTokens.go b/backend/internal/models/accessTokens.go new file mode 100644 index 0000000..64552d6 --- /dev/null +++ b/backend/internal/models/accessTokens.go @@ -0,0 +1,9 @@ +package models + +import "time" + +type AccessToken struct { + Token string `gorm:"primaryKey"` + UserID string `gorm:"not null"` + Expires time.Time `gorm:"not null"` +} diff --git a/backend/internal/models/user.go b/backend/internal/models/user.go new file mode 100644 index 0000000..67baada --- /dev/null +++ b/backend/internal/models/user.go @@ -0,0 +1,12 @@ +package models + +import "time" + +type User struct { + ID string `gorm:"primaryKey"` + Username string `gorm:"not null"` + Password string `gorm:"not null"` + IsAdmin bool `gorm:"not null"` + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/backend/internal/routes/user.go b/backend/internal/routes/user.go new file mode 100644 index 0000000..a10b74d --- /dev/null +++ b/backend/internal/routes/user.go @@ -0,0 +1,32 @@ +package routes + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/wisplite/raster/internal/services" +) + +func RegisterUserRoutes(rg *gin.RouterGroup) { + user := rg.Group("/user") + user.POST("/createUser", func(c *gin.Context) { + username := c.PostForm("username") + password := c.PostForm("password") + err := services.CreateUser(username, password) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"message": "User created successfully"}) + }) + user.POST("/login", func(c *gin.Context) { + username := c.PostForm("username") + password := c.PostForm("password") + accessToken, err := services.Login(username, password) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"message": "Login successful", "accessToken": accessToken.Token}) + }) +} diff --git a/backend/internal/services/accessTokens.go b/backend/internal/services/accessTokens.go new file mode 100644 index 0000000..1e9c508 --- /dev/null +++ b/backend/internal/services/accessTokens.go @@ -0,0 +1,24 @@ +package services + +import ( + "time" + + "github.com/google/uuid" + "github.com/wisplite/raster/internal/db" + "github.com/wisplite/raster/internal/models" +) + +func CreateAccessToken(userID string) (models.AccessToken, error) { + token := uuid.New().String() + expires := time.Now().Add(time.Hour * 24 * 30) + accessToken := models.AccessToken{ + Token: token, + UserID: userID, + Expires: expires, + } + result := db.GetDB().Create(&accessToken) + if result.Error != nil { + return models.AccessToken{}, result.Error + } + return accessToken, nil +} diff --git a/backend/internal/services/user.go b/backend/internal/services/user.go new file mode 100644 index 0000000..153c666 --- /dev/null +++ b/backend/internal/services/user.go @@ -0,0 +1,44 @@ +package services + +import ( + "log" + + "github.com/wisplite/raster/internal/db" + "github.com/wisplite/raster/internal/models" + "golang.org/x/crypto/bcrypt" +) + +func CreateUser(username string, password string) error { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + log.Fatal("failed to hash password: ", err) + return err + } + user := models.User{ + Username: username, + Password: string(hashedPassword), + IsAdmin: false, + } + result := db.GetDB().Create(&user) + if result.Error != nil { + return result.Error + } + return nil +} + +func Login(username string, password string) (models.AccessToken, error) { + user := models.User{} + result := db.GetDB().First(&user, "username = ?", username) + if result.Error != nil { + return models.AccessToken{}, result.Error + } + err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) + if err != nil { + return models.AccessToken{}, err + } + accessToken, err := CreateAccessToken(user.ID) + if err != nil { + return models.AccessToken{}, err + } + return accessToken, nil +}