Files
notely/backend/internal/interfaces/handlers/admin_handler.go
2026-03-25 14:11:39 +00:00

355 lines
10 KiB
Go

package handlers
import (
"encoding/json"
"net/http"
"github.com/gorilla/mux"
"github.com/noteapp/backend/internal/interfaces/middleware"
"go.mongodb.org/mongo-driver/v2/bson"
"github.com/noteapp/backend/internal/application/dto"
"github.com/noteapp/backend/internal/application/services"
)
// AdminHandler handles admin-level HTTP requests
type AdminHandler struct {
adminService *services.AdminService
}
// NewAdminHandler creates a new AdminHandler
func NewAdminHandler(adminService *services.AdminService) *AdminHandler {
return &AdminHandler{adminService: adminService}
}
// ListUsers handles GET /admin/users
func (h *AdminHandler) ListUsers(w http.ResponseWriter, r *http.Request) {
users, err := h.adminService.ListUsers(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{"users": users})
}
// DeleteUser handles DELETE /admin/users/{userId}
func (h *AdminHandler) DeleteUser(w http.ResponseWriter, r *http.Request) {
targetUserID, err := bson.ObjectIDFromHex(mux.Vars(r)["userId"])
if err != nil {
http.Error(w, "invalid user id", http.StatusBadRequest)
return
}
currentUserIDHex, err := middleware.GetUserIDFromContext(r.Context())
if err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
currentUserID, err := bson.ObjectIDFromHex(currentUserIDHex)
if err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
if err := h.adminService.DeleteUser(r.Context(), currentUserID, targetUserID); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusNoContent)
}
// UpdateUserGroups handles PUT /admin/users/{userId}/groups
func (h *AdminHandler) UpdateUserGroups(w http.ResponseWriter, r *http.Request) {
userID, err := bson.ObjectIDFromHex(mux.Vars(r)["userId"])
if err != nil {
http.Error(w, "invalid user id", http.StatusBadRequest)
return
}
var req dto.UpdateUserGroupsRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
groupIDs := make([]bson.ObjectID, 0, len(req.GroupIDs))
for _, groupID := range req.GroupIDs {
parsed, err := bson.ObjectIDFromHex(groupID)
if err != nil {
http.Error(w, "invalid group id", http.StatusBadRequest)
return
}
groupIDs = append(groupIDs, parsed)
}
user, err := h.adminService.UpdateUserGroups(r.Context(), userID, groupIDs)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
// DeleteGroup handles DELETE /admin/groups/{groupId}
func (h *AdminHandler) DeleteGroup(w http.ResponseWriter, r *http.Request) {
groupID, err := bson.ObjectIDFromHex(mux.Vars(r)["groupId"])
if err != nil {
http.Error(w, "invalid group id", http.StatusBadRequest)
return
}
if err := h.adminService.DeleteGroup(r.Context(), groupID); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusNoContent)
}
// ListGroups handles GET /admin/groups
func (h *AdminHandler) ListGroups(w http.ResponseWriter, r *http.Request) {
groups, err := h.adminService.ListGroups(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{"groups": groups})
}
// CreateGroup handles POST /admin/groups
func (h *AdminHandler) CreateGroup(w http.ResponseWriter, r *http.Request) {
var req dto.CreatePermissionGroupRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
group, err := h.adminService.CreateGroup(r.Context(), &req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(group)
}
// UpdateGroup handles PUT /admin/groups/{groupId}
func (h *AdminHandler) UpdateGroup(w http.ResponseWriter, r *http.Request) {
groupID, err := bson.ObjectIDFromHex(mux.Vars(r)["groupId"])
if err != nil {
http.Error(w, "invalid group id", http.StatusBadRequest)
return
}
var req dto.UpdatePermissionGroupRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
group, err := h.adminService.UpdateGroup(r.Context(), groupID, &req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(group)
}
// ListAllSpaces handles GET /admin/spaces
func (h *AdminHandler) ListAllSpaces(w http.ResponseWriter, r *http.Request) {
spaces, err := h.adminService.ListAllSpaces(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{"spaces": spaces})
}
// UpdateSpace handles PUT /admin/spaces/{spaceId}
func (h *AdminHandler) UpdateSpace(w http.ResponseWriter, r *http.Request) {
spaceID, err := bson.ObjectIDFromHex(mux.Vars(r)["spaceId"])
if err != nil {
http.Error(w, "invalid space id", http.StatusBadRequest)
return
}
var req dto.CreateSpaceRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
space, err := h.adminService.UpdateSpace(r.Context(), spaceID, &req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(space)
}
// SetSpaceVisibility handles PUT /admin/spaces/{spaceId}/visibility
func (h *AdminHandler) SetSpaceVisibility(w http.ResponseWriter, r *http.Request) {
spaceID, err := bson.ObjectIDFromHex(mux.Vars(r)["spaceId"])
if err != nil {
http.Error(w, "invalid space id", http.StatusBadRequest)
return
}
var req struct {
IsPublic bool `json:"is_public"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
if err := h.adminService.SetSpaceVisibility(r.Context(), spaceID, req.IsPublic); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": "visibility updated"})
}
// AddSpaceMember handles POST /admin/spaces/{spaceId}/members
func (h *AdminHandler) AddSpaceMember(w http.ResponseWriter, r *http.Request) {
spaceID, err := bson.ObjectIDFromHex(mux.Vars(r)["spaceId"])
if err != nil {
http.Error(w, "invalid space id", http.StatusBadRequest)
return
}
var req dto.AddSpaceMemberRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
userID, err := bson.ObjectIDFromHex(req.UserID)
if err != nil {
http.Error(w, "invalid user id", http.StatusBadRequest)
return
}
if err := h.adminService.AddSpaceMember(r.Context(), spaceID, userID); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"message": "member added"})
}
// ListSpaceMembers handles GET /admin/spaces/{spaceId}/members
func (h *AdminHandler) ListSpaceMembers(w http.ResponseWriter, r *http.Request) {
spaceID, err := bson.ObjectIDFromHex(mux.Vars(r)["spaceId"])
if err != nil {
http.Error(w, "invalid space id", http.StatusBadRequest)
return
}
members, err := h.adminService.ListSpaceMembers(r.Context(), spaceID)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{"members": members})
}
// RemoveSpaceMember handles DELETE /admin/spaces/{spaceId}/members/{userId}
func (h *AdminHandler) RemoveSpaceMember(w http.ResponseWriter, r *http.Request) {
spaceID, err := bson.ObjectIDFromHex(mux.Vars(r)["spaceId"])
if err != nil {
http.Error(w, "invalid space id", http.StatusBadRequest)
return
}
userID, err := bson.ObjectIDFromHex(mux.Vars(r)["userId"])
if err != nil {
http.Error(w, "invalid user id", http.StatusBadRequest)
return
}
if err := h.adminService.RemoveSpaceMember(r.Context(), spaceID, userID); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusNoContent)
}
// DeleteSpace handles DELETE /admin/spaces/{spaceId}
func (h *AdminHandler) DeleteSpace(w http.ResponseWriter, r *http.Request) {
spaceID, err := bson.ObjectIDFromHex(mux.Vars(r)["spaceId"])
if err != nil {
http.Error(w, "invalid space id", http.StatusBadRequest)
return
}
if err := h.adminService.DeleteSpace(r.Context(), spaceID); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusNoContent)
}
// GetFeatureFlags handles GET /admin/feature-flags
func (h *AdminHandler) GetFeatureFlags(w http.ResponseWriter, r *http.Request) {
flags, err := h.adminService.GetFeatureFlags(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(flags)
}
// UpdateFeatureFlags handles PUT /admin/feature-flags
func (h *AdminHandler) UpdateFeatureFlags(w http.ResponseWriter, r *http.Request) {
var req dto.UpdateFeatureFlagsRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
flags, err := h.adminService.UpdateFeatureFlags(r.Context(), &req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(flags)
}
// DeleteProvider handles DELETE /admin/auth/providers/{providerId}
func (h *AdminHandler) DeleteProvider(w http.ResponseWriter, r *http.Request) {
providerID, err := bson.ObjectIDFromHex(mux.Vars(r)["providerId"])
if err != nil {
http.Error(w, "invalid provider id", http.StatusBadRequest)
return
}
if err := h.adminService.DeleteProvider(r.Context(), providerID); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusNoContent)
}