You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
457 lines
12 KiB
Go
457 lines
12 KiB
Go
package valueobjects
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/knowfoolery/backend/shared/errors"
|
|
"github.com/knowfoolery/backend/shared/types"
|
|
)
|
|
|
|
type AdminActionType string
|
|
|
|
const (
|
|
AdminActionRead AdminActionType = "read"
|
|
AdminActionCreate AdminActionType = "create"
|
|
AdminActionUpdate AdminActionType = "update"
|
|
AdminActionDelete AdminActionType = "delete"
|
|
AdminActionExecute AdminActionType = "execute"
|
|
AdminActionApprove AdminActionType = "approve"
|
|
AdminActionReject AdminActionType = "reject"
|
|
AdminActionSuspend AdminActionType = "suspend"
|
|
AdminActionRestore AdminActionType = "restore"
|
|
)
|
|
|
|
type AdminActionStatus string
|
|
|
|
const (
|
|
AdminActionStatusPending AdminActionStatus = "pending"
|
|
AdminActionStatusExecuting AdminActionStatus = "executing"
|
|
AdminActionStatusCompleted AdminActionStatus = "completed"
|
|
AdminActionStatusFailed AdminActionStatus = "failed"
|
|
AdminActionStatusCancelled AdminActionStatus = "cancelled"
|
|
)
|
|
|
|
type AdminAction struct {
|
|
id types.AdminActionID
|
|
adminID types.UserID
|
|
adminName string
|
|
sessionID types.AdminSessionID
|
|
actionType AdminActionType
|
|
resourceType string
|
|
resourceID string
|
|
status AdminActionStatus
|
|
description string
|
|
parameters map[string]interface{}
|
|
results map[string]interface{}
|
|
startedAt time.Time
|
|
completedAt time.Time
|
|
duration time.Duration
|
|
errorMessage string
|
|
requiresApproval bool
|
|
approvedBy types.UserID
|
|
approvedAt time.Time
|
|
metadata map[string]interface{}
|
|
}
|
|
|
|
func NewAdminAction(
|
|
id types.AdminActionID,
|
|
adminID types.UserID,
|
|
adminName string,
|
|
sessionID types.AdminSessionID,
|
|
actionType AdminActionType,
|
|
resourceType, resourceID string,
|
|
) (*AdminAction, error) {
|
|
if id == "" {
|
|
return nil, errors.ErrValidationFailed("admin_action_id", "admin action ID cannot be empty")
|
|
}
|
|
if adminID == "" {
|
|
return nil, errors.ErrInvalidPlayerID
|
|
}
|
|
if adminName == "" {
|
|
return nil, errors.ErrValidationFailed("admin_name", "admin name cannot be empty")
|
|
}
|
|
if actionType == "" {
|
|
return nil, errors.ErrValidationFailed("action_type", "action type cannot be empty")
|
|
}
|
|
|
|
return &AdminAction{
|
|
id: id,
|
|
adminID: adminID,
|
|
adminName: adminName,
|
|
sessionID: sessionID,
|
|
actionType: actionType,
|
|
resourceType: resourceType,
|
|
resourceID: resourceID,
|
|
status: AdminActionStatusPending,
|
|
parameters: make(map[string]interface{}),
|
|
results: make(map[string]interface{}),
|
|
startedAt: time.Now(),
|
|
metadata: make(map[string]interface{}),
|
|
}, nil
|
|
}
|
|
|
|
func (aa *AdminAction) ID() types.AdminActionID {
|
|
return aa.id
|
|
}
|
|
|
|
func (aa *AdminAction) AdminID() types.UserID {
|
|
return aa.adminID
|
|
}
|
|
|
|
func (aa *AdminAction) AdminName() string {
|
|
return aa.adminName
|
|
}
|
|
|
|
func (aa *AdminAction) SessionID() types.AdminSessionID {
|
|
return aa.sessionID
|
|
}
|
|
|
|
func (aa *AdminAction) ActionType() AdminActionType {
|
|
return aa.actionType
|
|
}
|
|
|
|
func (aa *AdminAction) ResourceType() string {
|
|
return aa.resourceType
|
|
}
|
|
|
|
func (aa *AdminAction) ResourceID() string {
|
|
return aa.resourceID
|
|
}
|
|
|
|
func (aa *AdminAction) Status() AdminActionStatus {
|
|
return aa.status
|
|
}
|
|
|
|
func (aa *AdminAction) Description() string {
|
|
return aa.description
|
|
}
|
|
|
|
func (aa *AdminAction) Parameters() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
for k, v := range aa.parameters {
|
|
result[k] = v
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (aa *AdminAction) Results() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
for k, v := range aa.results {
|
|
result[k] = v
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (aa *AdminAction) StartedAt() time.Time {
|
|
return aa.startedAt
|
|
}
|
|
|
|
func (aa *AdminAction) CompletedAt() time.Time {
|
|
return aa.completedAt
|
|
}
|
|
|
|
func (aa *AdminAction) Duration() time.Duration {
|
|
return aa.duration
|
|
}
|
|
|
|
func (aa *AdminAction) ErrorMessage() string {
|
|
return aa.errorMessage
|
|
}
|
|
|
|
func (aa *AdminAction) RequiresApproval() bool {
|
|
return aa.requiresApproval
|
|
}
|
|
|
|
func (aa *AdminAction) ApprovedBy() types.UserID {
|
|
return aa.approvedBy
|
|
}
|
|
|
|
func (aa *AdminAction) ApprovedAt() time.Time {
|
|
return aa.approvedAt
|
|
}
|
|
|
|
func (aa *AdminAction) Metadata() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
for k, v := range aa.metadata {
|
|
result[k] = v
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (aa *AdminAction) SetDescription(description string) error {
|
|
if description == "" {
|
|
return errors.ErrValidationFailed("description", "description cannot be empty")
|
|
}
|
|
aa.description = description
|
|
return nil
|
|
}
|
|
|
|
func (aa *AdminAction) AddParameter(key string, value interface{}) error {
|
|
if key == "" {
|
|
return errors.ErrValidationFailed("parameter_key", "parameter key cannot be empty")
|
|
}
|
|
aa.parameters[key] = value
|
|
return nil
|
|
}
|
|
|
|
func (aa *AdminAction) RemoveParameter(key string) {
|
|
delete(aa.parameters, key)
|
|
}
|
|
|
|
func (aa *AdminAction) GetParameter(key string) (interface{}, bool) {
|
|
value, exists := aa.parameters[key]
|
|
return value, exists
|
|
}
|
|
|
|
func (aa *AdminAction) AddResult(key string, value interface{}) error {
|
|
if key == "" {
|
|
return errors.ErrValidationFailed("result_key", "result key cannot be empty")
|
|
}
|
|
aa.results[key] = value
|
|
return nil
|
|
}
|
|
|
|
func (aa *AdminAction) RemoveResult(key string) {
|
|
delete(aa.results, key)
|
|
}
|
|
|
|
func (aa *AdminAction) GetResult(key string) (interface{}, bool) {
|
|
value, exists := aa.results[key]
|
|
return value, exists
|
|
}
|
|
|
|
func (aa *AdminAction) SetRequiresApproval(requiresApproval bool) {
|
|
aa.requiresApproval = requiresApproval
|
|
}
|
|
|
|
func (aa *AdminAction) Approve(approverID types.UserID) error {
|
|
if approverID == "" {
|
|
return errors.ErrInvalidPlayerID
|
|
}
|
|
if !aa.requiresApproval {
|
|
return errors.ErrOperationNotAllowed("approve_action", "action does not require approval")
|
|
}
|
|
if aa.status != AdminActionStatusPending {
|
|
return errors.ErrOperationNotAllowed("approve_action", "action is not pending")
|
|
}
|
|
|
|
aa.approvedBy = approverID
|
|
aa.approvedAt = time.Now()
|
|
return nil
|
|
}
|
|
|
|
func (aa *AdminAction) StartExecution() error {
|
|
if aa.requiresApproval && aa.approvedBy == "" {
|
|
return errors.ErrOperationNotAllowed("start_execution", "action requires approval")
|
|
}
|
|
if aa.status != AdminActionStatusPending {
|
|
return errors.ErrOperationNotAllowed("start_execution", "action is not pending")
|
|
}
|
|
|
|
aa.status = AdminActionStatusExecuting
|
|
aa.startedAt = time.Now()
|
|
return nil
|
|
}
|
|
|
|
func (aa *AdminAction) CompleteExecution() error {
|
|
if aa.status != AdminActionStatusExecuting {
|
|
return errors.ErrOperationNotAllowed("complete_execution", "action is not executing")
|
|
}
|
|
|
|
now := time.Now()
|
|
aa.status = AdminActionStatusCompleted
|
|
aa.completedAt = now
|
|
aa.duration = now.Sub(aa.startedAt)
|
|
aa.errorMessage = ""
|
|
return nil
|
|
}
|
|
|
|
func (aa *AdminAction) FailExecution(errorMessage string) error {
|
|
if aa.status != AdminActionStatusExecuting {
|
|
return errors.ErrOperationNotAllowed("fail_execution", "action is not executing")
|
|
}
|
|
|
|
now := time.Now()
|
|
aa.status = AdminActionStatusFailed
|
|
aa.completedAt = now
|
|
aa.duration = now.Sub(aa.startedAt)
|
|
aa.errorMessage = errorMessage
|
|
return nil
|
|
}
|
|
|
|
func (aa *AdminAction) CancelExecution() error {
|
|
if aa.status != AdminActionStatusPending && aa.status != AdminActionStatusExecuting {
|
|
return errors.ErrOperationNotAllowed("cancel_execution", "action cannot be cancelled")
|
|
}
|
|
|
|
now := time.Now()
|
|
aa.status = AdminActionStatusCancelled
|
|
aa.completedAt = now
|
|
if aa.status == AdminActionStatusExecuting {
|
|
aa.duration = now.Sub(aa.startedAt)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (aa *AdminAction) SetMetadata(key string, value interface{}) error {
|
|
if key == "" {
|
|
return errors.ErrInvalidMetadata
|
|
}
|
|
aa.metadata[key] = value
|
|
return nil
|
|
}
|
|
|
|
func (aa *AdminAction) RemoveMetadata(key string) {
|
|
delete(aa.metadata, key)
|
|
}
|
|
|
|
func (aa *AdminAction) IsCompleted() bool {
|
|
return aa.status == AdminActionStatusCompleted
|
|
}
|
|
|
|
func (aa *AdminAction) IsFailed() bool {
|
|
return aa.status == AdminActionStatusFailed
|
|
}
|
|
|
|
func (aa *AdminAction) IsCancelled() bool {
|
|
return aa.status == AdminActionStatusCancelled
|
|
}
|
|
|
|
func (aa *AdminAction) IsFinished() bool {
|
|
return aa.IsCompleted() || aa.IsFailed() || aa.IsCancelled()
|
|
}
|
|
|
|
func (aa *AdminAction) IsExecuting() bool {
|
|
return aa.status == AdminActionStatusExecuting
|
|
}
|
|
|
|
func (aa *AdminAction) IsPending() bool {
|
|
return aa.status == AdminActionStatusPending
|
|
}
|
|
|
|
func (aa *AdminAction) IsApproved() bool {
|
|
return !aa.requiresApproval || aa.approvedBy != ""
|
|
}
|
|
|
|
func (aa *AdminAction) GetElapsedTime() time.Duration {
|
|
if aa.IsFinished() {
|
|
return aa.duration
|
|
}
|
|
return time.Since(aa.startedAt)
|
|
}
|
|
|
|
func (aa *AdminAction) IsHighRisk() bool {
|
|
switch aa.actionType {
|
|
case AdminActionDelete, AdminActionSuspend:
|
|
return true
|
|
default:
|
|
switch aa.resourceType {
|
|
case "admin", "system_config":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
func (aa *AdminAction) GetResourceIdentifier() string {
|
|
if aa.resourceID != "" {
|
|
return fmt.Sprintf("%s/%s", aa.resourceType, aa.resourceID)
|
|
}
|
|
return aa.resourceType
|
|
}
|
|
|
|
func (aa *AdminAction) String() string {
|
|
return fmt.Sprintf("AdminAction{ID: %s, Type: %s, Resource: %s, Status: %s, Admin: %s}",
|
|
aa.id, aa.actionType, aa.GetResourceIdentifier(), aa.status, aa.adminName)
|
|
}
|
|
|
|
type AdminActionSnapshot struct {
|
|
ID types.AdminActionID `json:"id"`
|
|
AdminID types.UserID `json:"admin_id"`
|
|
AdminName string `json:"admin_name"`
|
|
SessionID types.AdminSessionID `json:"session_id"`
|
|
ActionType AdminActionType `json:"action_type"`
|
|
ResourceType string `json:"resource_type"`
|
|
ResourceID string `json:"resource_id"`
|
|
Status AdminActionStatus `json:"status"`
|
|
Description string `json:"description"`
|
|
Parameters map[string]interface{} `json:"parameters"`
|
|
Results map[string]interface{} `json:"results"`
|
|
StartedAt time.Time `json:"started_at"`
|
|
CompletedAt time.Time `json:"completed_at"`
|
|
Duration time.Duration `json:"duration"`
|
|
ErrorMessage string `json:"error_message"`
|
|
RequiresApproval bool `json:"requires_approval"`
|
|
ApprovedBy types.UserID `json:"approved_by"`
|
|
ApprovedAt time.Time `json:"approved_at"`
|
|
Metadata map[string]interface{} `json:"metadata"`
|
|
}
|
|
|
|
func (aa *AdminAction) ToSnapshot() AdminActionSnapshot {
|
|
return AdminActionSnapshot{
|
|
ID: aa.id,
|
|
AdminID: aa.adminID,
|
|
AdminName: aa.adminName,
|
|
SessionID: aa.sessionID,
|
|
ActionType: aa.actionType,
|
|
ResourceType: aa.resourceType,
|
|
ResourceID: aa.resourceID,
|
|
Status: aa.status,
|
|
Description: aa.description,
|
|
Parameters: aa.Parameters(),
|
|
Results: aa.Results(),
|
|
StartedAt: aa.startedAt,
|
|
CompletedAt: aa.completedAt,
|
|
Duration: aa.duration,
|
|
ErrorMessage: aa.errorMessage,
|
|
RequiresApproval: aa.requiresApproval,
|
|
ApprovedBy: aa.approvedBy,
|
|
ApprovedAt: aa.approvedAt,
|
|
Metadata: aa.Metadata(),
|
|
}
|
|
}
|
|
|
|
func (aa *AdminAction) FromSnapshot(snapshot AdminActionSnapshot) error {
|
|
if snapshot.ID == "" {
|
|
return errors.ErrValidationFailed("admin_action_id", "admin action ID cannot be empty")
|
|
}
|
|
if snapshot.AdminID == "" {
|
|
return errors.ErrInvalidPlayerID
|
|
}
|
|
|
|
aa.id = snapshot.ID
|
|
aa.adminID = snapshot.AdminID
|
|
aa.adminName = snapshot.AdminName
|
|
aa.sessionID = snapshot.SessionID
|
|
aa.actionType = snapshot.ActionType
|
|
aa.resourceType = snapshot.ResourceType
|
|
aa.resourceID = snapshot.ResourceID
|
|
aa.status = snapshot.Status
|
|
aa.description = snapshot.Description
|
|
aa.startedAt = snapshot.StartedAt
|
|
aa.completedAt = snapshot.CompletedAt
|
|
aa.duration = snapshot.Duration
|
|
aa.errorMessage = snapshot.ErrorMessage
|
|
aa.requiresApproval = snapshot.RequiresApproval
|
|
aa.approvedBy = snapshot.ApprovedBy
|
|
aa.approvedAt = snapshot.ApprovedAt
|
|
|
|
aa.parameters = make(map[string]interface{})
|
|
for k, v := range snapshot.Parameters {
|
|
aa.parameters[k] = v
|
|
}
|
|
|
|
aa.results = make(map[string]interface{})
|
|
for k, v := range snapshot.Results {
|
|
aa.results[k] = v
|
|
}
|
|
|
|
aa.metadata = make(map[string]interface{})
|
|
for k, v := range snapshot.Metadata {
|
|
aa.metadata[k] = v
|
|
}
|
|
|
|
return nil
|
|
} |