Files
GopherGate/internal/server/users.go
T
hobokenchicken af2c5b95f7
CI / Lint (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled
feat: Phase 3 - architecture & maintainability
- Split 1474-line dashboard.go into 5 domain files (clients, providers, users, system)
- Unit tests for ModelRegistry.FindModel and CalculateCost
- go mod tidy + verify (deps clean)
- .gitignore excludes tool cache dirs (.pi-lens/, .opencode/)
2026-04-26 14:52:10 -04:00

120 lines
3.3 KiB
Go

package server
import (
"database/sql"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
"gophergate/internal/db"
"gophergate/internal/models"
"gophergate/internal/utils"
"log/slog"
"github.com/shirou/gopsutil/v3/cpu"
func (s *Server) handleGetUsers(c *gin.Context) {
var users []db.User
err := s.database.Select(&users, "SELECT id, username, display_name, role, must_change_password, created_at FROM users")
if err != nil {
c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error()))
return
}
c.JSON(http.StatusOK, SuccessResponse(users))
}
type CreateUserRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
DisplayName *string `json:"display_name"`
Role *string `json:"role"`
}
func (s *Server) handleCreateUser(c *gin.Context) {
var req CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, ErrorResponse("Invalid request"))
return
}
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 12)
if err != nil {
c.JSON(http.StatusInternalServerError, ErrorResponse("Failed to hash password"))
return
}
role := "viewer"
if req.Role != nil {
role = *req.Role
}
_, err = s.database.Exec("INSERT INTO users (username, password_hash, display_name, role, must_change_password) VALUES (?, ?, ?, ?, 1)",
req.Username, string(hash), req.DisplayName, role)
if err != nil {
c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error()))
return
}
c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "User created"}))
}
type UpdateUserRequest struct {
DisplayName *string `json:"display_name"`
Role *string `json:"role"`
Password *string `json:"password"`
MustChangePassword *bool `json:"must_change_password"`
}
func (s *Server) handleUpdateUser(c *gin.Context) {
id := c.Param("id")
var req UpdateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, ErrorResponse("Invalid request"))
return
}
if req.DisplayName != nil {
s.database.Exec("UPDATE users SET display_name = ? WHERE id = ?", req.DisplayName, id)
}
if req.Role != nil {
s.database.Exec("UPDATE users SET role = ? WHERE id = ?", req.Role, id)
}
if req.MustChangePassword != nil {
s.database.Exec("UPDATE users SET must_change_password = ? WHERE id = ?", req.MustChangePassword, id)
}
if req.Password != nil {
hash, _ := bcrypt.GenerateFromPassword([]byte(*req.Password), 12)
s.database.Exec("UPDATE users SET password_hash = ? WHERE id = ?", string(hash), id)
}
c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "User updated"}))
}
func (s *Server) handleDeleteUser(c *gin.Context) {
id := c.Param("id")
session, _ := c.Get("session")
if sess, ok := session.(*Session); ok {
var username string
s.database.Get(&username, "SELECT username FROM users WHERE id = ?", id)
if username == sess.Username {
c.JSON(http.StatusBadRequest, ErrorResponse("Cannot delete your own account"))
return
}
}
_, err := s.database.Exec("DELETE FROM users WHERE id = ?", id)
if err != nil {
c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error()))
return
}
c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "User deleted"}))
}