diff --git a/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs b/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs index f79650d..2a26bbf 100644 --- a/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs +++ b/Govor.API/Common/Extensions/ConfigurationProgramExtensions.cs @@ -98,7 +98,7 @@ public static class ConfigurationProgramExtensions // Auto Mapper - services.AddAutoMapper(typeof(MappingProfile)); + services.AddAutoMapper(op => { }, typeof(MappingProfile)); services.AddScoped(); @@ -111,52 +111,26 @@ public static class ConfigurationProgramExtensions services.AddScoped(); } - + public static void AddGovorDbContext(this IServiceCollection services, IConfiguration configuration) { - var useMySql = configuration.GetValue("UseMySql"); - - if (useMySql) + services.AddDbContext(options => { - services.AddDbContext(options => - { - var connectionString = configuration.GetConnectionString(nameof(GovorDbContext)); + options.UseNpgsql( + configuration.GetConnectionString(nameof(GovorDbContext)), + npgsqlOptions => + { + // retry for transient failures + npgsqlOptions.EnableRetryOnFailure( + 5, + TimeSpan.FromSeconds(5), + null); + }); - options.UseMySql( - connectionString, - new MySqlServerVersion(new Version(8, 0, 21)), - mySqlOptions => - { - mySqlOptions.EnableRetryOnFailure( - maxRetryCount: 5, - maxRetryDelay: TimeSpan.FromSeconds(5), - errorNumbersToAdd: null); - }); - - options.EnableSensitiveDataLogging(); - options.EnableDetailedErrors(); - }); - } - else - { - services.AddDbContext(options => - { - options.UseNpgsql( - configuration.GetConnectionString(nameof(GovorDbContext)), - npgsqlOptions => - { - // retry for transient failures - npgsqlOptions.EnableRetryOnFailure( - maxRetryCount: 5, - maxRetryDelay: TimeSpan.FromSeconds(5), - errorCodesToAdd: null); - }); - - //options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); - - options.EnableSensitiveDataLogging(); - options.EnableDetailedErrors(); - }); - } + //options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); + + options.EnableSensitiveDataLogging(); + options.EnableDetailedErrors(); + }); } } \ No newline at end of file diff --git a/Govor.API/Common/Extensions/ResultExtensions.cs b/Govor.API/Common/Extensions/ResultExtensions.cs new file mode 100644 index 0000000..aa5cac4 --- /dev/null +++ b/Govor.API/Common/Extensions/ResultExtensions.cs @@ -0,0 +1,60 @@ +using Govor.Domain.Common; +using Microsoft.AspNetCore.Mvc; +using SmartRes; + +namespace Govor.API.Common.Extensions; + +public static class ResultExtensions +{ + public static ActionResult ToActionResult(this Result result) + { + if (result.IsSuccess) + { + // Если тип Unit, возвращаем 204 No Content, иначе 200 OK со значением + return typeof(T) == typeof(Unit) + ? new StatusCodeResult(StatusCodes.Status204NoContent) + : new OkObjectResult(result.Value); + } + + return GenerateProblemDetails(result.Error); + } + + private static ActionResult GenerateProblemDetails(Error error) + { + var statusCode = error.Type switch + { + ErrorType.NotFound => StatusCodes.Status404NotFound, + ErrorType.Validation => StatusCodes.Status400BadRequest, + ErrorType.Conflict => StatusCodes.Status409Conflict, + ErrorType.Unauthorized => StatusCodes.Status401Unauthorized, + ErrorType.Forbidden => StatusCodes.Status403Forbidden, + _ => StatusCodes.Status400BadRequest + }; + + var problemDetails = new ProblemDetails + { + Status = statusCode, + Title = GetTitleForErrorType(error.Type), + Detail = error.Message, + }; + + problemDetails.Extensions.Add("errorCode", error.Code); + + if (error.Errors is not null) + { + problemDetails.Extensions.Add("errors", error.Errors); + } + + return new ObjectResult(problemDetails) { StatusCode = statusCode }; + } + + private static string GetTitleForErrorType(ErrorType type) => type switch + { + ErrorType.NotFound => "Not Found", + ErrorType.Validation => "Validation Error", + ErrorType.Conflict => "Conflict", + ErrorType.Unauthorized => "Unauthorized", + ErrorType.Forbidden => "Forbidden", + _ => "Bad Request" + }; +} \ No newline at end of file diff --git a/Govor.API/Controllers/Authentication/AuthController.cs b/Govor.API/Controllers/Authentication/AuthController.cs index f1c7fa8..11682f7 100644 --- a/Govor.API/Controllers/Authentication/AuthController.cs +++ b/Govor.API/Controllers/Authentication/AuthController.cs @@ -1,9 +1,10 @@ +using Govor.API.Common.Extensions; using Govor.Application.Authentication; -using Govor.Application.Authentication.Exceptions; using Govor.Application.Users.UserSessions; using Govor.Contracts.Requests; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using SmartRes; namespace Govor.API.Controllers.Authentication; @@ -30,82 +31,47 @@ public class AuthController : Controller } [HttpPost("register")] // api/auth/register - public async Task Register([FromBody] RegistrationRequest registrationRequest) + public async Task Register([FromBody] RegistrationRequest request) { - - var inviteResult = await _invitesService.ValidateAsync(registrationRequest.InviteLink); - if (!inviteResult.IsSuccess) + _logger.LogInformation("Processing registration request for: {Name}", request.Name); + + var result = await _invitesService.ValidateAsync(request.InviteLink) + .BindAsync(invite => _accountService.RegistrationAsync(request.Name, request.Password, invite)) + .TapAsync(user => _logger.LogInformation("User {Username} ({Id}) registered successfully", user.Username, user.Id)) + .BindAsync(user => _userSession.OpenSessionAsync(user, request.DeviceInfo)); + + if (result.IsFailure) { - _logger.LogWarning("Invite link invalid: {InviteLink}. Error: {Error}", registrationRequest.InviteLink, - inviteResult.Error); - return BadRequest($"Invite link invalid: {inviteResult.Error.Message}"); + _logger.LogWarning("Registration pipeline failed. Error: {Code} - {Message}", + result.Error.Code, result.Error.Message); + } + else + { + _logger.LogInformation("Session opened successfully for the request."); } - var userResult = await _accountService.RegistrationAsync( - registrationRequest.Name, - registrationRequest.Password, - inviteResult.Value); - - if (userResult.IsFailure) - { - _logger.LogWarning("Registration failed for user {Name}. Error: {Error}", registrationRequest.Name, - userResult.Error); - - return userResult.Error.Code switch - { - nameof(UserAlreadyExistException) => BadRequest($"Registration failed: {userResult.Error.Message}"), - nameof(InvalidUsernameException) => BadRequest($"Invalid username: {userResult.Error.Message}"), - _ => BadRequest($"Registration failed: {userResult.Error.Message}") - }; - } - - var user = userResult.Value; - _logger.LogInformation("Register request for {Username} with id {Id} processed successfully", user.Username, - user.Id); - - var sessionResult = await _userSession.OpenSessionAsync(user, registrationRequest.DeviceInfo); - if (sessionResult.IsFailure) - { - _logger.LogError("Failed to open session for user {Username}. Error: {Error}", user.Username, - sessionResult.Error.Message); - return StatusCode(500, "An error occurred while creating the session."); - } - - _logger.LogInformation("Session for user {Username} with id {Id} has been opened", user.Username, user.Id); - return Ok(sessionResult.Value); + return result.ToActionResult(); } - [HttpPost("login")] // api/auth/login - public async Task Login([FromBody] LoginRequest loginRequest) + public async Task Login([FromBody] LoginRequest request) { - var userResult = await _accountService.LoginAsync(loginRequest.Name, loginRequest.Password); + _logger.LogInformation("Processing registration request for: {Name}", request.Name); + + var result = await _accountService.LoginAsync(request.Name, request.Password) + .TapAsync(user => _logger.LogInformation("User {Username} ({Id}) logged in.", user.Username, user.Id)) + .BindAsync(user => _userSession.OpenSessionAsync(user, request.DeviceInfo)); - if (userResult.IsFailure) + if (result.IsFailure) { - _logger.LogWarning("Login failed for user {Name}. Error: {Code}", loginRequest.Name, userResult.Error); - - return userResult.Error.Code switch - { - nameof(UserNotRegisteredException) => BadRequest("Login failed: user does not exist."), - nameof(InvalidOperationException) => BadRequest("Login failed: username or password is incorrect."), - _ => BadRequest($"Login failed: {userResult.Error.Message}") - }; + _logger.LogWarning("Login pipeline failed. Error: {Code} - {Message}", + result.Error.Code, result.Error.Message); + } + else + { + _logger.LogInformation("Session opened successfully for the request."); } - var user = userResult.Value; - _logger.LogInformation("Login request for {Username} with id {Id} processed successfully", user.Username, user.Id); - - var sessionResult = await _userSession.OpenSessionAsync(user, loginRequest.DeviceInfo); - - if (sessionResult.IsFailure) - { - _logger.LogError("Failed to open session for user {Username}. Error: {Error}", user.Username, sessionResult.Error); - return StatusCode(500, "An error occurred while creating the session."); - } - - _logger.LogInformation("Session for user {Username} with id {Id} has been opened", user.Username, user.Id); - - return Ok(sessionResult.Value); + return result.ToActionResult(); } } \ No newline at end of file diff --git a/Govor.API/Controllers/Friends/FriendshipController.cs b/Govor.API/Controllers/Friends/FriendshipController.cs index d375717..afbc129 100644 --- a/Govor.API/Controllers/Friends/FriendshipController.cs +++ b/Govor.API/Controllers/Friends/FriendshipController.cs @@ -44,7 +44,7 @@ public class FriendshipController : Controller { _logger.LogWarning(ex, ex.Message); return Forbid(ex.Message); - } + } catch (Exception ex) { _logger.LogError(ex, ex.Message); diff --git a/Govor.API/Govor.API.csproj b/Govor.API/Govor.API.csproj index c109986..1d39339 100644 --- a/Govor.API/Govor.API.csproj +++ b/Govor.API/Govor.API.csproj @@ -1,27 +1,33 @@ - net8.0 disable enable + net10.0 - - - - - - - - + + + + + + + + + + + + - - - + + + + ..\libs\SmartRes.dll + diff --git a/Govor.API/Program.cs b/Govor.API/Program.cs index 1ee5ad4..c84e91b 100644 --- a/Govor.API/Program.cs +++ b/Govor.API/Program.cs @@ -7,7 +7,7 @@ using Govor.Application.Authentication.JWT; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; var builder = WebApplication.CreateBuilder(args); @@ -86,28 +86,34 @@ builder.Services.AddGovorDbContext(configuration); // GovorDbContext init builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(options => +services.AddSwaggerGen(options => { + const string schemeId = "Bearer"; + options.SwaggerDoc("v1", new OpenApiInfo { Title = "Govor API", Version = "v1" }); - options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + options.AddSecurityDefinition(schemeId, new OpenApiSecurityScheme { - Description = "JWT Authorization header using the Bearer scheme. Example: 'Bearer {token}'", - Name = "Authorization", - In = ParameterLocation.Header, Type = SecuritySchemeType.Http, - Scheme = "bearer" + In = ParameterLocation.Header, + Scheme = "bearer", + BearerFormat = "JWT", + Description = "JWT Authorization header using the Bearer scheme. Example: 'Bearer {token}'" }); - options.AddSecurityRequirement(new OpenApiSecurityRequirement + options.AddSecurityRequirement(document => { + var requirement = new OpenApiSecurityRequirement { - new OpenApiSecurityScheme { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } - }, - Array.Empty() - } + new OpenApiSecuritySchemeReference(schemeId) + { + Reference = new OpenApiReferenceWithDescription { Type = ReferenceType.SecurityScheme, Id = "Bearer" } + }, + [] + } + }; + return requirement; }); }); @@ -121,7 +127,7 @@ if (!app.Environment.IsDevelopment()) { //app.MapOpenApi(); builder.WebHost.UseUrls("http://0.0.0.0:8080"); - builder.WebHost.UseUrls("http://10.8.0.5:5000"); + //builder.WebHost.UseUrls("http://10.8.0.5:5000"); //builder.WebHost.UseUrls("http://192.168.1.107:8080"); } diff --git a/Govor.API/Properties/launchSettings.json b/Govor.API/Properties/launchSettings.json index 83eb278..bdcb24a 100644 --- a/Govor.API/Properties/launchSettings.json +++ b/Govor.API/Properties/launchSettings.json @@ -5,7 +5,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": false, - "applicationUrl": "http://0.0.0.0:8080;http://localhost:7155;http://10.8.0.5:5000", + "applicationUrl": "http://0.0.0.0:8080;http://localhost:7155", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/Govor.Application/Authentication/AuthService.cs b/Govor.Application/Authentication/AuthService.cs index 2f9df25..a5b6b88 100644 --- a/Govor.Application/Authentication/AuthService.cs +++ b/Govor.Application/Authentication/AuthService.cs @@ -6,6 +6,7 @@ using Govor.Domain.Common; using Govor.Domain.Models; using Govor.Domain.Models.Users; using Microsoft.EntityFrameworkCore; +using SmartRes; namespace Govor.Application.Authentication; @@ -28,18 +29,18 @@ public class AuthService : IAccountService _usernameValidator = usernameValidator; } - public async Task> RegistrationAsync(string name, string password, Invitation invitation) + public async Task> RegistrationAsync(string name, string password, Invitation invitation) { var validationResult = _usernameValidator.Validate(name); if (validationResult.IsFailure) { - return Result.Failure(validationResult.Error); + return Result.Failure(validationResult.Error); } if (await _userNameExistValidator.IsUsernameExistsAsync(name)) { - return Result.Failure(new Error( + return Result.Failure(Error.Conflict( nameof(UserAlreadyExistException), $"User with username '{name}' already exists.")); } @@ -63,12 +64,14 @@ public class AuthService : IAccountService await SetRoleAsync(user, invitation); + // TODO: inv.participantCount -= 1; db.save(); + await _context.SaveChangesAsync(); return user; // Success } - public async Task> LoginAsync(string name, string password) + public async Task> LoginAsync(string name, string password) { var user = await _context.Users .AsNoTracking() @@ -76,14 +79,14 @@ public class AuthService : IAccountService if (user is null) { - return Result.Failure(new Error( + return Result.Failure(Error.NotFound( nameof(UserNotRegisteredException), $"User '{name}' is not registered.")); } if (!_passwordHasher.Verify(password, user.PasswordHash)) { - return Result.Failure(new Error( + return Result.Failure(Error.Failure( nameof(InvalidOperationException), "The password provided is incorrect.")); } diff --git a/Govor.Application/Authentication/IAuthService.cs b/Govor.Application/Authentication/IAuthService.cs index 48816d1..1f508c4 100644 --- a/Govor.Application/Authentication/IAuthService.cs +++ b/Govor.Application/Authentication/IAuthService.cs @@ -1,11 +1,12 @@ using Govor.Domain.Common; using Govor.Domain.Models; using Govor.Domain.Models.Users; +using SmartRes; namespace Govor.Application.Authentication; public interface IAccountService { - public Task> RegistrationAsync(string name, string password, Invitation invitation); - public Task> LoginAsync(string name, string password); + public Task> RegistrationAsync(string name, string password, Invitation invitation); + public Task> LoginAsync(string name, string password); } \ No newline at end of file diff --git a/Govor.Application/Authentication/IInvitesService.cs b/Govor.Application/Authentication/IInvitesService.cs index 18e030a..1c8ecf3 100644 --- a/Govor.Application/Authentication/IInvitesService.cs +++ b/Govor.Application/Authentication/IInvitesService.cs @@ -1,6 +1,7 @@ using Govor.Domain.Common; using Govor.Domain.Models; using Govor.Domain.Models.Users; +using SmartRes; namespace Govor.Application.Authentication; @@ -8,5 +9,5 @@ public interface IInvitesService { public Task GetRoleNameAsync(User user); public Task GetRoleNameAsync(Guid sessionId); - public Task> ValidateAsync(string inviteCode); + public Task> ValidateAsync(string inviteCode); } \ No newline at end of file diff --git a/Govor.Application/Authentication/InvitesService.cs b/Govor.Application/Authentication/InvitesService.cs index 1d2a70c..c4b4d60 100644 --- a/Govor.Application/Authentication/InvitesService.cs +++ b/Govor.Application/Authentication/InvitesService.cs @@ -4,6 +4,7 @@ using Govor.Domain.Common; using Govor.Domain.Models; using Govor.Domain.Models.Users; using Microsoft.EntityFrameworkCore; +using SmartRes; namespace Govor.Application.Authentication; @@ -31,22 +32,24 @@ public class InvitesService : IInvitesService return invitation.IsAdmin ? "Admin" : "User"; } - public async Task> ValidateAsync(string inviteCode) + public async Task> ValidateAsync(string inviteCode) { var invite = await _context.Invitations .Include(s => s.Users) .FirstOrDefaultAsync(s => s.Code == inviteCode); if (invite == null) - return Result.Failure(Error.Null); + return Result.Failure(Error.NotFound("Auth.LinkNotFount","Invitation not found.")); if (invite.EndDate < DateTime.Now || invite.MaxParticipants <= invite.Users.Count) { invite.IsActive = false; await _context.SaveChangesAsync(); - return Result.Failure(new Error( - "Auth.InviteLinkInvalid", $"Invite link invalid: {inviteCode}") + return Result.Failure( + Error.Failure( + "Auth.InviteLinkInvalid", $"Invite link invalid: {inviteCode}" + ) ); } diff --git a/Govor.Application/Friends/FriendRequestCommandService.cs b/Govor.Application/Friends/FriendRequestCommandService.cs index 6656510..66e349e 100644 --- a/Govor.Application/Friends/FriendRequestCommandService.cs +++ b/Govor.Application/Friends/FriendRequestCommandService.cs @@ -5,6 +5,7 @@ using Govor.Domain; using Govor.Domain.Common; using Govor.Domain.Models; using Microsoft.EntityFrameworkCore; +using SmartRes; namespace Govor.Application.Friends; @@ -21,10 +22,10 @@ public class FriendRequestCommandService : IFriendRequestCommandService _privateChatsCreator = privateChatsCreator; } - public async Task> SendAsync(Guid fromUserId, Guid toUserId) + public async Task> SendAsync(Guid fromUserId, Guid toUserId) { if (fromUserId == toUserId) - return Result.Failure(new Error( + return Result.Failure(Error.Failure( "Friendship.Send", "Cannot send a request to self user") ); @@ -50,7 +51,7 @@ public class FriendRequestCommandService : IFriendRequestCommandService friendship.Status == FriendshipStatus.Accepted || friendship.Status == FriendshipStatus.Blocked) { - return Result.Failure(new Error( + return Result.Failure(Error.Conflict( "Friendship.Send", $"The request is already {friendship.Status}") ); @@ -65,24 +66,24 @@ public class FriendRequestCommandService : IFriendRequestCommandService return friendship; } - public async Task> AcceptAsync(Guid requestId, Guid currentUserId) + public async Task> AcceptAsync(Guid requestId, Guid currentUserId) { var friendship = await _context.Friendships.FindAsync(requestId); if (friendship is null) - return Result.Failure(new Error( + return Result.Failure(Error.NotFound( "Friendship.Accept", "Friendship not found! You cant accept request!") ); if (friendship.AddresseeId != currentUserId) - return Result.Failure(new Error( + return Result.Failure(Error.Forbidden( "Friendship.Accept", "You cannot accept this request!") ); if (friendship.Status != FriendshipStatus.Pending) - return Result.Failure(new Error( + return Result.Failure(Error.Forbidden( "Friendship.Accept", "Request is already accepted!") ); @@ -96,24 +97,24 @@ public class FriendRequestCommandService : IFriendRequestCommandService return friendship; } - public async Task> RejectAsync(Guid requestId, Guid currentUserId) + public async Task> RejectAsync(Guid requestId, Guid currentUserId) { var friendship = await _context.Friendships.FindAsync(requestId); if (friendship == null) - return Result.Failure(new Error( + return Result.Failure(Error.NotFound( "Friendship.Reject", "Friendship not found! You cant reject request!") ); if (friendship.AddresseeId != currentUserId) - return Result.Failure(new Error( + return Result.Failure(Error.Forbidden( "Friendship.Reject", "You cannot reject this request!") ); if (friendship.Status != FriendshipStatus.Pending && friendship.Status != FriendshipStatus.Rejected) - return Result.Failure(new Error( + return Result.Failure(Error.Conflict( "Friendship.Reject", $"Request is already {friendship.Status}") ); diff --git a/Govor.Application/Friends/IFriendRequestCommandService.cs b/Govor.Application/Friends/IFriendRequestCommandService.cs index 1ed7d01..7473eae 100644 --- a/Govor.Application/Friends/IFriendRequestCommandService.cs +++ b/Govor.Application/Friends/IFriendRequestCommandService.cs @@ -1,11 +1,12 @@ using Govor.Domain.Common; using Govor.Domain.Models; +using SmartRes; namespace Govor.Application.Friends; public interface IFriendRequestCommandService { - Task> SendAsync(Guid fromUserId, Guid toUserId); - Task> AcceptAsync(Guid requestId, Guid currentUserId); - Task> RejectAsync(Guid requestId, Guid currentUserId); + Task> SendAsync(Guid fromUserId, Guid toUserId); + Task> AcceptAsync(Guid requestId, Guid currentUserId); + Task> RejectAsync(Guid requestId, Guid currentUserId); } diff --git a/Govor.Application/Govor.Application.csproj b/Govor.Application/Govor.Application.csproj index 3fc6ad7..78d9a3a 100644 --- a/Govor.Application/Govor.Application.csproj +++ b/Govor.Application/Govor.Application.csproj @@ -1,23 +1,31 @@  - net8.0 + net10.0 enable enable - + - - - - - - - - + + + + + + + + + + + + + + ..\libs\SmartRes.dll + + diff --git a/Govor.Application/Groups/IGroupService.cs b/Govor.Application/Groups/IGroupService.cs index 830b079..075c365 100644 --- a/Govor.Application/Groups/IGroupService.cs +++ b/Govor.Application/Groups/IGroupService.cs @@ -1,6 +1,7 @@ using Govor.Domain.Common; using Govor.Domain.Models; using Govor.Domain.Models.Users; +using SmartRes; namespace Govor.Application.Groups; @@ -8,9 +9,9 @@ public interface IGroupService { Task GetGroupByIdAsync(Guid groupId); Task CreateGroupAsync(string name, Guid creatorId, IEnumerable initialMemberIds); - Task AddUserToGroupByInvitationAsync(Guid userId, string invitationCode); - Task RemoveUserFromGroupAsync(Guid groupId, Guid userId, Guid removedByUserId); - Task DeleteGroupAsync(Guid groupId, Guid userId); + Task> AddUserToGroupByInvitationAsync(Guid userId, string invitationCode); + Task> RemoveUserFromGroupAsync(Guid groupId, Guid userId, Guid removedByUserId); + Task> DeleteGroupAsync(Guid groupId, Guid userId); Task> GetGroupMembersAsync(Guid groupId); Task>GetUserGroupsAsync(Guid userId); ChatGroup GetGroupByInviteCode(string code); diff --git a/Govor.Application/Infrastructure/AdminsStuff/IInvitationGetter.cs b/Govor.Application/Infrastructure/AdminsStuff/IInvitationGetter.cs index e34c12d..7713287 100644 --- a/Govor.Application/Infrastructure/AdminsStuff/IInvitationGetter.cs +++ b/Govor.Application/Infrastructure/AdminsStuff/IInvitationGetter.cs @@ -1,10 +1,11 @@ using Govor.Domain.Common; using Govor.Domain.Models; +using SmartRes; namespace Govor.Application.Infrastructure.AdminsStuff; public interface IInvitationGetter { Task> GetAllAsync(); - Task> FindByIdAsync(Guid id); + Task> FindByIdAsync(Guid id); } \ No newline at end of file diff --git a/Govor.Application/Infrastructure/AdminsStuff/InvitationGetter.cs b/Govor.Application/Infrastructure/AdminsStuff/InvitationGetter.cs index 961eaa1..4ca7c89 100644 --- a/Govor.Application/Infrastructure/AdminsStuff/InvitationGetter.cs +++ b/Govor.Application/Infrastructure/AdminsStuff/InvitationGetter.cs @@ -3,6 +3,7 @@ using Govor.Domain.Common; using Govor.Domain.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using SmartRes; namespace Govor.Application.Infrastructure.AdminsStuff; @@ -25,13 +26,13 @@ public class InvitationGetter : IInvitationGetter .ToListAsync(); } - public async Task> FindByIdAsync(Guid id) + public async Task> FindByIdAsync(Guid id) { var res = await _context.Invitations.AsNoTracking() .FirstOrDefaultAsync(iv => iv.Id == id); if (res is null) - return Result.Failure(new Error( + return Result.Failure(Error.NotFound( nameof(InvalidOperationException), "Invitation not found.") ); diff --git a/Govor.Application/Infrastructure/Validators/IUsernameValidator.cs b/Govor.Application/Infrastructure/Validators/IUsernameValidator.cs index 46106c0..8d78d49 100644 --- a/Govor.Application/Infrastructure/Validators/IUsernameValidator.cs +++ b/Govor.Application/Infrastructure/Validators/IUsernameValidator.cs @@ -1,9 +1,10 @@ using Govor.Domain.Common; +using SmartRes; namespace Govor.Application.Infrastructure.Validators; public interface IUsernameValidator { - Result Validate(string username); + Result Validate(string username); bool TryValidate(string username); } \ No newline at end of file diff --git a/Govor.Application/Infrastructure/Validators/UsernameValidator.cs b/Govor.Application/Infrastructure/Validators/UsernameValidator.cs index 4e9c531..269beee 100644 --- a/Govor.Application/Infrastructure/Validators/UsernameValidator.cs +++ b/Govor.Application/Infrastructure/Validators/UsernameValidator.cs @@ -3,6 +3,7 @@ using Govor.Application.Authentication.Exceptions; using Govor.Domain.Common.Constants; using Govor.Domain.Common; using Microsoft.Extensions.Configuration; +using SmartRes; namespace Govor.Application.Infrastructure.Validators; @@ -39,52 +40,58 @@ public class UsernameValidator : IUsernameValidator ?? throw new InvalidOperationException("Reserved not set"); } - public Result Validate(string username) + public Result Validate(string username) { if (username.Length < UserConstants.MIN_LENGHT_OF_NAME || username.Length > UserConstants.MAX_LENGHT_OF_NAME) { - return new Error( + return Result.Failure(Error.Validation( ErrorCode, - $"Username must be between {UserConstants.MIN_LENGHT_OF_NAME} and {UserConstants.MAX_LENGHT_OF_NAME} characters."); + $"Username must be between {UserConstants.MIN_LENGHT_OF_NAME} and {UserConstants.MAX_LENGHT_OF_NAME} characters.") + ); } if (!_usernameRegex.IsMatch(username)) { - return new Error( + return Result.Failure(Error.Validation( ErrorCode, - "The username must be in Cyrillic and start with a letter."); + "The username must be in Cyrillic and start with a letter.") + ); } if (Regex.IsMatch(username, @"(.)\1{4,}")) { - return new Error( + return Result.Failure(Error.Validation( ErrorCode, - "Too many repeating characters."); + "Too many repeating characters.") + ); } var normalized = Normalize(username); if (_reserved.Contains(normalized)) { - return new Error( + return Result.Failure(Error.Validation( ErrorCode, - "This username is reserved."); + "This username is reserved.") + ); } if (_blockedExact.Contains(normalized)) { - return new Error( + return Result.Failure(Error.Validation( ErrorCode, - "This username is not allowed."); + "This username is not allowed.") + ); } foreach (var banned in _blockedContains) { if (normalized.Contains(banned)) { - return new Error( + return Result.Failure(Error.Validation( ErrorCode, - "Username contains prohibited content."); + "Username contains prohibited content.") + ); } } diff --git a/Govor.Application/PrivateUserChats/IUserPrivateChatsGetterService.cs b/Govor.Application/PrivateUserChats/IUserPrivateChatsGetterService.cs index 412427e..a404ad5 100644 --- a/Govor.Application/PrivateUserChats/IUserPrivateChatsGetterService.cs +++ b/Govor.Application/PrivateUserChats/IUserPrivateChatsGetterService.cs @@ -1,11 +1,12 @@ using Govor.Domain.Common; using Govor.Domain.Models; +using SmartRes; namespace Govor.Application.PrivateUserChats; public interface IUserPrivateChatsGetterService { Task> GetUserChatsAsync(Guid userId); - Task> GetPrivateChatAsync(Guid chatId); + Task> GetPrivateChatAsync(Guid chatId); Task ExistChatAsync(Guid userIdA, Guid userIdB); } \ No newline at end of file diff --git a/Govor.Application/PrivateUserChats/UserPrivateChatsGetter.cs b/Govor.Application/PrivateUserChats/UserPrivateChatsGetter.cs index 9773a2e..e38690a 100644 --- a/Govor.Application/PrivateUserChats/UserPrivateChatsGetter.cs +++ b/Govor.Application/PrivateUserChats/UserPrivateChatsGetter.cs @@ -2,6 +2,7 @@ using Govor.Domain; using Govor.Domain.Common; using Govor.Domain.Models; using Microsoft.EntityFrameworkCore; +using SmartRes; namespace Govor.Application.PrivateUserChats; @@ -22,14 +23,16 @@ public class UserPrivateChatsGetter : IUserPrivateChatsGetterService .ToListAsync(); } - public async Task> GetPrivateChatAsync(Guid chatId) + public async Task> GetPrivateChatAsync(Guid chatId) { var res = await _context.PrivateChats.AsNoTracking() .FirstOrDefaultAsync(p => p.Id == chatId); if (res == null) - return Result.Failure(new Error(nameof(InvalidOperationException), - "PrivateChat not found.") + return Result.Failure( + Error.Failure( + nameof(InvalidOperationException), + "PrivateChat not found.") ); return res; diff --git a/Govor.Application/Profiles/IProfileService.cs b/Govor.Application/Profiles/IProfileService.cs index 16f4a45..c9a2956 100644 --- a/Govor.Application/Profiles/IProfileService.cs +++ b/Govor.Application/Profiles/IProfileService.cs @@ -1,10 +1,11 @@ using Govor.Domain.Common; +using SmartRes; namespace Govor.Application.Profiles; public interface IProfileService { - public Task> GetUserProfileAsync(Guid userId); - public Task SetDescription(string description, Guid userId); - public Task SetNewIcon(Guid userId, Guid iconId); + public Task> GetUserProfileAsync(Guid userId); + public Task> SetDescription(string description, Guid userId); + public Task> SetNewIcon(Guid userId, Guid iconId); } diff --git a/Govor.Application/Profiles/ProfileService.cs b/Govor.Application/Profiles/ProfileService.cs index b8f7a02..da575af 100644 --- a/Govor.Application/Profiles/ProfileService.cs +++ b/Govor.Application/Profiles/ProfileService.cs @@ -2,6 +2,7 @@ using Govor.Domain.Common; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using SmartRes; namespace Govor.Application.Profiles; @@ -16,7 +17,7 @@ public class ProfileService : IProfileService _logger = logger; } - public async Task> GetUserProfileAsync(Guid userId) + public async Task> GetUserProfileAsync(Guid userId) { _logger.LogInformation("Getting user {UserId} profile", userId); @@ -34,13 +35,13 @@ public class ProfileService : IProfileService if (profile is null) { - return Result.Failure(CreateNotFoundError(userId)); + return Result.Failure(CreateNotFoundError(userId)); } return profile; } - public async Task SetDescription(string description, Guid userId) + public async Task> SetDescription(string description, Guid userId) { _logger.LogInformation("Updating description for user {UserId}", userId); @@ -56,7 +57,7 @@ public class ProfileService : IProfileService return Result.Success(); } - public async Task SetNewIcon(Guid userId, Guid iconId) + public async Task> SetNewIcon(Guid userId, Guid iconId) { _logger.LogInformation("Updating icon for user {UserId}", userId); @@ -74,5 +75,5 @@ public class ProfileService : IProfileService } private static Error CreateNotFoundError(Guid userId) => - new("Profile.UserNotFound", $"User with ID {userId} was not found."); + Error.NotFound("Profile.UserNotFound", $"User with ID {userId} was not found."); } diff --git a/Govor.Application/PushNotifications/IPushNotificationService.cs b/Govor.Application/PushNotifications/IPushNotificationService.cs index 39c1a39..d81a4d8 100644 --- a/Govor.Application/PushNotifications/IPushNotificationService.cs +++ b/Govor.Application/PushNotifications/IPushNotificationService.cs @@ -1,12 +1,13 @@ using Govor.Domain.Common; +using SmartRes; namespace Govor.Application.PushNotifications; public interface IPushNotificationService { - Task SendToUserAsync(Guid userId, string title, string body, string channelId, string tag = "", Dictionary? data = null); + Task> SendToUserAsync(Guid userId, string title, string body, string channelId, string tag = "", Dictionary? data = null); - Task SendToUsersAsync(IEnumerable userIds, string title, string body, string channelId, string tag = "", Dictionary? data = null); + Task> SendToUsersAsync(IEnumerable userIds, string title, string body, string channelId, string tag = "", Dictionary? data = null); - Task SendToSessionAsync(Guid sessionId, string title, string body, string channelId, string tag = "", Dictionary? data = null); + Task> SendToSessionAsync(Guid sessionId, string title, string body, string channelId, string tag = "", Dictionary? data = null); } \ No newline at end of file diff --git a/Govor.Application/PushNotifications/IPushTokenService.cs b/Govor.Application/PushNotifications/IPushTokenService.cs index 57bdca4..70cdf2d 100644 --- a/Govor.Application/PushNotifications/IPushTokenService.cs +++ b/Govor.Application/PushNotifications/IPushTokenService.cs @@ -1,14 +1,15 @@ using Govor.Domain.Common; +using SmartRes; namespace Govor.Application.PushNotifications; public interface IPushTokenService { - Task DeactivateTokenBySessionAsync(Guid sessionId); - Task DeactivateAllTokensByUserIdAsync(Guid userId); - Task>> GetStringsActiveTokensAsync(Guid userId); - Task>> GetUsersStringsActiveTokensAsync(IEnumerable userIds); - Task> GetActiveTokenBySessionAsync(Guid sessionId); - Task RemoveTokensAsync(IEnumerable tokens); - Task AddOrUpdateTokenAsync(Guid userId, Guid sessionId, string token, string platform); + Task> DeactivateTokenBySessionAsync(Guid sessionId); + Task> DeactivateAllTokensByUserIdAsync(Guid userId); + Task, Error>> GetStringsActiveTokensAsync(Guid userId); + Task, Error>> GetUsersStringsActiveTokensAsync(IEnumerable userIds); + Task> GetActiveTokenBySessionAsync(Guid sessionId); + Task> RemoveTokensAsync(IEnumerable tokens); + Task> AddOrUpdateTokenAsync(Guid userId, Guid sessionId, string token, string platform); } \ No newline at end of file diff --git a/Govor.Application/PushNotifications/PushNotificationService.cs b/Govor.Application/PushNotifications/PushNotificationService.cs index 2d7dd61..0f6121c 100644 --- a/Govor.Application/PushNotifications/PushNotificationService.cs +++ b/Govor.Application/PushNotifications/PushNotificationService.cs @@ -2,6 +2,7 @@ using Govor.Application.Interfaces.PushNotifications.Models; using Govor.Application.PushNotifications.Providers; using Govor.Domain.Common; using Microsoft.Extensions.Logging; +using SmartRes; namespace Govor.Application.PushNotifications; @@ -21,7 +22,7 @@ public class PushNotificationService : IPushNotificationService _logger = logger; } - public async Task SendToUserAsync(Guid userId, string title, string body, string channelId, string tag = "", Dictionary? data = null) + public async Task> SendToUserAsync(Guid userId, string title, string body, string channelId, string tag = "", Dictionary? data = null) { var resultTokens = await _tokenService.GetStringsActiveTokensAsync(userId); if (resultTokens.IsFailure) @@ -37,10 +38,10 @@ public class PushNotificationService : IPushNotificationService return await SendMulticastInternalAsync(tokens, title, body, channelId, tag, data, userId.ToString()); } - public async Task SendToUsersAsync(IEnumerable userIds, string title, string body, string channelId, string tag = "", Dictionary? data = null) + public async Task> SendToUsersAsync(IEnumerable userIds, string title, string body, string channelId, string tag = "", Dictionary? data = null) { if (userIds == null || !userIds.Any()) - return Result.Failure(new Error("Push.InvalidArgs", "User IDs collection cannot be empty.")); + return Result.Failure(Error.Failure("Push.InvalidArgs", "User IDs collection cannot be empty.")); var tokensResult = await _tokenService.GetUsersStringsActiveTokensAsync(userIds); if (tokensResult.IsFailure) @@ -53,7 +54,7 @@ public class PushNotificationService : IPushNotificationService return await SendMulticastInternalAsync(tokens, title, body, channelId, tag, data, "bulk_request"); } - public async Task SendToSessionAsync(Guid sessionId, string title, string body, string channelId, string tag = "", Dictionary? data = null) + public async Task> SendToSessionAsync(Guid sessionId, string title, string body, string channelId, string tag = "", Dictionary? data = null) { var tokenResult = await _tokenService.GetActiveTokenBySessionAsync(sessionId); if (tokenResult.IsFailure) @@ -83,7 +84,7 @@ public class PushNotificationService : IPushNotificationService } } - private async Task SendMulticastInternalAsync(List tokens, string title, string body, string channelId, string tag, Dictionary? data, string targetInfo) + private async Task> SendMulticastInternalAsync(List tokens, string title, string body, string channelId, string tag, Dictionary? data, string targetInfo) { try { diff --git a/Govor.Application/PushNotifications/PushTokenService.cs b/Govor.Application/PushNotifications/PushTokenService.cs index 76f1ad2..11b1dea 100644 --- a/Govor.Application/PushNotifications/PushTokenService.cs +++ b/Govor.Application/PushNotifications/PushTokenService.cs @@ -3,6 +3,7 @@ using Govor.Domain.Common; using Govor.Domain.Models.Users; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using SmartRes; namespace Govor.Application.PushNotifications; @@ -17,7 +18,7 @@ public class PushTokenService : IPushTokenService _logger = logger; } - public async Task DeactivateTokenBySessionAsync(Guid sessionId) + public async Task> DeactivateTokenBySessionAsync(Guid sessionId) { try { @@ -34,7 +35,7 @@ public class PushTokenService : IPushTokenService } } - public async Task DeactivateAllTokensByUserIdAsync(Guid userId) + public async Task> DeactivateAllTokensByUserIdAsync(Guid userId) { try { @@ -51,7 +52,7 @@ public class PushTokenService : IPushTokenService } } - public async Task>> GetStringsActiveTokensAsync(Guid userId) + public async Task, Error>> GetStringsActiveTokensAsync(Guid userId) { try { @@ -66,11 +67,11 @@ public class PushTokenService : IPushTokenService catch (Exception ex) { _logger.LogError(ex, "Failed to fetch active push tokens for user {UserId}", userId); - return Result>.Failure(ex); + return Result.Failure>(ex); } } - public async Task>> GetUsersStringsActiveTokensAsync(IEnumerable userIds) + public async Task, Error>> GetUsersStringsActiveTokensAsync(IEnumerable userIds) { try { @@ -85,11 +86,11 @@ public class PushTokenService : IPushTokenService catch (Exception ex) { _logger.LogError(ex, "Failed to fetch active push tokens for bulk users"); - return Result>.Failure(ex); + return Result.Failure>(ex); } } - public async Task> GetActiveTokenBySessionAsync(Guid sessionId) + public async Task> GetActiveTokenBySessionAsync(Guid sessionId) { try { @@ -104,11 +105,11 @@ public class PushTokenService : IPushTokenService catch (Exception ex) { _logger.LogError(ex, "Failed to fetch active push token for session {SessionId}", sessionId); - return Result.Failure(ex); + return Result.Failure(ex); } } - public async Task RemoveTokensAsync(IEnumerable tokens) + public async Task> RemoveTokensAsync(IEnumerable tokens) { if (tokens is null || !tokens.Any()) return Result.Success(); @@ -128,11 +129,11 @@ public class PushTokenService : IPushTokenService } } - public async Task AddOrUpdateTokenAsync(Guid userId, Guid sessionId, string token, string platform) + public async Task> AddOrUpdateTokenAsync(Guid userId, Guid sessionId, string token, string platform) { if (string.IsNullOrWhiteSpace(token)) { - return new Error("PushToken.Empty", "Push token cannot be empty."); + return Result.Failure(Error.Failure("PushToken.Empty", "Push token cannot be empty.")); } var existingToken = await _context.UserPushTokens diff --git a/Govor.Application/Users/UserSessions/IUserSessionOpener.cs b/Govor.Application/Users/UserSessions/IUserSessionOpener.cs index 4f95a7a..3738ce6 100644 --- a/Govor.Application/Users/UserSessions/IUserSessionOpener.cs +++ b/Govor.Application/Users/UserSessions/IUserSessionOpener.cs @@ -1,11 +1,12 @@ using Govor.Domain.Common; using Govor.Domain.Models.Users; +using SmartRes; namespace Govor.Application.Users.UserSessions; public interface IUserSessionOpener { - Task> OpenSessionAsync(User user, string deviceInfo); + Task> OpenSessionAsync(User user, string deviceInfo); } diff --git a/Govor.Application/Users/UserSessions/IUserSessionReader.cs b/Govor.Application/Users/UserSessions/IUserSessionReader.cs index 49432c5..d52680a 100644 --- a/Govor.Application/Users/UserSessions/IUserSessionReader.cs +++ b/Govor.Application/Users/UserSessions/IUserSessionReader.cs @@ -1,8 +1,9 @@ using Govor.Domain.Common; +using SmartRes; namespace Govor.Application.Users.UserSessions; public interface IUserSessionReader { - Task>> GetAllSessionsAsync(Guid userId); + Task, Error>> GetAllSessionsAsync(Guid userId); } \ No newline at end of file diff --git a/Govor.Application/Users/UserSessions/IUserSessionRefresher.cs b/Govor.Application/Users/UserSessions/IUserSessionRefresher.cs index 602e79e..b2b6ca4 100644 --- a/Govor.Application/Users/UserSessions/IUserSessionRefresher.cs +++ b/Govor.Application/Users/UserSessions/IUserSessionRefresher.cs @@ -1,8 +1,9 @@ using Govor.Domain.Common; +using SmartRes; namespace Govor.Application.Users.UserSessions; public interface IUserSessionRefresher { - Task> RefreshTokenAsync(string refreshToken); + Task> RefreshTokenAsync(string refreshToken); } \ No newline at end of file diff --git a/Govor.Application/Users/UserSessions/IUserSessionRevoker.cs b/Govor.Application/Users/UserSessions/IUserSessionRevoker.cs index 0369cef..6fb1f99 100644 --- a/Govor.Application/Users/UserSessions/IUserSessionRevoker.cs +++ b/Govor.Application/Users/UserSessions/IUserSessionRevoker.cs @@ -1,10 +1,11 @@ using Govor.Domain.Common; +using SmartRes; namespace Govor.Application.Users.UserSessions; public interface IUserSessionRevoker { - Task CloseSessionByIdAsync(Guid sessionId, Guid userId); - Task CloseAllSessionsAsync(Guid userId); + Task> CloseSessionByIdAsync(Guid sessionId, Guid userId); + Task> CloseAllSessionsAsync(Guid userId); } diff --git a/Govor.Application/Users/UserSessions/UserSessionOpener.cs b/Govor.Application/Users/UserSessions/UserSessionOpener.cs index 4788505..8646cf8 100644 --- a/Govor.Application/Users/UserSessions/UserSessionOpener.cs +++ b/Govor.Application/Users/UserSessions/UserSessionOpener.cs @@ -1,9 +1,10 @@ using Govor.Application.Authentication.JWT; using Govor.Domain; -using Govor.Domain.Common; // Путь к вашему Result и Error +using Govor.Domain.Common; using Govor.Domain.Models.Users; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using SmartRes; namespace Govor.Application.Users.UserSessions; @@ -32,7 +33,7 @@ public class UserSessionOpener : IUserSessionOpener _jwtService = jwtService; } - public async Task> OpenSessionAsync(User user, string deviceInfo) + public async Task> OpenSessionAsync(User user, string deviceInfo) { _logger.LogInformation("Opening session for user {UserId} on device '{DeviceInfo}'", user.Id, deviceInfo); @@ -41,7 +42,7 @@ public class UserSessionOpener : IUserSessionOpener if (result.IsFailure) { _logger.LogError("Failed to fetch sessions for user {UserId}: {Error}", user.Id, result.Error.Message); - return Result.Failure(result.Error); + return Result.Failure(result.Error); } var sessions = result.Value; @@ -67,7 +68,7 @@ public class UserSessionOpener : IUserSessionOpener catch (Exception ex) { _logger.LogError(ex, "Database error while opening session for user {UserId}", user.Id); - return Result.Failure(ex); + return Result.Failure(ex); } } diff --git a/Govor.Application/Users/UserSessions/UserSessionReader.cs b/Govor.Application/Users/UserSessions/UserSessionReader.cs index fdbcb68..92de90f 100644 --- a/Govor.Application/Users/UserSessions/UserSessionReader.cs +++ b/Govor.Application/Users/UserSessions/UserSessionReader.cs @@ -3,6 +3,7 @@ using Govor.Domain.Common; using Govor.Domain.Models.Users; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using SmartRes; namespace Govor.Application.Users.UserSessions; @@ -17,13 +18,14 @@ public class UserSessionReader : IUserSessionReader _logger = logger; } - public async Task>> GetAllSessionsAsync(Guid userId) + public async Task, Error>> GetAllSessionsAsync(Guid userId) { if (userId == Guid.Empty) { - return Result>.Failure(new Error( - "UserSession.InvalidUserId", - "Provided User ID cannot be empty.")); + return Result.Failure>(Error.Conflict("UserSession.InvalidUserId", + "Provided User ID cannot be empty.") + ); + } _logger.LogInformation("Getting all active sessions for user {UserId}", userId); @@ -40,7 +42,7 @@ public class UserSessionReader : IUserSessionReader catch (Exception ex) { _logger.LogError(ex, "Failed to fetch user sessions for user {UserId}", userId); - return Result>.Failure(ex); + return Result.Failure>(ex); } } } \ No newline at end of file diff --git a/Govor.Application/Users/UserSessions/UserSessionRefresher.cs b/Govor.Application/Users/UserSessions/UserSessionRefresher.cs index 1486d16..6c7467b 100644 --- a/Govor.Application/Users/UserSessions/UserSessionRefresher.cs +++ b/Govor.Application/Users/UserSessions/UserSessionRefresher.cs @@ -4,6 +4,7 @@ using Govor.Domain.Common; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using SmartRes; namespace Govor.Application.Users.UserSessions; @@ -29,11 +30,11 @@ public class UserSessionRefresher : IUserSessionRefresher _context = context; } - public async Task> RefreshTokenAsync(string refreshToken) + public async Task> RefreshTokenAsync(string refreshToken) { if (string.IsNullOrWhiteSpace(refreshToken)) { - return Result.Failure(new Error("Auth.EmptyToken", "Refresh token cannot be empty.")); + return Result.Failure(Error.Failure("Auth.EmptyToken", "Refresh token cannot be empty.")); } var hashedToken = _jwtTokenHasher.HashToken(refreshToken); @@ -48,13 +49,13 @@ public class UserSessionRefresher : IUserSessionRefresher if (session is null) { _logger.LogWarning("Refresh token session not found for hashed token"); - return Result.Failure(new Error("Auth.InvalidToken", "Invalid refresh token.")); + return Result.Failure(Error.Failure("Auth.InvalidToken", "Invalid refresh token.")); } if (session.IsRevoked || session.ExpiresAt <= DateTime.UtcNow) { _logger.LogWarning("Attempted to refresh an expired or revoked session: {SessionId}", session.Id); - return Result.Failure(new Error("Auth.InvalidToken", "Refresh token is invalid or expired.")); + return Result.Failure(Error.Failure("Auth.InvalidToken", "Refresh token is invalid or expired.")); } var newAccessToken = await _jwtService.GenerateAccessTokenAsync(session.User, session.Id); @@ -74,7 +75,7 @@ public class UserSessionRefresher : IUserSessionRefresher catch (Exception ex) { _logger.LogError(ex, "Database error occurred during token refresh execution"); - return Result.Failure(ex); + return Result.Failure(ex); } } } diff --git a/Govor.Application/Users/UserSessions/UserSessionRevoker.cs b/Govor.Application/Users/UserSessions/UserSessionRevoker.cs index 4135449..87a6a8a 100644 --- a/Govor.Application/Users/UserSessions/UserSessionRevoker.cs +++ b/Govor.Application/Users/UserSessions/UserSessionRevoker.cs @@ -3,6 +3,7 @@ using Govor.Domain; using Govor.Domain.Common; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using SmartRes; namespace Govor.Application.Users.UserSessions; @@ -22,7 +23,7 @@ public class UserSessionRevoker : IUserSessionRevoker _logger = logger; } - public async Task CloseSessionByIdAsync(Guid sessionId, Guid userId) + public async Task> CloseSessionByIdAsync(Guid sessionId, Guid userId) { _logger.LogInformation("Attempting to close session {SessionId} for user {UserId}", sessionId, userId); @@ -34,9 +35,13 @@ public class UserSessionRevoker : IUserSessionRevoker if (session is null) { _logger.LogWarning("Active session {SessionId} not found or doesn't belong to user {UserId}", sessionId, userId); - return Result.Failure(new Error( - "UserSession.NotFoundOrUnauthorized", - $"Active session {sessionId} for user {userId} was not found.")); + + return Result.Failure( + Error.NotFound( + "UserSession.NotFoundOrUnauthorized", + $"Active session {sessionId} for user {userId} was not found." + ) + ); } session.IsRevoked = true; @@ -55,7 +60,7 @@ public class UserSessionRevoker : IUserSessionRevoker } } - public async Task CloseAllSessionsAsync(Guid userId) + public async Task> CloseAllSessionsAsync(Guid userId) { _logger.LogInformation("Attempting to close all active sessions for user {UserId}", userId); diff --git a/Govor.ConsoleClient.Tests/Govor.ConsoleClient.Tests.csproj b/Govor.ConsoleClient.Tests/Govor.ConsoleClient.Tests.csproj index 8dc0b55..382cce0 100644 --- a/Govor.ConsoleClient.Tests/Govor.ConsoleClient.Tests.csproj +++ b/Govor.ConsoleClient.Tests/Govor.ConsoleClient.Tests.csproj @@ -25,8 +25,4 @@ - - - - diff --git a/Govor.ConsoleClient/App.cs b/Govor.ConsoleClient/App.cs deleted file mode 100644 index 8b2a59a..0000000 --- a/Govor.ConsoleClient/App.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Govor.ConsoleClient.Services; -using Govor.ConsoleClient.Services.Interfaces; - -namespace Govor.ConsoleClient; - -public class App -{ - private readonly IInputPipeline _inputPipeline; - private readonly ILogger _logger; - - public App(IInputPipeline inputPipeline, ILogger logger) - { - _logger = logger; - _inputPipeline = inputPipeline; - } - - public async Task RunAsync() - { - _logger.Title("Добро пожаловать в консольный клиент Говор!"); - while (true) - { - Console.Write(">> "); - var input = Console.ReadLine(); - if (input == null || input.Trim().ToLower() == "exit") break; - await _inputPipeline.ProcessInputAsync(input); - } - } -} diff --git a/Govor.ConsoleClient/Commands/HelpCommand.cs b/Govor.ConsoleClient/Commands/HelpCommand.cs deleted file mode 100644 index f0fa612..0000000 --- a/Govor.ConsoleClient/Commands/HelpCommand.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Reflection; -using Govor.ConsoleClient.Services; -using Govor.ConsoleClient.Services.Interfaces; -using Microsoft.Extensions.DependencyInjection; - -namespace Govor.ConsoleClient.Commands; - -public class HelpCommand : ICommand -{ - private readonly IServiceProvider _serviceProvider; - private readonly ILogger _logger; - - public HelpCommand(IServiceProvider serviceProvider, ILogger logger) - { - _serviceProvider = serviceProvider; - _logger = logger; - } - - public Task ExecuteAsync(CommandContext context) - { - var dispatcher = _serviceProvider.GetRequiredService(); - var commands = dispatcher.GetAllCommands(); - - if (context.Arguments is not null) - { - var command = commands.FirstOrDefault(c => - (c.GetType().GetCustomAttribute()?.Path.Replace("/", "").ToLower() - ?? c.GetType().Name.Replace("Command", "").ToLower()) == context.Arguments.ToLower()); - - if (command != null) - { - _logger.Info($"{context.Arguments} - {command.LongHelp()}"); - } - else - { - _logger.Warn("Unknown command"); - } - } - else - { - _logger.Info("Чтобы получить подробную информацию, напишите /help {command}"); - foreach (var command in commands) - { - var name = command.GetType().GetCustomAttribute()?.Path.Replace("/", "").ToLower() - ?? command.GetType().Name.Replace("Command", "").ToLower(); - - _logger.Log($"{name} - {command.ShortHelp()}"); - } - } - return Task.CompletedTask; - } - - public string LongHelp() => "Необходима для получения информации о доступных командах\nЧтобы получить подробную информацию о команде, напишите /help {command}"; - - public string ShortHelp() => "Необходима для получения информации о доступных командах"; -} diff --git a/Govor.ConsoleClient/Commands/ICommand.cs b/Govor.ConsoleClient/Commands/ICommand.cs deleted file mode 100644 index aa1c844..0000000 --- a/Govor.ConsoleClient/Commands/ICommand.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Govor.ConsoleClient.Services; - -namespace Govor.ConsoleClient.Commands; - -public interface ICommand -{ - Task ExecuteAsync(CommandContext context); - string LongHelp(); - string ShortHelp(); -} - -[AttributeUsage(AttributeTargets.Class)] -public class CommandRouteAttribute : Attribute -{ - public string Path { get; } - public CommandRouteAttribute(string path) => Path = path; -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Commands/IInteractiveCommand.cs b/Govor.ConsoleClient/Commands/IInteractiveCommand.cs deleted file mode 100644 index c13154e..0000000 --- a/Govor.ConsoleClient/Commands/IInteractiveCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Govor.ConsoleClient.Commands; - -public interface IInteractiveCommand : ICommand -{ - Task HandleInputAsync(string input); - bool IsCompleted { get; } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Commands/SendMessageCommand.cs b/Govor.ConsoleClient/Commands/SendMessageCommand.cs deleted file mode 100644 index 0c59601..0000000 --- a/Govor.ConsoleClient/Commands/SendMessageCommand.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Govor.ConsoleClient.Services; - -namespace Govor.ConsoleClient.Commands; - -[CommandRoute("/send")] -public class SendMessageCommand : IInteractiveCommand -{ - private string? _recipient; - private bool _isCompleted; - public bool IsCompleted => _isCompleted; - - public Task ExecuteAsync(CommandContext context) - { - Console.WriteLine("Кому вы хотите отправить сообщение?"); - return Task.CompletedTask; - } - - public async Task HandleInputAsync(string input) - { - if (_recipient == null) - { - _recipient = input; - Console.WriteLine("Введите сообщение:"); - } - else - { - var message = input; - Console.WriteLine($"(Отправка '{message}' пользователю '{_recipient}')"); - _isCompleted = true; - } - - await Task.CompletedTask; - } - - public string LongHelp() - { - return "Отпарвка тестовых сообщений существующему юзеру 2"; - } - - public string ShortHelp() - { - return "Отпарвка тестовых сообщений существующему юзеру"; - } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/DependencyInjection.cs b/Govor.ConsoleClient/DependencyInjection.cs deleted file mode 100644 index 15f06f5..0000000 --- a/Govor.ConsoleClient/DependencyInjection.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Reflection; -using Govor.ConsoleClient.Commands; -using Govor.ConsoleClient.Services; -using Govor.ConsoleClient.Services.Extensions; -using Govor.ConsoleClient.Services.Interfaces; -using Govor.ConsoleClient.Services.Middleware; -using Microsoft.Extensions.DependencyInjection; - -namespace Govor.ConsoleClient; - -public static class DependencyInjection -{ - public static IServiceProvider Configure() - { - var services = new ServiceCollection(); - - // Регистрация команд - services.AddCommands(); - - // Сервисы - services.AddApplicationServices(); - - services.AddSingleton(); - - // Middleware - services.AddSingleton(); - - - return services.BuildServiceProvider(); - } - - public static IServiceCollection AddCommands(this IServiceCollection services) - { - var commandTypes = Assembly.GetExecutingAssembly() - .GetTypes() - .Where(t => typeof(ICommand).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); - - foreach (var type in commandTypes) - { - services.AddTransient(typeof(ICommand), type); - services.AddTransient(type); // Для прямого внедрения - } - - return services; - } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Govor.ConsoleClient.csproj b/Govor.ConsoleClient/Govor.ConsoleClient.csproj deleted file mode 100644 index 1686633..0000000 --- a/Govor.ConsoleClient/Govor.ConsoleClient.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - Exe - net8.0 - 12 - enable - enable - - - - - - - - - - - - - - - - - - - - - diff --git a/Govor.ConsoleClient/Program.cs b/Govor.ConsoleClient/Program.cs deleted file mode 100644 index 491b499..0000000 --- a/Govor.ConsoleClient/Program.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace Govor.ConsoleClient; - -internal class Program -{ - //private const string HubBaseUrl = "https://govor-team-govor-88b3.twc1.net/hubs"; - - static async Task Main() - { - Console.Title = "Govor Console Client"; - - var serviceProvider = DependencyInjection.Configure(); - - var app = ActivatorUtilities.CreateInstance(serviceProvider); - await app.RunAsync(); - } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/CommandContext.cs b/Govor.ConsoleClient/Services/CommandContext.cs deleted file mode 100644 index d3af695..0000000 --- a/Govor.ConsoleClient/Services/CommandContext.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Govor.ConsoleClient.Commands; - -namespace Govor.ConsoleClient.Services; - -public class CommandContext -{ - public string Route { get; } - public string? Arguments { get; } - public ICommand Command { get; } - - public CommandContext(string route, string? arguments, ICommand command) - { - Route = route; - Arguments = arguments; - Command = command; - } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Extensions/ServiceCollectionExtensions.cs b/Govor.ConsoleClient/Services/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index a095ff0..0000000 --- a/Govor.ConsoleClient/Services/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Govor.ConsoleClient.Services.Implementations; -using Govor.ConsoleClient.Services.Interfaces; -using Microsoft.Extensions.DependencyInjection; - -namespace Govor.ConsoleClient.Services.Extensions; - -public static class ServiceCollectionExtensions -{ - public static IServiceCollection AddApplicationServices(this IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(sp => sp.GetRequiredService()); - return services; - } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Implementations/CommandDispatcher.cs b/Govor.ConsoleClient/Services/Implementations/CommandDispatcher.cs deleted file mode 100644 index 52b440d..0000000 --- a/Govor.ConsoleClient/Services/Implementations/CommandDispatcher.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Reflection; -using Govor.ConsoleClient.Commands; -using Govor.ConsoleClient.Services.Interfaces; - -namespace Govor.ConsoleClient.Services.Implementations; - -public class CommandDispatcher : ICommandDispatcher -{ - private readonly Dictionary _commands = new(); - private readonly ILogger _logger; - private readonly IMiddlewarePipeline _pipeline; - - public CommandDispatcher(IEnumerable commands, ILogger logger, IMiddlewarePipeline pipeline) - { - _logger = logger; - _pipeline = pipeline; - - foreach (var command in commands) - { - var route = command.GetType().GetCustomAttribute()?.Path.Replace("/","") - ?? command.GetType().Name.Replace("Command", "").ToLower(); - _commands[route.ToLower()] = command; - } - } - - public async Task DispatchAsync(string input) - { - var args = input.Split(' ', 2); - var cmd = args[0].ToLower(); - if (_commands.TryGetValue(cmd, out var command)) - { - var context = new CommandContext(cmd, args.Length > 1 ? args[1] : null, command); - await _pipeline.ExecuteAsync(context); - return command; - } - else - { - _logger.Warn("Неизвестная команда. Введите '/help'."); - return null; - } - } - - public IEnumerable GetAllCommands() => _commands.Values; -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Implementations/ConsoleLogger.cs b/Govor.ConsoleClient/Services/Implementations/ConsoleLogger.cs deleted file mode 100644 index 86d22c3..0000000 --- a/Govor.ConsoleClient/Services/Implementations/ConsoleLogger.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Govor.ConsoleClient.Services.Interfaces; - -namespace Govor.ConsoleClient.Services.Implementations; - -public class ConsoleLogger : ILogger -{ - public void Log(string message) - { - Console.ResetColor(); - Console.WriteLine(message); - } - - public void Info(string message) - { - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"[INFO] {message}"); - Console.ResetColor(); - } - - public void Warn(string message) - { - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine($"[WARN] {message}"); - Console.ResetColor(); - } - - public void Error(string message) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine($"[ERROR] {message}"); - Console.ResetColor(); - } - - public void Title(string message) - { - var upper = message.ToUpper(); - var length = upper.Length + 6; - var border = new string('=', length); - var padded = $"= {upper} ="; - - Console.ForegroundColor = ConsoleColor.Cyan; - Console.WriteLine(border); - Console.WriteLine(padded); - Console.WriteLine(border); - Console.ResetColor(); - } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Implementations/InputPipeline.cs b/Govor.ConsoleClient/Services/Implementations/InputPipeline.cs deleted file mode 100644 index 996fbe6..0000000 --- a/Govor.ConsoleClient/Services/Implementations/InputPipeline.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Govor.ConsoleClient.Commands; -using Govor.ConsoleClient.Services.Interfaces; - -namespace Govor.ConsoleClient.Services.Implementations; - -public class InputPipeline : IInputPipeline -{ - private readonly ICommandDispatcher _dispatcher; - private readonly ILogger _logger; - private IInteractiveCommand? _activeCommand; - - public InputPipeline(ICommandDispatcher dispatcher, ILogger logger) - { - _dispatcher = dispatcher; - _logger = logger; - } - - public async Task ProcessInputAsync(string input) - { - if (string.IsNullOrWhiteSpace(input)) return; - - if (input.StartsWith("/")) - { - _activeCommand = null; - - var commandInput = input[1..]; - var result = await _dispatcher.DispatchAsync(commandInput); - - // If the command supports interactivity, save it as active - if (result is IInteractiveCommand interactiveCommand && !interactiveCommand.IsCompleted) - { - _activeCommand = interactiveCommand; - } - } - else - { - if (_activeCommand != null) - { - await _activeCommand.HandleInputAsync(input); - - if (_activeCommand.IsCompleted) - _activeCommand = null; - } - else - { - _logger.Info($"Введите /help, чтобы узнать доступные команды!"); - } - } - } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Implementations/MiddlewarePipeline.cs b/Govor.ConsoleClient/Services/Implementations/MiddlewarePipeline.cs deleted file mode 100644 index 4cc7da1..0000000 --- a/Govor.ConsoleClient/Services/Implementations/MiddlewarePipeline.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Govor.ConsoleClient.Services.Interfaces; -using Govor.ConsoleClient.Services.Middleware; - -namespace Govor.ConsoleClient.Services.Implementations; - -public delegate Task CommandMiddleware(CommandContext context, Func next); - -public class MiddlewarePipeline : IMiddlewarePipeline -{ - private readonly IList _middlewares; - - public MiddlewarePipeline(IEnumerable middlewares) - { - _middlewares = middlewares.ToList(); - } - - public Task ExecuteAsync(CommandContext context) - { - return InvokeNext(0, context); - } - - private Task InvokeNext(int index, CommandContext context) - { - if (index < _middlewares.Count) - { - return _middlewares[index].InvokeAsync(context, () => InvokeNext(index + 1, context)); - } - return context.Command.ExecuteAsync(context); - } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Interfaces/ICommandDispatcher.cs b/Govor.ConsoleClient/Services/Interfaces/ICommandDispatcher.cs deleted file mode 100644 index 370adab..0000000 --- a/Govor.ConsoleClient/Services/Interfaces/ICommandDispatcher.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Govor.ConsoleClient.Commands; - -namespace Govor.ConsoleClient.Services.Interfaces; - -public interface ICommandDispatcher -{ - Task DispatchAsync(string input); - IEnumerable GetAllCommands(); -} diff --git a/Govor.ConsoleClient/Services/Interfaces/IInputPipeline.cs b/Govor.ConsoleClient/Services/Interfaces/IInputPipeline.cs deleted file mode 100644 index be8fd92..0000000 --- a/Govor.ConsoleClient/Services/Interfaces/IInputPipeline.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Govor.ConsoleClient.Services.Interfaces; - -public interface IInputPipeline -{ - Task ProcessInputAsync(string input); -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Interfaces/ILogger.cs b/Govor.ConsoleClient/Services/Interfaces/ILogger.cs deleted file mode 100644 index 15136b0..0000000 --- a/Govor.ConsoleClient/Services/Interfaces/ILogger.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Govor.ConsoleClient.Services.Interfaces; - -public interface ILogger -{ - void Log(string message); - void Info(string message); - void Warn(string message); - void Error(string message); - void Title(string title); -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Interfaces/IMiddlewarePipeline.cs b/Govor.ConsoleClient/Services/Interfaces/IMiddlewarePipeline.cs deleted file mode 100644 index b33e96a..0000000 --- a/Govor.ConsoleClient/Services/Interfaces/IMiddlewarePipeline.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Govor.ConsoleClient.Services.Interfaces; - -public interface IMiddlewarePipeline -{ - Task ExecuteAsync(CommandContext context); -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Middleware/ExceptionHandlingMiddleware.cs b/Govor.ConsoleClient/Services/Middleware/ExceptionHandlingMiddleware.cs deleted file mode 100644 index b105e77..0000000 --- a/Govor.ConsoleClient/Services/Middleware/ExceptionHandlingMiddleware.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Govor.ConsoleClient.Services.Interfaces; - -namespace Govor.ConsoleClient.Services.Middleware; - -public class ExceptionHandlingMiddleware : ICommandMiddleware -{ - private readonly ILogger _logger; - - public ExceptionHandlingMiddleware(ILogger logger) - { - _logger = logger; - } - - public async Task InvokeAsync(CommandContext context, Func next) - { - try - { - await next(); - } - catch (Exception ex) - { - _logger.Error($"Произошла ошибка при выполнении команды '{context?.Route}': {ex.Message}"); - } - } -} \ No newline at end of file diff --git a/Govor.ConsoleClient/Services/Middleware/ICommandMiddleware.cs b/Govor.ConsoleClient/Services/Middleware/ICommandMiddleware.cs deleted file mode 100644 index e441906..0000000 --- a/Govor.ConsoleClient/Services/Middleware/ICommandMiddleware.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Govor.ConsoleClient.Services.Middleware; - -public interface ICommandMiddleware -{ - Task InvokeAsync(CommandContext context, Func next); -} \ No newline at end of file diff --git a/Govor.ConsoleClient/appsettings.json b/Govor.ConsoleClient/appsettings.json deleted file mode 100644 index 3cc9e3f..0000000 --- a/Govor.ConsoleClient/appsettings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "BaseUrl": "https://govor-team-govor-88b3.twc1.net" -} \ No newline at end of file diff --git a/Govor.Contracts/Govor.Contracts.csproj b/Govor.Contracts/Govor.Contracts.csproj index ed3cb48..bbc76cb 100644 --- a/Govor.Contracts/Govor.Contracts.csproj +++ b/Govor.Contracts/Govor.Contracts.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable @@ -11,6 +11,6 @@ - + diff --git a/Govor.Domain/Common/Error.cs b/Govor.Domain/Common/Error.cs index e49d9f4..242be7a 100644 --- a/Govor.Domain/Common/Error.cs +++ b/Govor.Domain/Common/Error.cs @@ -1,9 +1,16 @@ namespace Govor.Domain.Common; - -public record Error(string Code, string Message) +public record Error(string Code, string Message, ErrorType Type, Dictionary? Errors = null) { - public static readonly Error None = new(string.Empty, string.Empty); - public static readonly Error Null = new("NULL", "Value cannot be null."); + public static Error NotFound(string code, string message) => new(code, message, ErrorType.NotFound); + + public static Error Validation(string code, string message, Dictionary? errors = null) => + new(code, message, ErrorType.Validation, errors); + + public static Error Conflict(string code, string message) => new(code, message, ErrorType.Conflict); + public static Error Unauthorized(string code, string message) => new(code, message, ErrorType.Unauthorized); + public static Error Forbidden(string code, string message) => new(code, message, ErrorType.Forbidden); + public static Error Failure(string code, string message) => new(code, message, ErrorType.Failure); + public override string ToString() => $"{Code}: {Message}"; } \ No newline at end of file diff --git a/Govor.Domain/Common/ErrorType.cs b/Govor.Domain/Common/ErrorType.cs new file mode 100644 index 0000000..ac228b8 --- /dev/null +++ b/Govor.Domain/Common/ErrorType.cs @@ -0,0 +1,11 @@ +namespace Govor.Domain.Common; + +public enum ErrorType +{ + Failure = 0, // (400 Bad Request) + Validation = 1, // (400 Bad Request / 422 Unprocessable) + NotFound = 2, // (404 Not Found) + Conflict = 3, // (409 Conflict) + Unauthorized = 4, // (401 Unauthorized) + Forbidden = 5 // (403 Forbidden) +} \ No newline at end of file diff --git a/Govor.Domain/Common/Result.cs b/Govor.Domain/Common/Result.cs index cfcf3a1..4018144 100644 --- a/Govor.Domain/Common/Result.cs +++ b/Govor.Domain/Common/Result.cs @@ -1,49 +1,20 @@ +using SmartRes; + namespace Govor.Domain.Common; -public class Result +public static class Result { - protected Result(bool isSuccess, Error error) - { - if (isSuccess && error != Error.None || !isSuccess && error == Error.None) - { - throw new ArgumentException("Invalid error state", nameof(error)); - } + // Для методов, возвращающих значение + public static Result Success(T value) => Result.Success(value); + public static Result Failure(Error error) => Result.Failure(error); - IsSuccess = isSuccess; - Error = error; - } - - public bool IsSuccess { get; } - public bool IsFailure => !IsSuccess; - public Error Error { get; } - - public static Result Success() => new(true, Error.None); - public static Result Failure(Error error) => new(false, error); + // Для методов, которые раньше возвращали просто Result (без T) + public static Result Success() => Result.Success(Unit.Value); + public static Result Failure(Error error) => Result.Failure(error); - public static Result Failure(Exception ex) => new(false, new Error(ex.GetType().Name, ex.Message)); - - public static implicit operator Result(Error error) => Failure(error); -} - -public class Result : Result -{ - private readonly T? _value; - - private Result(T? value, bool isSuccess, Error error) : base(isSuccess, error) - { - _value = value; - } - - public T Value => IsSuccess - ? _value! - : throw new InvalidOperationException("The value of a failure result cannot be accessed."); - - public static Result Success(T value) => new(value, true, Error.None); - public static new Result Failure(Error error) => new(default, false, error); - public static new Result Failure(Exception ex) => new(default, false, new Error(ex.GetType().Name, ex.Message)); - - public static implicit operator Result(T value) => Success(value); - - public static implicit operator Result(Error error) => Failure(error); - public static implicit operator T(Result result) => result.Value; + public static Result Failure(Exception ex) => + Result.Failure(new Error(ex.GetType().Name, ex.Message, ErrorType.Failure)); + + public static Result Failure(Exception ex) => + Result.Failure(new Error(ex.GetType().Name, ex.Message, ErrorType.Failure)); } \ No newline at end of file diff --git a/Govor.Domain/Govor.Domain.csproj b/Govor.Domain/Govor.Domain.csproj index 0c11df6..cbaea9f 100644 --- a/Govor.Domain/Govor.Domain.csproj +++ b/Govor.Domain/Govor.Domain.csproj @@ -1,14 +1,20 @@  - net8.0 enable enable + net10.0 - - - + + + + + + + + ..\libs\SmartRes.dll + diff --git a/Govor.sln b/Govor.sln index adbac74..fbf82f2 100644 --- a/Govor.sln +++ b/Govor.sln @@ -4,8 +4,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Govor.API.Tests", "Govor.AP EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Govor.API", "Govor.API\Govor.API.csproj", "{EA8F272F-4276-438A-9DEA-C58860A440AE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Govor.ConsoleClient", "Govor.ConsoleClient\Govor.ConsoleClient.csproj", "{F4535DC3-BDFB-4EB2-B259-F92B6BBB535B}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4ED5259A-6FB4-4D89-8E6B-4778DC68F7D4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{114F53C1-B0AB-4BA0-9E36-0E811D1B3776}" @@ -25,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Govor.ConsoleClient.Tests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Govor.Domain", "Govor.Domain\Govor.Domain.csproj", "{868EAA88-A8E8-4508-B264-1AE3576D47C1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartRes", "..\SmartRes\SmartRes\SmartRes.csproj", "{3AB1DEB2-131B-476F-B4B7-7F8F5479D2FC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,10 +39,6 @@ Global {EA8F272F-4276-438A-9DEA-C58860A440AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA8F272F-4276-438A-9DEA-C58860A440AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA8F272F-4276-438A-9DEA-C58860A440AE}.Release|Any CPU.Build.0 = Release|Any CPU - {F4535DC3-BDFB-4EB2-B259-F92B6BBB535B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F4535DC3-BDFB-4EB2-B259-F92B6BBB535B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F4535DC3-BDFB-4EB2-B259-F92B6BBB535B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F4535DC3-BDFB-4EB2-B259-F92B6BBB535B}.Release|Any CPU.Build.0 = Release|Any CPU {4E94907F-BE20-42A6-AB15-637850FEAD11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4E94907F-BE20-42A6-AB15-637850FEAD11}.Debug|Any CPU.Build.0 = Debug|Any CPU {4E94907F-BE20-42A6-AB15-637850FEAD11}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -67,11 +63,14 @@ Global {868EAA88-A8E8-4508-B264-1AE3576D47C1}.Debug|Any CPU.Build.0 = Debug|Any CPU {868EAA88-A8E8-4508-B264-1AE3576D47C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {868EAA88-A8E8-4508-B264-1AE3576D47C1}.Release|Any CPU.Build.0 = Release|Any CPU + {3AB1DEB2-131B-476F-B4B7-7F8F5479D2FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AB1DEB2-131B-476F-B4B7-7F8F5479D2FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AB1DEB2-131B-476F-B4B7-7F8F5479D2FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AB1DEB2-131B-476F-B4B7-7F8F5479D2FC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {15031CBD-F319-4755-BA91-B86F20BD8E37} = {4ED5259A-6FB4-4D89-8E6B-4778DC68F7D4} {EA8F272F-4276-438A-9DEA-C58860A440AE} = {114F53C1-B0AB-4BA0-9E36-0E811D1B3776} - {F4535DC3-BDFB-4EB2-B259-F92B6BBB535B} = {114F53C1-B0AB-4BA0-9E36-0E811D1B3776} {4E94907F-BE20-42A6-AB15-637850FEAD11} = {114F53C1-B0AB-4BA0-9E36-0E811D1B3776} {FC5EDCA8-FD58-4078-8FB1-2BDBB2F6CA3E} = {114F53C1-B0AB-4BA0-9E36-0E811D1B3776} {F56A64DF-2938-4BE0-83F2-B86429F19259} = {4ED5259A-6FB4-4D89-8E6B-4778DC68F7D4} diff --git a/global.json b/global.json index dad2db5..a11f48e 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.0", + "version": "10.0.0", "rollForward": "latestMajor", "allowPrerelease": true } diff --git a/libs/SmartRes.dll b/libs/SmartRes.dll new file mode 100644 index 0000000..e2c071e Binary files /dev/null and b/libs/SmartRes.dll differ