bulk actions for peers and users (#492) (#602)

This commit is contained in:
h44z
2026-01-05 23:25:17 +01:00
committed by GitHub
parent 4b49a55ea2
commit 015220dc7b
15 changed files with 1329 additions and 4 deletions

View File

@@ -800,6 +800,126 @@
}
}
},
"/peer/bulk-delete": {
"post": {
"produces": [
"application/json"
],
"tags": [
"Peer"
],
"summary": "Bulk delete selected peers.",
"operationId": "peers_handleBulkDelete",
"parameters": [
{
"description": "A list of peer identifiers to delete",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.BulkPeerRequest"
}
}
],
"responses": {
"204": {
"description": "No content if deletion was successful"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/model.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/model.Error"
}
}
}
}
},
"/peer/bulk-disable": {
"post": {
"produces": [
"application/json"
],
"tags": [
"Peer"
],
"summary": "Bulk disable selected peers.",
"operationId": "peers_handleBulkDisable",
"parameters": [
{
"description": "A list of peer identifiers to disable",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.BulkPeerRequest"
}
}
],
"responses": {
"204": {
"description": "No content if action was successful"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/model.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/model.Error"
}
}
}
}
},
"/peer/bulk-enable": {
"post": {
"produces": [
"application/json"
],
"tags": [
"Peer"
],
"summary": "Bulk enable selected peers.",
"operationId": "peers_handleBulkEnable",
"parameters": [
{
"description": "A list of peer identifiers to enable",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.BulkPeerRequest"
}
}
],
"responses": {
"204": {
"description": "No content if action was successful"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/model.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/model.Error"
}
}
}
}
},
"/peer/config-mail": {
"post": {
"produces": [
@@ -1324,6 +1444,206 @@
}
}
},
"/user/bulk-delete": {
"post": {
"produces": [
"application/json"
],
"tags": [
"Users"
],
"summary": "Bulk delete selected users.",
"operationId": "users_handleBulkDelete",
"parameters": [
{
"description": "A list of user identifiers to delete",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.BulkPeerRequest"
}
}
],
"responses": {
"204": {
"description": "No content if deletion was successful"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/model.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/model.Error"
}
}
}
}
},
"/user/bulk-disable": {
"post": {
"produces": [
"application/json"
],
"tags": [
"Users"
],
"summary": "Bulk disable selected users.",
"operationId": "users_handleBulkDisable",
"parameters": [
{
"description": "A list of user identifiers to disable",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.BulkPeerRequest"
}
}
],
"responses": {
"204": {
"description": "No content if action was successful"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/model.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/model.Error"
}
}
}
}
},
"/user/bulk-enable": {
"post": {
"produces": [
"application/json"
],
"tags": [
"Users"
],
"summary": "Bulk enable selected users.",
"operationId": "users_handleBulkEnable",
"parameters": [
{
"description": "A list of user identifiers to enable",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.BulkPeerRequest"
}
}
],
"responses": {
"204": {
"description": "No content if action was successful"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/model.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/model.Error"
}
}
}
}
},
"/user/bulk-lock": {
"post": {
"produces": [
"application/json"
],
"tags": [
"Users"
],
"summary": "Bulk lock selected users.",
"operationId": "users_handleBulkLock",
"parameters": [
{
"description": "A list of user identifiers to lock",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.BulkPeerRequest"
}
}
],
"responses": {
"204": {
"description": "No content if action was successful"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/model.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/model.Error"
}
}
}
}
},
"/user/bulk-unlock": {
"post": {
"produces": [
"application/json"
],
"tags": [
"Users"
],
"summary": "Bulk unlock selected users.",
"operationId": "users_handleBulkUnlock",
"parameters": [
{
"description": "A list of user identifiers to unlock",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/model.BulkPeerRequest"
}
}
],
"responses": {
"204": {
"description": "No content if action was successful"
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/model.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/model.Error"
}
}
}
}
},
"/user/new": {
"post": {
"produces": [
@@ -1737,6 +2057,23 @@
}
}
},
"model.BulkPeerRequest": {
"type": "object",
"required": [
"Identifiers"
],
"properties": {
"Identifiers": {
"type": "array",
"items": {
"type": "string"
}
},
"Reason": {
"type": "string"
}
}
},
"model.ConfigOption-array_string": {
"type": "object",
"properties": {

View File

@@ -16,6 +16,17 @@ definitions:
Timestamp:
type: string
type: object
model.BulkPeerRequest:
properties:
Identifiers:
items:
type: string
type: array
Reason:
type: string
required:
- Identifiers
type: object
model.ConfigOption-array_string:
properties:
Overridable:
@@ -1080,6 +1091,84 @@ paths:
summary: Update the given peer record.
tags:
- Peer
/peer/bulk-delete:
post:
operationId: peers_handleBulkDelete
parameters:
- description: A list of peer identifiers to delete
in: body
name: request
required: true
schema:
$ref: '#/definitions/model.BulkPeerRequest'
produces:
- application/json
responses:
"204":
description: No content if deletion was successful
"400":
description: Bad Request
schema:
$ref: '#/definitions/model.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/model.Error'
summary: Bulk delete selected peers.
tags:
- Peer
/peer/bulk-disable:
post:
operationId: peers_handleBulkDisable
parameters:
- description: A list of peer identifiers to disable
in: body
name: request
required: true
schema:
$ref: '#/definitions/model.BulkPeerRequest'
produces:
- application/json
responses:
"204":
description: No content if action was successful
"400":
description: Bad Request
schema:
$ref: '#/definitions/model.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/model.Error'
summary: Bulk disable selected peers.
tags:
- Peer
/peer/bulk-enable:
post:
operationId: peers_handleBulkEnable
parameters:
- description: A list of peer identifiers to enable
in: body
name: request
required: true
schema:
$ref: '#/definitions/model.BulkPeerRequest'
produces:
- application/json
responses:
"204":
description: No content if action was successful
"400":
description: Bad Request
schema:
$ref: '#/definitions/model.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/model.Error'
summary: Bulk enable selected peers.
tags:
- Peer
/peer/config-mail:
post:
operationId: peers_handleEmailPost
@@ -1571,6 +1660,136 @@ paths:
summary: Get all user records.
tags:
- Users
/user/bulk-delete:
post:
operationId: users_handleBulkDelete
parameters:
- description: A list of user identifiers to delete
in: body
name: request
required: true
schema:
$ref: '#/definitions/model.BulkPeerRequest'
produces:
- application/json
responses:
"204":
description: No content if deletion was successful
"400":
description: Bad Request
schema:
$ref: '#/definitions/model.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/model.Error'
summary: Bulk delete selected users.
tags:
- Users
/user/bulk-disable:
post:
operationId: users_handleBulkDisable
parameters:
- description: A list of user identifiers to disable
in: body
name: request
required: true
schema:
$ref: '#/definitions/model.BulkPeerRequest'
produces:
- application/json
responses:
"204":
description: No content if action was successful
"400":
description: Bad Request
schema:
$ref: '#/definitions/model.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/model.Error'
summary: Bulk disable selected users.
tags:
- Users
/user/bulk-enable:
post:
operationId: users_handleBulkEnable
parameters:
- description: A list of user identifiers to enable
in: body
name: request
required: true
schema:
$ref: '#/definitions/model.BulkPeerRequest'
produces:
- application/json
responses:
"204":
description: No content if action was successful
"400":
description: Bad Request
schema:
$ref: '#/definitions/model.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/model.Error'
summary: Bulk enable selected users.
tags:
- Users
/user/bulk-lock:
post:
operationId: users_handleBulkLock
parameters:
- description: A list of user identifiers to lock
in: body
name: request
required: true
schema:
$ref: '#/definitions/model.BulkPeerRequest'
produces:
- application/json
responses:
"204":
description: No content if action was successful
"400":
description: Bad Request
schema:
$ref: '#/definitions/model.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/model.Error'
summary: Bulk lock selected users.
tags:
- Users
/user/bulk-unlock:
post:
operationId: users_handleBulkUnlock
parameters:
- description: A list of user identifiers to unlock
in: body
name: request
required: true
schema:
$ref: '#/definitions/model.BulkPeerRequest'
produces:
- application/json
responses:
"204":
description: No content if action was successful
"400":
description: Bad Request
schema:
$ref: '#/definitions/model.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/model.Error'
summary: Bulk unlock selected users.
tags:
- Users
/user/new:
post:
operationId: users_handleCreatePost

View File

@@ -2,6 +2,7 @@ package backend
import (
"context"
"fmt"
"io"
"github.com/h44z/wg-portal/internal/config"
@@ -118,3 +119,30 @@ func (p PeerService) SendPeerEmail(
func (p PeerService) GetPeerStats(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.PeerStatus, error) {
return p.peers.GetPeerStats(ctx, id)
}
func (p PeerService) BulkDelete(ctx context.Context, ids []domain.PeerIdentifier) error {
for _, id := range ids {
if err := p.peers.DeletePeer(ctx, id); err != nil {
return fmt.Errorf("failed to delete peer %s: %w", id, err)
}
}
return nil
}
func (p PeerService) BulkUpdate(ctx context.Context, ids []domain.PeerIdentifier, updateFn func(*domain.Peer)) error {
for _, id := range ids {
peer, err := p.peers.GetPeer(ctx, id)
if err != nil {
return fmt.Errorf("failed to get peer %s: %w", id, err)
}
updateFn(peer)
if _, err := p.peers.UpdatePeer(ctx, peer); err != nil {
return fmt.Errorf("failed to update peer %s: %w", id, err)
}
}
return nil
}

View File

@@ -72,7 +72,11 @@ func (u UserService) DeactivateApi(ctx context.Context, id domain.UserIdentifier
return u.users.DeactivateApi(ctx, id)
}
func (u UserService) ChangePassword(ctx context.Context, id domain.UserIdentifier, oldPassword, newPassword string) (*domain.User, error) {
func (u UserService) ChangePassword(
ctx context.Context,
id domain.UserIdentifier,
oldPassword, newPassword string,
) (*domain.User, error) {
oldPassword = strings.TrimSpace(oldPassword)
newPassword = strings.TrimSpace(newPassword)
@@ -121,3 +125,30 @@ func (u UserService) GetUserPeerStats(ctx context.Context, id domain.UserIdentif
func (u UserService) GetUserInterfaces(ctx context.Context, id domain.UserIdentifier) ([]domain.Interface, error) {
return u.wg.GetUserInterfaces(ctx, id)
}
func (u UserService) BulkDelete(ctx context.Context, ids []domain.UserIdentifier) error {
for _, id := range ids {
if err := u.users.DeleteUser(ctx, id); err != nil {
return fmt.Errorf("failed to delete user %s: %w", id, err)
}
}
return nil
}
func (u UserService) BulkUpdate(ctx context.Context, ids []domain.UserIdentifier, updateFn func(*domain.User)) error {
for _, id := range ids {
user, err := u.users.GetUser(ctx, id)
if err != nil {
return fmt.Errorf("failed to get user %s: %w", id, err)
}
updateFn(user)
if _, err := u.users.UpdateUser(ctx, user); err != nil {
return fmt.Errorf("failed to update user %s: %w", id, err)
}
}
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"io"
"net/http"
"time"
"github.com/go-pkgz/routegroup"
@@ -41,6 +42,10 @@ type PeerService interface {
SendPeerEmail(ctx context.Context, linkOnly bool, style string, peers ...domain.PeerIdentifier) error
// GetPeerStats returns the peer stats for the given interface.
GetPeerStats(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.PeerStatus, error)
// BulkDelete deletes multiple peers.
BulkDelete(context.Context, []domain.PeerIdentifier) error
// BulkUpdate modifies multiple peers.
BulkUpdate(context.Context, []domain.PeerIdentifier, func(*domain.Peer)) error
}
type PeerEndpoint struct {
@@ -84,6 +89,9 @@ func (e PeerEndpoint) RegisterRoutes(g *routegroup.Bundle) {
apiGroup.HandleFunc("GET /{id}", e.handleSingleGet())
apiGroup.HandleFunc("PUT /{id}", e.handleUpdatePut())
apiGroup.HandleFunc("DELETE /{id}", e.handleDelete())
apiGroup.HandleFunc("POST /bulk-delete", e.handleBulkDelete())
apiGroup.HandleFunc("POST /bulk-enable", e.handleBulkEnable())
apiGroup.HandleFunc("POST /bulk-disable", e.handleBulkDisable())
}
// handleAllGet returns a gorm Handler function.
@@ -521,3 +529,114 @@ func (e PeerEndpoint) getConfigStyle(r *http.Request) string {
}
return configStyle
}
// handleBulkDelete returns a gorm Handler function.
//
// @ID peers_handleBulkDelete
// @Tags Peer
// @Summary Bulk delete selected peers.
// @Produce json
// @Param request body model.BulkPeerRequest true "A list of peer identifiers to delete"
// @Success 204 "No content if deletion was successful"
// @Failure 400 {object} model.Error
// @Failure 500 {object} model.Error
// @Router /peer/bulk-delete [post]
func (e PeerEndpoint) handleBulkDelete() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req model.BulkPeerRequest
if err := request.BodyJson(r, &req); err != nil {
respond.JSON(w, http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()})
return
}
ids := make([]domain.PeerIdentifier, len(req.Identifiers))
for i, id := range req.Identifiers {
ids[i] = domain.PeerIdentifier(id)
}
err := e.peerService.BulkDelete(r.Context(), ids)
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.Status(w, http.StatusNoContent)
}
}
// handleBulkEnable returns a gorm Handler function.
//
// @ID peers_handleBulkEnable
// @Tags Peer
// @Summary Bulk enable selected peers.
// @Produce json
// @Param request body model.BulkPeerRequest true "A list of peer identifiers to enable"
// @Success 204 "No content if action was successful"
// @Failure 400 {object} model.Error
// @Failure 500 {object} model.Error
// @Router /peer/bulk-enable [post]
func (e PeerEndpoint) handleBulkEnable() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req model.BulkPeerRequest
if err := request.BodyJson(r, &req); err != nil {
respond.JSON(w, http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()})
return
}
ids := make([]domain.PeerIdentifier, len(req.Identifiers))
for i, id := range req.Identifiers {
ids[i] = domain.PeerIdentifier(id)
}
err := e.peerService.BulkUpdate(r.Context(), ids, func(p *domain.Peer) {
p.Disabled = nil
})
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.Status(w, http.StatusNoContent)
}
}
// handleBulkDisable returns a gorm Handler function.
//
// @ID peers_handleBulkDisable
// @Tags Peer
// @Summary Bulk disable selected peers.
// @Produce json
// @Param request body model.BulkPeerRequest true "A list of peer identifiers to disable"
// @Success 204 "No content if action was successful"
// @Failure 400 {object} model.Error
// @Failure 500 {object} model.Error
// @Router /peer/bulk-disable [post]
func (e PeerEndpoint) handleBulkDisable() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req model.BulkPeerRequest
if err := request.BodyJson(r, &req); err != nil {
respond.JSON(w, http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()})
return
}
ids := make([]domain.PeerIdentifier, len(req.Identifiers))
for i, id := range req.Identifiers {
ids[i] = domain.PeerIdentifier(id)
}
now := time.Now()
err := e.peerService.BulkUpdate(r.Context(), ids, func(p *domain.Peer) {
p.Disabled = &now
p.DisabledReason = domain.DisabledReasonAdmin
})
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.Status(w, http.StatusNoContent)
}
}

