feat: Updated admin panel styles

This commit is contained in:
domrichardson
2026-03-25 14:11:39 +00:00
parent 168f5eac83
commit 1f1fd90890
8 changed files with 700 additions and 131 deletions

View File

@@ -17,6 +17,8 @@ import (
type AdminService struct {
userRepo repositories.UserRepository
groupRepo repositories.GroupRepository
providerRepo repositories.AuthProviderRepository
linkRepo repositories.UserProviderLinkRepository
spaceRepo repositories.SpaceRepository
membershipRepo repositories.MembershipRepository
noteRepo repositories.NoteRepository
@@ -30,6 +32,8 @@ type AdminService struct {
func NewAdminService(
userRepo repositories.UserRepository,
groupRepo repositories.GroupRepository,
providerRepo repositories.AuthProviderRepository,
linkRepo repositories.UserProviderLinkRepository,
spaceRepo repositories.SpaceRepository,
membershipRepo repositories.MembershipRepository,
noteRepo repositories.NoteRepository,
@@ -41,6 +45,8 @@ func NewAdminService(
return &AdminService{
userRepo: userRepo,
groupRepo: groupRepo,
providerRepo: providerRepo,
linkRepo: linkRepo,
spaceRepo: spaceRepo,
membershipRepo: membershipRepo,
noteRepo: noteRepo,
@@ -51,6 +57,114 @@ func NewAdminService(
}
}
// DeleteUser deletes a user and related memberships/provider links.
func (s *AdminService) DeleteUser(ctx context.Context, currentUserID, targetUserID bson.ObjectID) error {
if currentUserID == targetUserID {
return errors.New("you cannot delete your own account")
}
spaces, err := s.spaceRepo.GetAllSpaces(ctx)
if err != nil {
return err
}
for _, space := range spaces {
if space.OwnerID == targetUserID {
return errors.New("cannot delete user that owns spaces; transfer or delete spaces first")
}
}
memberships, err := s.membershipRepo.GetUserMemberships(ctx, targetUserID)
if err == nil {
for _, membership := range memberships {
if err := s.membershipRepo.DeleteMembership(ctx, membership.ID); err != nil {
return err
}
}
}
if s.linkRepo != nil {
links, err := s.linkRepo.GetUserLinks(ctx, targetUserID)
if err == nil {
for _, link := range links {
if err := s.linkRepo.DeleteLink(ctx, link.ID); err != nil {
return err
}
}
}
}
return s.userRepo.DeleteUser(ctx, targetUserID)
}
// DeleteGroup deletes a non-system group and removes it from users.
func (s *AdminService) DeleteGroup(ctx context.Context, groupID bson.ObjectID) error {
group, err := s.groupRepo.GetGroupByID(ctx, groupID)
if err != nil {
return err
}
if group.IsSystem {
return errors.New("system groups cannot be deleted")
}
users, err := s.userRepo.ListAllUsers(ctx)
if err != nil {
return err
}
for _, user := range users {
filtered := make([]bson.ObjectID, 0, len(user.GroupIDs))
changed := false
for _, assignedGroupID := range user.GroupIDs {
if assignedGroupID == groupID {
changed = true
continue
}
filtered = append(filtered, assignedGroupID)
}
if !changed {
continue
}
user.GroupIDs = filtered
if err := s.userRepo.UpdateUser(ctx, user); err != nil {
return err
}
}
if err := s.groupRepo.DeleteGroup(ctx, groupID); err != nil {
return err
}
return s.refreshAllUserPermissions(ctx)
}
// DeleteProvider deletes an auth provider and all user-provider links connected to it.
func (s *AdminService) DeleteProvider(ctx context.Context, providerID bson.ObjectID) error {
if s.providerRepo == nil {
return errors.New("provider repository unavailable")
}
if s.linkRepo != nil {
users, err := s.userRepo.ListAllUsers(ctx)
if err != nil {
return err
}
for _, user := range users {
links, err := s.linkRepo.GetUserLinks(ctx, user.ID)
if err != nil {
continue
}
for _, link := range links {
if link.ProviderID == providerID {
if err := s.linkRepo.DeleteLink(ctx, link.ID); err != nil {
return err
}
}
}
}
}
return s.providerRepo.DeleteProvider(ctx, providerID)
}
// ListUsers returns all users as admin DTOs
func (s *AdminService) ListUsers(ctx context.Context) ([]*dto.AdminUserDTO, error) {
users, err := s.userRepo.ListAllUsers(ctx)

View File

@@ -5,6 +5,7 @@ import (
"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"
@@ -32,6 +33,33 @@ func (h *AdminHandler) ListUsers(w http.ResponseWriter, r *http.Request) {
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"])
@@ -66,6 +94,22 @@ func (h *AdminHandler) UpdateUserGroups(w http.ResponseWriter, r *http.Request)
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())
@@ -292,3 +336,19 @@ func (h *AdminHandler) UpdateFeatureFlags(w http.ResponseWriter, r *http.Request
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)
}