package useCases import ( "RenewCMS/internal/domain/article" "RenewCMS/internal/domain/audit" "RenewCMS/internal/domain/image" "RenewCMS/internal/domain/invite" "RenewCMS/internal/domain/purge" "RenewCMS/internal/domain/update" "RenewCMS/internal/domain/user" "context" "errors" "slices" ) const GenericFailureReason = "Something went wrong on our side. Please try again." type failureReason struct { cause error reason string } var failureReasons = []failureReason{ {article.ErrInvalidTitle, "The title must be between 3 and 200 characters."}, {image.ErrImageTypeNotAllowed, "The file must be a PNG, JPEG, WEBP or SVG image."}, {image.ErrImageTooLarge, "The file is too large to upload."}, {image.ErrImageUnreadable, "The image could not be read."}, {image.ErrInvalidFilename, "That file name is not valid."}, {image.ErrInvalidMergeRequest, "The merge request is not valid."}, {image.ErrMergeCopyMissing, "One of the copies no longer exists."}, {image.ErrMergeHashMismatch, "The images are not identical copies."}, {image.ErrVariantExists, "A variant with that name already exists."}, {purge.ErrPurgeConflict, "The library changed since the preview. Review it again."}, {purge.ErrPurgeBusy, "A purge is already running."}, {user.ErrRegistrationClosed, "Registration is closed on this site."}, {user.ErrVerificationEmailNotSent, "The verification email could not be sent."}, {user.ErrInvalidVerificationCode, "The verification code is not valid."}, {user.ErrVerificationCodeExpired, "The verification code has expired."}, {user.ErrInvalidDisplayName, "That display name is too long."}, {user.ErrUsernameTaken, "That username is already taken."}, {invite.ErrEmailHasAccount, "That email already has an account."}, {invite.ErrPendingInvite, "An invitation is already pending for that email."}, {invite.ErrInviteEmailNotSent, "The invitation email could not be sent."}, {invite.ErrInviteAlreadyAccepted, "That invitation was already accepted."}, {invite.ErrInviteInvalid, "That invitation is not valid."}, {invite.ErrInviteExpired, "That invitation has expired."}, {update.ErrFeedUnavailable, "The release feed could not be reached."}, {update.ErrNoRelease, "No release was found."}, {update.ErrNotPrepared, "This server cannot apply updates."}, {update.ErrJobInProgress, "An update is already being downloaded."}, {update.ErrRestartPending, "A restart is already pending."}, {update.ErrNothingToUpdate, "There is nothing to update."}, {update.ErrPreflightRefused, "The update cannot be downloaded."}, {update.ErrRestartKindMismatch, "The requested restart does not match the server state."}, } var restartFailureReasons = []failureReason{ {update.ErrJobInProgress, "An update is still being downloaded."}, } var downloadFailureReasons = map[string]string{ update.ErrorKindDownload: "The update could not be downloaded.", update.ErrorKindVerification: "The downloaded update failed its integrity check.", } var notFoundCauses = []error{ article.ErrArticleNotFound, image.ErrImageNotFound, image.ErrVariantNotFound, invite.ErrInviteNotFound, user.ErrUserNotFound, } func reasonFor(err error, overrides ...failureReason) string { for _, candidate := range slices.Concat(overrides, failureReasons) { if errors.Is(err, candidate.cause) { return candidate.reason } } return GenericFailureReason } func isNotFound(err error) bool { for _, cause := range notFoundCauses { if errors.Is(err, cause) { return true } } return false } func recordFailure(ctx context.Context, recorder audit.Recorder, event audit.Event, err error, overrides ...failureReason) { if isNotFound(err) { return } failFrom(ctx, recorder, event, reasonFor(err, overrides...)) } func failFrom(ctx context.Context, recorder audit.Recorder, event audit.Event, reason string) { event.Outcome = audit.OutcomeFailed event.FailureReason = &reason recorder.Record(ctx, event) }