add basic auth with uuid-based access tokens

This commit is contained in:
wisplite
2025-08-27 02:42:48 -05:00
parent 8263f9c50a
commit 362d61eb48
6 changed files with 123 additions and 0 deletions
+2
View File
@@ -20,6 +20,8 @@ func Init() bool {
// Run migrations // Run migrations
err = database.AutoMigrate( err = database.AutoMigrate(
&models.Album{}, &models.Album{},
&models.User{},
&models.AccessToken{},
) )
if err != nil { if err != nil {
log.Fatal("failed to migrate database: ", err) log.Fatal("failed to migrate database: ", err)
+9
View File
@@ -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"`
}
+12
View File
@@ -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
}
+32
View File
@@ -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})
})
}
+24
View File
@@ -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
}
+44
View File
@@ -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
}