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

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
}