diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go
index 31572a5..393d5f4 100644
--- a/internal/handlers/auth.go
+++ b/internal/handlers/auth.go
@@ -35,6 +35,7 @@ type RegisterRequest struct {
Password string `form:"password" json:"password" validate:"required,min=6"`
FirstName string `form:"first_name" json:"first_name,omitempty"`
LastName string `form:"last_name" json:"last_name,omitempty"`
+ Role string `form:"role" json:"role,omitempty"`
}
type LoginRequest struct {
@@ -69,6 +70,7 @@ func (h *AuthHandler) Register(c echo.Context) error {
password := c.FormValue("password")
firstName := c.FormValue("first_name")
lastName := c.FormValue("last_name")
+ role := c.FormValue("role")
if email == "" || username == "" || password == "" {
// Fallback to JSON binding
@@ -90,9 +92,10 @@ func (h *AuthHandler) Register(c echo.Context) error {
password = req.Password
firstName = req.FirstName
lastName = req.LastName
+ role = req.Role
}
- req := RegisterRequest{Email: email, Username: username, Password: password, FirstName: firstName, LastName: lastName}
+ req := RegisterRequest{Email: email, Username: username, Password: password, FirstName: firstName, LastName: lastName, Role: role}
if err := c.Validate(&req); err != nil {
if c.Request().Header.Get("HX-Request") == "true" {
return c.HTML(http.StatusBadRequest, `
`+err.Error()+`
`)
@@ -115,6 +118,15 @@ func (h *AuthHandler) Register(c echo.Context) error {
return c.JSON(http.StatusConflict, map[string]string{"error": "username already exists"})
}
+ // Check if this is the first user - if so, make them admin regardless of request
+ users, err := h.db.ListUsers(c.Request().Context())
+ if err != nil {
+ if c.Request().Header.Get("HX-Request") == "true" {
+ return c.HTML(http.StatusInternalServerError, `Failed to check existing users
`)
+ }
+ return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to check existing users"})
+ }
+
// Hash password
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
@@ -124,6 +136,24 @@ func (h *AuthHandler) Register(c echo.Context) error {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to hash password"})
}
+ // Set role - first user is always admin, otherwise validate requested role
+ var userRole string
+ if len(users) == 0 {
+ userRole = "admin" // First user is always admin
+ } else {
+ userRole = req.Role
+ if userRole == "" {
+ userRole = "user"
+ }
+ // Validate role for subsequent users
+ if userRole != "user" && userRole != "admin" {
+ if c.Request().Header.Get("HX-Request") == "true" {
+ return c.HTML(http.StatusBadRequest, `Invalid role. Must be 'user' or 'admin'
`)
+ }
+ return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid role. must be 'user' or 'admin'"})
+ }
+ }
+
// Create user
user, err := h.db.CreateUser(c.Request().Context(), database.CreateUserParams{
Email: req.Email,
@@ -131,6 +161,8 @@ func (h *AuthHandler) Register(c echo.Context) error {
PasswordHash: string(hashedPassword),
FirstName: pgtype.Text{String: req.FirstName, Valid: req.FirstName != ""},
LastName: pgtype.Text{String: req.LastName, Valid: req.LastName != ""},
+ Theme: pgtype.Text{String: "tokyo-night", Valid: true}, // default theme
+ Role: userRole,
})
if err != nil {
if c.Request().Header.Get("HX-Request") == "true" {
@@ -706,9 +738,29 @@ func (h *AuthHandler) DeleteAccount(c echo.Context) error {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid user id"})
}
+ // Check if this is the last user - prevent deletion
+ users, err := h.db.ListUsers(c.Request().Context())
+ if err != nil {
+ return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to check existing users"})
+ }
+
+ if len(users) == 1 {
+ // Convert user UUIDs to string for comparison
+ lastUserID := uuid.UUID(users[0].ID.Bytes).String()
+ if lastUserID == userID {
+ if c.Request().Header.Get("HX-Request") == "true" {
+ return c.HTML(http.StatusBadRequest, `Cannot delete the last user account
`)
+ }
+ return c.JSON(http.StatusBadRequest, map[string]string{"error": "cannot delete the last user account"})
+ }
+ }
+
// Delete user (this will cascade to delete all related data)
err = h.db.DeleteUser(c.Request().Context(), pgtype.UUID{Bytes: userUUID, Valid: true})
if err != nil {
+ if c.Request().Header.Get("HX-Request") == "true" {
+ return c.HTML(http.StatusInternalServerError, `Failed to delete account
`)
+ }
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}