using System.ComponentModel; using Azaion.Common.Extensions; namespace Azaion.Common; public class BusinessException(ExceptionEnum exEnum) : Exception(GetMessage(exEnum)) { private static readonly Dictionary ExceptionDescriptions; static BusinessException() { ExceptionDescriptions = EnumExtensions.GetDescriptions(); } public ExceptionEnum ExceptionEnum { get; set; } = exEnum; /// /// Optional cooldown hint surfaced as a Retry-After response header by the exception /// handler. Used by AccountLocked and LoginRateLimited (AZ-537). /// public int? RetryAfterSeconds { get; init; } public BusinessException(ExceptionEnum exEnum, int retryAfterSeconds) : this(exEnum) { RetryAfterSeconds = retryAfterSeconds; } public static string GetMessage(ExceptionEnum exEnum) => ExceptionDescriptions.GetValueOrDefault(exEnum) ?? exEnum.ToString(); } public enum ExceptionEnum { [Description("No such email found.")] NoEmailFound = 10, [Description("Email already exists.")] EmailExists = 20, [Description("Passwords do not match.")] WrongPassword = 30, [Description("Password should be at least 12 characters.")] PasswordLengthIncorrect = 32, [Description("Email is empty or invalid.")] EmailLengthIncorrect = 35, WrongEmail = 37, [Description("User account is disabled.")] UserDisabled = 38, [Description("Account is temporarily locked due to too many failed login attempts.")] AccountLocked = 50, [Description("Too many login attempts. Try again later.")] LoginRateLimited = 51, [Description("Refresh token is invalid, expired, or has been revoked.")] InvalidRefreshToken = 52, [Description("Session not found.")] SessionNotFound = 53, [Description("Mission token request is invalid.")] InvalidMissionRequest = 54, [Description("Aircraft not found or wrong role.")] AircraftNotFound = 55, [Description("No file provided.")] NoFileProvided = 60, }