View File

@@ -3,6 +3,7 @@ package handlers
import (
"context"
"net/http"
"time"
"github.com/go-pkgz/routegroup"
@@ -36,6 +37,10 @@ type UserService interface {
GetUserPeerStats(ctx context.Context, id domain.UserIdentifier) ([]domain.PeerStatus, error)
// GetUserInterfaces returns all interfaces for the given user.
GetUserInterfaces(ctx context.Context, id domain.UserIdentifier) ([]domain.Interface, error)
// BulkDelete deletes multiple users.
BulkDelete(ctx context.Context, ids []domain.UserIdentifier) error
// BulkUpdate modifies multiple users.
BulkUpdate(ctx context.Context, ids []domain.UserIdentifier, updateFn func(*domain.User)) error
}
type UserEndpoint struct {
@@ -77,7 +82,13 @@ func (e UserEndpoint) RegisterRoutes(g *routegroup.Bundle) {
apiGroup.With(e.authenticator.UserIdMatch("id")).HandleFunc("GET /{id}/interfaces", e.handleInterfacesGet())
apiGroup.With(e.authenticator.UserIdMatch("id")).HandleFunc("POST /{id}/api/enable", e.handleApiEnablePost())
apiGroup.With(e.authenticator.UserIdMatch("id")).HandleFunc("POST /{id}/api/disable", e.handleApiDisablePost())
apiGroup.With(e.authenticator.UserIdMatch("id")).HandleFunc("POST /{id}/change-password", e.handleChangePasswordPost())
apiGroup.With(e.authenticator.UserIdMatch("id")).HandleFunc("POST /{id}/change-password",
e.handleChangePasswordPost())
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("POST /bulk-delete", e.handleBulkDelete())
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("POST /bulk-enable", e.handleBulkEnable())
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("POST /bulk-disable", e.handleBulkDisable())
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("POST /bulk-lock", e.handleBulkLock())
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("POST /bulk-unlock", e.handleBulkUnlock())
}
// handleAllGet returns a gorm Handler function.
@@ -459,3 +470,190 @@ func (e UserEndpoint) handleChangePasswordPost() http.HandlerFunc {
respond.JSON(w, http.StatusOK, model.NewUser(user, false))
}
}
// handleBulkDelete returns a gorm Handler function.
//
// @ID users_handleBulkDelete
// @Tags Users
// @Summary Bulk delete selected users.
// @Produce json
// @Param request body model.BulkPeerRequest true "A list of user identifiers to delete"
// @Success 204 "No content if deletion was successful"
// @Failure 400 {object} model.Error
// @Failure 500 {object} model.Error
// @Router /user/bulk-delete [post]
func (e UserEndpoint) handleBulkDelete() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req model.BulkUserRequest
if err := request.BodyJson(r, &req); err != nil {
respond.JSON(w, http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()})
return
}
ids := make([]domain.UserIdentifier, len(req.Identifiers))
for i, id := range req.Identifiers {
ids[i] = domain.UserIdentifier(id)
}
err := e.userService.BulkDelete(r.Context(), ids)
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.Status(w, http.StatusNoContent)
}
}
// handleBulkEnable returns a gorm Handler function.
//
// @ID users_handleBulkEnable
// @Tags Users
// @Summary Bulk enable selected users.
// @Produce json
// @Param request body model.BulkPeerRequest true "A list of user identifiers to enable"
// @Success 204 "No content if action was successful"
// @Failure 400 {object} model.Error
// @Failure 500 {object} model.Error
// @Router /user/bulk-enable [post]
func (e UserEndpoint) handleBulkEnable() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req model.BulkUserRequest
if err := request.BodyJson(r, &req); err != nil {
respond.JSON(w, http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()})
return
}
ids := make([]domain.UserIdentifier, len(req.Identifiers))
for i, id := range req.Identifiers {
ids[i] = domain.UserIdentifier(id)
}
err := e.userService.BulkUpdate(r.Context(), ids, func(user *domain.User) {
user.Disabled = nil
})
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.Status(w, http.StatusNoContent)
}
}
// handleBulkDisable returns a gorm Handler function.
//
// @ID users_handleBulkDisable
// @Tags Users
// @Summary Bulk disable selected users.
// @Produce json
// @Param request body model.BulkPeerRequest true "A list of user identifiers to disable"
// @Success 204 "No content if action was successful"
// @Failure 400 {object} model.Error
// @Failure 500 {object} model.Error
// @Router /user/bulk-disable [post]
func (e UserEndpoint) handleBulkDisable() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req model.BulkUserRequest
if err := request.BodyJson(r, &req); err != nil {
respond.JSON(w, http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()})
return
}
ids := make([]domain.UserIdentifier, len(req.Identifiers))
for i, id := range req.Identifiers {
ids[i] = domain.UserIdentifier(id)
}
now := time.Now()
err := e.userService.BulkUpdate(r.Context(), ids, func(user *domain.User) {
user.Disabled = &now
user.DisabledReason = domain.DisabledReasonAdmin
})
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.Status(w, http.StatusNoContent)
}
}
// handleBulkLock returns a gorm Handler function.
//
// @ID users_handleBulkLock
// @Tags Users
// @Summary Bulk lock selected users.
// @Produce json
// @Param request body model.BulkPeerRequest true "A list of user identifiers to lock"
// @Success 204 "No content if action was successful"
// @Failure 400 {object} model.Error
// @Failure 500 {object} model.Error
// @Router /user/bulk-lock [post]
func (e UserEndpoint) handleBulkLock() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req model.BulkUserRequest
if err := request.BodyJson(r, &req); err != nil {
respond.JSON(w, http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()})
return
}
ids := make([]domain.UserIdentifier, len(req.Identifiers))
for i, id := range req.Identifiers {
ids[i] = domain.UserIdentifier(id)
}
now := time.Now()
err := e.userService.BulkUpdate(r.Context(), ids, func(user *domain.User) {
user.Locked = &now
user.LockedReason = domain.LockedReasonAdmin
})
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.Status(w, http.StatusNoContent)
}
}
// handleBulkUnlock returns a gorm Handler function.
//
// @ID users_handleBulkUnlock
// @Tags Users
// @Summary Bulk unlock selected users.
// @Produce json
// @Param request body model.BulkPeerRequest true "A list of user identifiers to unlock"
// @Success 204 "No content if action was successful"
// @Failure 400 {object} model.Error
// @Failure 500 {object} model.Error
// @Router /user/bulk-unlock [post]
func (e UserEndpoint) handleBulkUnlock() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req model.BulkUserRequest
if err := request.BodyJson(r, &req); err != nil {
respond.JSON(w, http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()})
return
}
ids := make([]domain.UserIdentifier, len(req.Identifiers))
for i, id := range req.Identifiers {
ids[i] = domain.UserIdentifier(id)
}
err := e.userService.BulkUpdate(r.Context(), ids, func(user *domain.User) {
user.Locked = nil
})
if err != nil {
respond.JSON(w, http.StatusInternalServerError,
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
return
}
respond.Status(w, http.StatusNoContent)
}
}

View File

@@ -0,0 +1,10 @@
package model
type BulkPeerRequest struct {
Identifiers []string `json:"Identifiers" binding:"required"`
Reason string `json:"Reason"`
}
type BulkUserRequest struct {
Identifiers []string `json:"Identifiers" binding:"required"`
}