af2c5b95f7
- 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/)
120 lines
3.3 KiB
Go
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"}))
|
|
}
|
|
|