From c7a94893a2d46758512260a725bdde8d813e508d Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 28 Aug 2025 16:01:55 +0200 Subject: [PATCH] redo --- back/.program_data/app.db-shm | Bin 32768 -> 0 bytes back/.program_data/app.db-wal | Bin 78312 -> 0 bytes back/.program_data/imgs/systemkey.lock | 4 +- back/DTO/UserDto.cs | 4 +- back/DataModels/Event.cs | 4 +- back/DataModels/Gallery.cs | 2 +- back/DataModels/Permission.cs | 34 ++++++- back/DataModels/Person.cs | 4 +- back/DataModels/Photo.cs | 4 +- back/DataModels/Role.cs | 39 ++++++- back/DataModels/User.cs | 6 +- back/Program.cs | 8 +- back/ServicesExtensions/ServicesExtensions.cs | 4 +- back/appsettings.Development.json | 11 +- back/back.csproj | 5 +- back/back.sln | 12 +-- back/controllers/AuthController.cs | 44 ++++++++ back/controllers/UsersController.cs | 8 +- .../options/HealthChecksConfigs.cs | 6 +- back/healthchecks/sqlite.cs | 14 +-- back/persistance/blob/IBlobStorageService.cs | 2 +- back/persistance/data/DataContext.cs | 9 -- .../Abstracts/IPermissionRepository.cs | 10 ++ .../Abstracts/IPersonRepository.cs | 4 +- .../Abstracts/IPhotoRepository.cs | 6 +- .../repositories/Abstracts/IRoleRepository.cs | 10 ++ .../repositories/Abstracts/IUserRepository.cs | 4 +- .../data/repositories/PermissionRepository.cs | 34 +++++++ .../data/repositories/PersonRepository.cs | 2 +- .../data/repositories/PhotoRepository.cs | 2 +- .../data/repositories/RoleRepository.cs | 27 +++++ .../data/repositories/UserRepository.cs | 33 +++--- .../data/seeders/PermissionSeeder.cs | 42 ++++---- back/persistance/data/seeders/RoleSeeder.cs | 28 ++--- .../data/seeders/SystemUserSeeder.cs | 24 ++--- .../bussines/PhotoService/IPhotoService.cs | 2 +- .../bussines/UserService/IUserService.cs | 2 +- .../bussines/UserService/UserService.cs | 5 +- back/services/engine/Crypto/ICryptoService.cs | 2 +- .../engine/ImageResizer/IImageResizer.cs | 2 +- .../PasswordGenerator/IPasswordGenerator.cs | 2 +- .../engine/SystemUser/ISystemUserGenerator.cs | 2 +- .../engine/SystemUser/SystemUserGenerator.cs | 13 ++- back/services/engine/mailing/IEmailService.cs | 2 +- .../Controllers/AuthController.cs | 8 ++ .../Responses/ExecutionError.cs | 6 ++ .../Infraestructura/Responses/Response.cs | 29 ++++++ .../Responses/ValidationError.cs | 7 ++ backend/Presentation/Presentation.csproj | 34 +++++++ backend/Presentation/Program.cs | 96 ++++++++++++++++++ .../Properties/launchSettings.json | 23 +++++ .../Presentation/appsettings.Development.json | 8 ++ backend/Presentation/appsettings.json | 9 ++ .../admin-panel-link/admin-panel-link.html | 9 +- .../admin-panel-link/admin-panel-link.ts | 9 +- .../content-manager-panel-link.html | 9 +- .../content-manager-panel-link.ts | 9 +- .../app/global-components/header/header.ts | 1 - .../user-galleries-link.html | 9 +- .../user-galleries-link.ts | 11 +- .../app/services/userService/userService.ts | 7 +- front/v2/src/models/roleModel.ts | 7 ++ front/v2/src/models/userModel.ts | 60 ++++++----- 63 files changed, 633 insertions(+), 200 deletions(-) delete mode 100644 back/.program_data/app.db-shm delete mode 100644 back/.program_data/app.db-wal create mode 100644 back/controllers/AuthController.cs create mode 100644 back/persistance/data/repositories/Abstracts/IPermissionRepository.cs create mode 100644 back/persistance/data/repositories/Abstracts/IRoleRepository.cs create mode 100644 back/persistance/data/repositories/PermissionRepository.cs create mode 100644 back/persistance/data/repositories/RoleRepository.cs create mode 100644 backend/Presentation/Controllers/AuthController.cs create mode 100644 backend/Presentation/Infraestructura/Responses/ExecutionError.cs create mode 100644 backend/Presentation/Infraestructura/Responses/Response.cs create mode 100644 backend/Presentation/Infraestructura/Responses/ValidationError.cs create mode 100644 backend/Presentation/Presentation.csproj create mode 100644 backend/Presentation/Program.cs create mode 100644 backend/Presentation/Properties/launchSettings.json create mode 100644 backend/Presentation/appsettings.Development.json create mode 100644 backend/Presentation/appsettings.json diff --git a/back/.program_data/app.db-shm b/back/.program_data/app.db-shm deleted file mode 100644 index fbabe1e9ab2a6b057cd8411f2829a4662061f017..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI*F-pT=7zW_4)wV_)s#Du5h)XGS=wZBtlSl9Z^$w0Y30}b6vEb$`LeZi9{*WPy zgIh7r3vco#goN)oz@N$cRg?^}y@>H?o>$rD#ohMjW_|zg`t|+!@wB{sd^y{^U0+WA z_|-Sax+HMirD4rVfB*pk1pcW&*^^ZW5FkK+009C72oNAZ wfB*pk1PBlyK!5-N0t5&UAV8o92CF0ir7}O#lD@ diff --git a/back/.program_data/app.db-wal b/back/.program_data/app.db-wal deleted file mode 100644 index 9157b2c1dc667b412aaecad52baee15b02d19c49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78312 zcmeI*O{^Aj)O}~!jfA`GfOSBD5a}Tts{dM zx2-<%&*}HQce}vbM;G2c;KlJP5P$##AOHafKmY;|fB*y_009WZUf}%sqZ{`_9$!cB z?=J90<4Zr@Ui`onTt^W5KOY8!00bZa0SG_<0uX=z1Rwx`mH`f>j+MC0e!lBMA8dCKo^Ml;=*_kfB*y_009U<00Izz z00bZqdx3+xKzBe-QdFfvNt`8a^#wVU8Q<=CY<0FYHCkq6@@+ zd0{{ZKmY;|fB*y_009U<00Iz*xxhhPz?_7%H(656L5mwXo>fXVy8S9Al7S$3TQO96 zO_leiq$HTRq+Q_IMP@r%7bonPDsc{_PlOhq>9z@JS@S(v*)eTvROgk`U4Y3FXYLnB zJ@N8~?WbS77hNFciwomH00Izz00bZa0SG_<0uX>e>;(?$0%O__194#WMy+IvX@sqN=+m3CKuW^MHxpfRb zKV((=L>EY9X(CIqXYCjG>o=I^AAfB97P>&}mlp5EC zZO~BJ?ao~n-R{JiFxpO%#ylq#Pjvy7%F?N`y1-YSx%J@(&S&pN7l`@d!gvsX00bZa z0SG_<0uX=z1RxN5frGk0y{rVi8LzIzR-&BNnB<_}EobJF1A)jW&>M_!WOA0B3sF?Evr&Bgo+Xl`;)MFx(jew z^33}V{^<3eef8P5?sy1YAm)n;<3Rud5P$##AOHafKmY;|fI#d84(b98Qy7>7n)> z%NhIKmOIwHMW>;x^BiZWCfk&WiYEq?#OFL-(RpXBPA8iVUuTF(o=H`8Q?xB0}?zu>-uv0q&n5CRZ@00bZa0SG_<0uX=z z1mZ4mP#4$^HdfW*qFsHhSM#N6Crxr1CxxAHLZzpAVV2BfrYTkSwPtRjY>22Z$h(mu zxm6*sQhm>3O^%uhgHoU~8{cdyW~F8sCqIvX%Tnb2aaI@j!+m%E^5NU>yo~(InYvQ0j5^cZio%}q4eHX}N*)u<< z@Ubrz?s@bl-0#r^;=Z~t9|Rx(0SG_<0uX=z1Rwwb2*h3Bur8nr8L!NTE)|q!lI+rH zGE!AFaF@*npKRACt3Kg35;j$)pQ(Uz6QUNOkGdvOmx(rOcwt zPSp1NcV^wfB*y_009U<00Izz00bZqcY%Yt zKxxyN3~W{Q7EvLU6So2>dvW=;xXe&`uCbK?F2=`2HKN%pMk2=q5!_{Yazy7wt`fw-?O%m)Do zKmY;|fB*y_009U<00MCrIH(KwqC?THvKUd*O1ovK`EAXp7!(~Xonl%_wE18r=G#V( zSZ#6<=_!u2lm|nr9=Y7aXojIvY7_;RDD&DRy_EE2fzfnVU!3j&OqM0i>H;s`p8ego zuRQt)xCe+A PxXSs+U?$6erGsH?O+ diff --git a/back/.program_data/imgs/systemkey.lock b/back/.program_data/imgs/systemkey.lock index 92b2ff1..6012986 100644 --- a/back/.program_data/imgs/systemkey.lock +++ b/back/.program_data/imgs/systemkey.lock @@ -1,5 +1,5 @@ { "email": "sys@t.em", - "key": "aa0e0979-99db-42e7-8b60-91c2d055b9d0", - "password": "+z1L[oYUupZ>L{4a" + "key": "b60e166e-d4a5-416e-a7c9-142d05fb7f31", + "password": "8C3,uTÑ Roles { get; set; } = []; + public ICollection Roles { get; set; } = []; } diff --git a/back/DataModels/Event.cs b/back/DataModels/Event.cs index 81c9219..85d833c 100644 --- a/back/DataModels/Event.cs +++ b/back/DataModels/Event.cs @@ -1,6 +1,6 @@ -using System.ComponentModel.DataAnnotations; +using MCVIngenieros.Transactional.Abstractions; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using Transactional.Abstractions; namespace back.DataModels; diff --git a/back/DataModels/Gallery.cs b/back/DataModels/Gallery.cs index b540d8e..246f750 100644 --- a/back/DataModels/Gallery.cs +++ b/back/DataModels/Gallery.cs @@ -1,6 +1,6 @@ +using MCVIngenieros.Transactional.Abstractions; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using Transactional.Abstractions; namespace back.DataModels; diff --git a/back/DataModels/Permission.cs b/back/DataModels/Permission.cs index 6fa6251..11de19f 100644 --- a/back/DataModels/Permission.cs +++ b/back/DataModels/Permission.cs @@ -1,10 +1,16 @@ +using MCVIngenieros.Transactional.Abstractions.Interfaces; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace back.DataModels; +public record PermissionDto +{ + public string Id { get; set; } = null!; +} + [Table("Permissions")] -public partial class Permission: IEquatable +public partial class Permission : IEntity { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public string Id { get; set; } = null!; @@ -26,6 +32,32 @@ public partial class Permission: IEquatable Id == other.Id || GetHashCode() == other.GetHashCode(); } + public bool IsNull => this is null; + + public object Clone() => (Permission)MemberwiseClone(); + + public int CompareTo(object? obj) + { + if (obj is null) return 1; + if (obj is not Permission other) throw new ArgumentException("Object is not a Person"); + return CompareTo(other); + } + + public int CompareTo(Permission? other) + { + if (other is null) return 1; + if (ReferenceEquals(this, other)) return 0; + return string.Compare(Id, other.Id, StringComparison.OrdinalIgnoreCase); + } + + public PermissionDto ToDto() + { + return new PermissionDto + { + Id = Id + }; + } + // Static permissions public static readonly Permission ViewContentPermission = new() { Id = "1", Name = "VIEW_CONTENT", Description = "Permission to view content" }; public static readonly Permission LikeContentPermission = new() { Id = "2", Name = "LIKE_CONTENT", Description = "Permission to like content" }; diff --git a/back/DataModels/Person.cs b/back/DataModels/Person.cs index 1c10a4b..61bdfaa 100644 --- a/back/DataModels/Person.cs +++ b/back/DataModels/Person.cs @@ -1,7 +1,7 @@ +using MCVIngenieros.Transactional.Abstractions; +using MCVIngenieros.Transactional.Abstractions.Interfaces; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using Transactional.Abstractions; -using Transactional.Abstractions.Interfaces; namespace back.DataModels; diff --git a/back/DataModels/Photo.cs b/back/DataModels/Photo.cs index 694ee86..fb7af91 100644 --- a/back/DataModels/Photo.cs +++ b/back/DataModels/Photo.cs @@ -1,6 +1,6 @@ -using System.ComponentModel.DataAnnotations; +using MCVIngenieros.Transactional.Abstractions.Interfaces; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using Transactional.Abstractions.Interfaces; namespace back.DataModels; diff --git a/back/DataModels/Role.cs b/back/DataModels/Role.cs index a0466ad..b1dba06 100644 --- a/back/DataModels/Role.cs +++ b/back/DataModels/Role.cs @@ -1,10 +1,17 @@ +using MCVIngenieros.Transactional.Abstractions.Interfaces; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace back.DataModels; +public class RoleDto +{ + public string Id { get; set; } = null!; + public List Permissions { get; set; } = []; +} + [Table("Roles")] -public partial class Role : IEquatable +public partial class Role : IEntity { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public string Id { get; set; } = null!; @@ -18,7 +25,6 @@ public partial class Role : IEquatable public virtual ICollection Permissions { get; set; } = new HashSet(); public virtual ICollection Users { get; set; } = []; - public bool IsAdmin() => BaseRoleModel != null ? BaseRoleModel.IsAdmin() : Id == AdminRole.Id; public bool IsContentManager() => BaseRoleModel != null ? BaseRoleModel.IsContentManager() : Id == ContentManagerRole.Id; public bool IsUser() => BaseRoleModel != null ? BaseRoleModel.IsUser() : Id == UserRole.Id; @@ -43,7 +49,7 @@ public partial class Role : IEquatable BaseRoleModelId = baseRoleModel.Id; foreach (var permission in baseRoleModel.Permissions) { - if (!Permissions.Any(p => p.Id == permission.Id)) + if (!Permissions.Any(p => p.Id == permission.Id)) { Permissions.Add(permission); } @@ -62,6 +68,33 @@ public partial class Role : IEquatable return Id == other.Id || GetHashCode() == other.GetHashCode(); } + public bool IsNull => this is null; + + public object Clone() => (Role)MemberwiseClone(); + + public int CompareTo(object? obj) + { + if (obj is null) return 1; + if (obj is not Role other) throw new ArgumentException("Object is not a Person"); + return CompareTo(other); + } + + public int CompareTo(Role? other) + { + if (other is null) return 1; + if (ReferenceEquals(this, other)) return 0; + return string.Compare(Id, other.Id, StringComparison.OrdinalIgnoreCase); + } + + public RoleDto ToDto() + { + return new RoleDto + { + Id = Id, + Permissions = [.. Permissions.Select(p => p.ToDto())] + }; + } + public static readonly Role UserRole = new( "1", "User", "Role for regular users", [ diff --git a/back/DataModels/User.cs b/back/DataModels/User.cs index 5dbd692..0bbcb94 100644 --- a/back/DataModels/User.cs +++ b/back/DataModels/User.cs @@ -1,7 +1,7 @@ using back.DTO; +using MCVIngenieros.Transactional.Abstractions.Interfaces; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using Transactional.Abstractions.Interfaces; namespace back.DataModels; @@ -36,7 +36,7 @@ public class User : IEntity public UserDto ToDto() => new() { Id = Id, - Roles = Roles + Roles = [.. Roles.Select(r => r.ToDto())] }; public bool IsAdmin() => Roles.Any(r => r.IsAdmin()); @@ -80,7 +80,7 @@ public class User : IEntity password: "", createdAt: DateTime.UtcNow ) - { + { Roles = [Role.AdminRole, Role.ContentManagerRole, Role.UserRole] }; } \ No newline at end of file diff --git a/back/Program.cs b/back/Program.cs index 61028ea..00c7195 100644 --- a/back/Program.cs +++ b/back/Program.cs @@ -1,5 +1,5 @@ using back.ServicesExtensions; -using healthchecks; +using MCVIngenieros.Healthchecks; namespace back; @@ -13,11 +13,7 @@ public class Program builder.Services.AddControllers(); - builder.Services.AddHealthChecks(options => { - options.CacheDuration = TimeSpan.FromMinutes(30); - options.Timeout = TimeSpan.FromSeconds(5); - options.AssembliesToScan = [typeof(Program).Assembly]; - }).DiscoverHealthChecks(); + builder.Services.AddHealthChecksSupport().DiscoverHealthChecks(); // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddSwaggerGen(); diff --git a/back/ServicesExtensions/ServicesExtensions.cs b/back/ServicesExtensions/ServicesExtensions.cs index 00101c8..a797685 100644 --- a/back/ServicesExtensions/ServicesExtensions.cs +++ b/back/ServicesExtensions/ServicesExtensions.cs @@ -3,8 +3,8 @@ using System.Text.Json.Serialization; using back.services.engine.SystemUser; using DependencyInjector; using System.Text.Json; -using Transactional.Abstractions.Interfaces; -using Transactional.Implementations.EntityFramework; +using MCVIngenieros.Transactional.Abstractions.Interfaces; +using MCVIngenieros.Transactional.Implementations.EntityFramework; namespace back.ServicesExtensions; diff --git a/back/appsettings.Development.json b/back/appsettings.Development.json index 46009f1..8f113a3 100644 --- a/back/appsettings.Development.json +++ b/back/appsettings.Development.json @@ -19,10 +19,15 @@ "EnableSsl": true }, "HealthChecksConfigs": { + "CacheDuration": "00:30:00", + "Timeout": "00:00:05", + "AssembliesToScan": [ + "back" + ], "Sqlite": { - "RetryAttempts" : 2, - "Timeout" : "00:05:00", - "RetryDelay" : "00:00:10", + "RetryAttempts": 2, + "Timeout": "00:05:00", + "RetryDelay": "00:00:10", "Severity": "Info" } } diff --git a/back/back.csproj b/back/back.csproj index f2b09dd..5c70458 100644 --- a/back/back.csproj +++ b/back/back.csproj @@ -12,6 +12,7 @@ + @@ -33,7 +34,6 @@ - @@ -48,8 +48,7 @@ - - + diff --git a/back/back.sln b/back/back.sln index 65a05f5..8823921 100644 --- a/back/back.sln +++ b/back/back.sln @@ -5,11 +5,11 @@ VisualStudioVersion = 17.14.36401.2 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "back", "back.csproj", "{392278F3-4B36-47F4-AD31-5FBFCC181AD4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Transactional", "..\..\nuget\Transactional\Transactional.csproj", "{ED76105A-5E6F-4997-86FE-6A7902A2AEBA}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MCVIngenieros.Transactional", "..\..\nuget\Transactional\MCVIngenieros.Transactional.csproj", "{ED76105A-5E6F-4997-86FE-6A7902A2AEBA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjector", "..\..\nuget\DependencyInjector\DependencyInjector.csproj", "{DBDF84A4-235C-4F29-8626-5BD1DC255970}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "healthchecks", "..\..\nuget\healthchecks\healthchecks.csproj", "{B21E2BEF-17B7-4981-9843-C0CC36D67010}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Presentation", "..\backend\Presentation\Presentation.csproj", "{F1DD9D2A-0467-41EE-B3BB-303F1A0C18D6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -29,10 +29,10 @@ Global {DBDF84A4-235C-4F29-8626-5BD1DC255970}.Debug|Any CPU.Build.0 = Debug|Any CPU {DBDF84A4-235C-4F29-8626-5BD1DC255970}.Release|Any CPU.ActiveCfg = Release|Any CPU {DBDF84A4-235C-4F29-8626-5BD1DC255970}.Release|Any CPU.Build.0 = Release|Any CPU - {B21E2BEF-17B7-4981-9843-C0CC36D67010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B21E2BEF-17B7-4981-9843-C0CC36D67010}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B21E2BEF-17B7-4981-9843-C0CC36D67010}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B21E2BEF-17B7-4981-9843-C0CC36D67010}.Release|Any CPU.Build.0 = Release|Any CPU + {F1DD9D2A-0467-41EE-B3BB-303F1A0C18D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1DD9D2A-0467-41EE-B3BB-303F1A0C18D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1DD9D2A-0467-41EE-B3BB-303F1A0C18D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1DD9D2A-0467-41EE-B3BB-303F1A0C18D6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/back/controllers/AuthController.cs b/back/controllers/AuthController.cs new file mode 100644 index 0000000..6f761d5 --- /dev/null +++ b/back/controllers/AuthController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Mvc; + +namespace back.controllers; + +public class ValidationErrors +{ + public string? Field { get; set; } + public string? Message { get; set; } +} + +public class ExecutionErrors +{ + public Exception? Exception { get; set; } + public string? Message { get; set; } +} + +public abstract class ResponseBase +{ + public object? Data { get; set; } + public string? Message { get; set; } + public bool Success { get; set; } + public int StatusCode { get; set; } + public ValidationErrors[] ValidationErrors { get; set; } + public ExecutionErrors[] ExecutionErrors { get; set; } +} + +public record LoginRequest(string Username, string Password); + +[ApiController, Route("api/[controller]")] +public class AuthController(IAuthService authService) : ControllerBase +{ + + private readonly IAuthService _authService = authService; + + [HttpPost, Route("login")] + public async Task Login([FromBody] LoginRequest loginRequest) + { + // validar que el usuario y la contraseña sean correctos + // obtener el token JWT encriptado + // obtener el refresh token + // devolver el token JWT y el refresh token en los headers de las respuestas + // devolver datos del usuario en el body de la respuesta + } +} diff --git a/back/controllers/UsersController.cs b/back/controllers/UsersController.cs index 1568056..b35d60a 100644 --- a/back/controllers/UsersController.cs +++ b/back/controllers/UsersController.cs @@ -1,8 +1,6 @@ using back.DataModels; -using back.DTO; using back.services.bussines; using back.services.bussines.UserService; -using Mapster; using Microsoft.AspNetCore.Mvc; namespace back.controllers; @@ -46,14 +44,14 @@ public class UsersController(IUserService user) : ControllerBase if (user == null || string.IsNullOrEmpty(user.Email) || string.IsNullOrEmpty(user.Password)) return BadRequest(Errors.BadRequest.Description); - if (user.Email.Equals(DataModels.User.SystemUser.Email, StringComparison.InvariantCultureIgnoreCase)) + if (user.Email.Equals(DataModels.User.SystemUser.Email, StringComparison.InvariantCultureIgnoreCase)) { if (string.IsNullOrEmpty(user.SystemKey)) return Unauthorized(Errors.Unauthorized.Description); var systemUser = await _user.ValidateSystemUser(user.Email, user.Password, user.SystemKey, clientId); if (systemUser == null) return Unauthorized(Errors.Unauthorized.Description); - return Ok(systemUser.Adapt()); + return Ok(systemUser.ToDto()); } var existingUser = await _user.Login(user.Email, user.Password, clientId); @@ -74,7 +72,7 @@ public class UsersController(IUserService user) : ControllerBase // POST api/ [HttpPost("[action]")] public async Task Register( - [FromHeader(Name = "X-client-thumbprint")] string clientId, + [FromHeader(Name = "X-client-thumbprint")] string clientId, [FromBody] RegisterFromModel user) { if (user == null) diff --git a/back/healthchecks/options/HealthChecksConfigs.cs b/back/healthchecks/options/HealthChecksConfigs.cs index 2c432ac..88fa79b 100644 --- a/back/healthchecks/options/HealthChecksConfigs.cs +++ b/back/healthchecks/options/HealthChecksConfigs.cs @@ -1,8 +1,6 @@ -using HealthChecksConfigsBase = healthchecks.Options.HealthChecksConfigs; +namespace back.healthchecks.Options; -namespace back.healthchecks.Options; - -public partial class HealthChecksConfigs : HealthChecksConfigsBase +public partial class HealthChecksConfigs : MCVIngenieros.Healthchecks.Options.HealthChecksConfigs { public const string Sqlite = "Sqlite"; } diff --git a/back/healthchecks/sqlite.cs b/back/healthchecks/sqlite.cs index 5f22ce3..f825a40 100644 --- a/back/healthchecks/sqlite.cs +++ b/back/healthchecks/sqlite.cs @@ -1,12 +1,12 @@ -using back.Options; -using healthchecks; -using healthchecks.Abstracts; -using back.healthchecks.Options; +using back.healthchecks.Options; +using back.Options; +using MCVIngenieros.Healthchecks; +using MCVIngenieros.Healthchecks.Abstracts; using Microsoft.Extensions.Options; namespace back.healthchecks; -public class SqliteHealthCheck(IOptionsMonitor databaseConfig, IOptionsMonitor healthchecksConfig) : IHealthCheck +public class SqliteHealthCheck(IOptionsMonitor databaseConfig, IOptionsMonitor healthchecksConfig) : HealthCheck { private readonly DatabaseConfig databaseConfig = databaseConfig.Get(DatabaseConfig.DataStorage); private readonly HealthChecksConfigs hcConfig = healthchecksConfig.Get(HealthChecksConfigs.Sqlite); @@ -17,7 +17,7 @@ public class SqliteHealthCheck(IOptionsMonitor databaseConfig, I public TimeSpan? RetryDelay => hcConfig.RetryDelay ?? TimeSpan.FromSeconds(1); public HealthCheckSeverity? Severity => hcConfig.Severity ?? HealthCheckSeverity.Critical; - public Task CheckAsync(CancellationToken cancellationToken = default) + public override Task CheckAsync(CancellationToken cancellationToken = default) { var isHealthy = false; var details = string.Empty; @@ -43,7 +43,7 @@ public class SqliteHealthCheck(IOptionsMonitor databaseConfig, I details = $"Failed to connect to SQLite database: {ex.Message}"; } - return Task.FromResult(new HealthCheckResult(isHealthy, null) + return Task.FromResult(new HealthCheckResult(isHealthy) { Details = details, Severity = isHealthy ? HealthCheckSeverity.Info : HealthCheckSeverity.Critical diff --git a/back/persistance/blob/IBlobStorageService.cs b/back/persistance/blob/IBlobStorageService.cs index 1ca5f14..d9138af 100644 --- a/back/persistance/blob/IBlobStorageService.cs +++ b/back/persistance/blob/IBlobStorageService.cs @@ -1,4 +1,4 @@ -using DependencyInjector.Lifetimes; +using DependencyInjector.Abstractions.Lifetimes; namespace back.persistance.blob; diff --git a/back/persistance/data/DataContext.cs b/back/persistance/data/DataContext.cs index 5e32618..7dc197e 100644 --- a/back/persistance/data/DataContext.cs +++ b/back/persistance/data/DataContext.cs @@ -49,15 +49,6 @@ public partial class DataContext : DbContext relationEstablisher?.EstablishRelation(modelBuilder); }); - //typeof(ISeeder).Assembly.GetExportedTypes() - // .Where(t => typeof(ISeeder).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract) - // .ToList() - // .ForEach(seederType => - // { - // var seeder = (ISeeder?)Activator.CreateInstance(seederType); - // seeder?.Seed(modelBuilder); - // }); - OnModelCreatingPartial(modelBuilder); } diff --git a/back/persistance/data/repositories/Abstracts/IPermissionRepository.cs b/back/persistance/data/repositories/Abstracts/IPermissionRepository.cs new file mode 100644 index 0000000..e85d8e1 --- /dev/null +++ b/back/persistance/data/repositories/Abstracts/IPermissionRepository.cs @@ -0,0 +1,10 @@ +using back.DataModels; +using DependencyInjector.Abstractions.Lifetimes; +using MCVIngenieros.Transactional.Abstractions.Interfaces; + +namespace back.persistance.data.repositories.Abstracts; + +public interface IPermissionRepository : IRepository, IScoped +{ + Task SeedDefaultPermissions(); +} diff --git a/back/persistance/data/repositories/Abstracts/IPersonRepository.cs b/back/persistance/data/repositories/Abstracts/IPersonRepository.cs index 3820ccf..f28dcf3 100644 --- a/back/persistance/data/repositories/Abstracts/IPersonRepository.cs +++ b/back/persistance/data/repositories/Abstracts/IPersonRepository.cs @@ -1,6 +1,6 @@ using back.DataModels; -using DependencyInjector.Lifetimes; -using Transactional.Abstractions.Interfaces; +using DependencyInjector.Abstractions.Lifetimes; +using MCVIngenieros.Transactional.Abstractions.Interfaces; namespace back.persistance.data.repositories.Abstracts; diff --git a/back/persistance/data/repositories/Abstracts/IPhotoRepository.cs b/back/persistance/data/repositories/Abstracts/IPhotoRepository.cs index b3cb733..ed03f6b 100644 --- a/back/persistance/data/repositories/Abstracts/IPhotoRepository.cs +++ b/back/persistance/data/repositories/Abstracts/IPhotoRepository.cs @@ -1,8 +1,8 @@ using back.DataModels; -using DependencyInjector.Lifetimes; -using Transactional.Abstractions.Interfaces; +using DependencyInjector.Abstractions.Lifetimes; +using MCVIngenieros.Transactional.Abstractions.Interfaces; namespace back.persistance.data.repositories.Abstracts; -public interface IPhotoRepository : IRepository, IScoped +public interface IPhotoRepository : IRepository, IScoped { } diff --git a/back/persistance/data/repositories/Abstracts/IRoleRepository.cs b/back/persistance/data/repositories/Abstracts/IRoleRepository.cs new file mode 100644 index 0000000..35b8b34 --- /dev/null +++ b/back/persistance/data/repositories/Abstracts/IRoleRepository.cs @@ -0,0 +1,10 @@ +using back.DataModels; +using DependencyInjector.Abstractions.Lifetimes; +using MCVIngenieros.Transactional.Abstractions.Interfaces; + +namespace back.persistance.data.repositories.Abstracts; + +public interface IRoleRepository : IRepository, IScoped +{ + Task SeedDefaultRoles(); +} diff --git a/back/persistance/data/repositories/Abstracts/IUserRepository.cs b/back/persistance/data/repositories/Abstracts/IUserRepository.cs index b5d8aab..6d39852 100644 --- a/back/persistance/data/repositories/Abstracts/IUserRepository.cs +++ b/back/persistance/data/repositories/Abstracts/IUserRepository.cs @@ -1,6 +1,6 @@ using back.DataModels; -using DependencyInjector.Lifetimes; -using Transactional.Abstractions.Interfaces; +using DependencyInjector.Abstractions.Lifetimes; +using MCVIngenieros.Transactional.Abstractions.Interfaces; namespace back.persistance.data.repositories.Abstracts; diff --git a/back/persistance/data/repositories/PermissionRepository.cs b/back/persistance/data/repositories/PermissionRepository.cs new file mode 100644 index 0000000..3425e17 --- /dev/null +++ b/back/persistance/data/repositories/PermissionRepository.cs @@ -0,0 +1,34 @@ +using back.DataModels; +using back.persistance.data.repositories.Abstracts; +using MCVIngenieros.Transactional.Implementations.EntityFramework; + +namespace back.persistance.data.repositories; + +public class PermissionRepository(DataContext context) : ReadWriteRepository(context), IPermissionRepository +{ + // Implement methods specific to Photo repository if needed + public async Task SeedDefaultPermissions() + { + var defaultPermissions = new List + { + Permission.ViewContentPermission, + Permission.LikeContentPermission, + Permission.EditContentPermission, + Permission.DeleteContentPermission, + Permission.CreateContentPermission, + Permission.EditUserPermission, + Permission.DeleteUserPermission, + Permission.DisableUserPermission, + Permission.CreateUserPermission, + Permission.EditWebConfigPermission + }; + foreach (var permission in defaultPermissions) + { + if (!Entities.Any(p => p.Id == permission.Id)) + { + Entities.Add(permission); + } + } + await SaveChanges(); + } +} diff --git a/back/persistance/data/repositories/PersonRepository.cs b/back/persistance/data/repositories/PersonRepository.cs index 0d834b3..b5e4def 100644 --- a/back/persistance/data/repositories/PersonRepository.cs +++ b/back/persistance/data/repositories/PersonRepository.cs @@ -1,6 +1,6 @@ using back.DataModels; using back.persistance.data.repositories.Abstracts; -using Transactional.Implementations.EntityFramework; +using MCVIngenieros.Transactional.Implementations.EntityFramework; namespace back.persistance.data.repositories; diff --git a/back/persistance/data/repositories/PhotoRepository.cs b/back/persistance/data/repositories/PhotoRepository.cs index d7c4084..36aee8a 100644 --- a/back/persistance/data/repositories/PhotoRepository.cs +++ b/back/persistance/data/repositories/PhotoRepository.cs @@ -1,6 +1,6 @@ using back.DataModels; using back.persistance.data.repositories.Abstracts; -using Transactional.Implementations.EntityFramework; +using MCVIngenieros.Transactional.Implementations.EntityFramework; namespace back.persistance.data.repositories; diff --git a/back/persistance/data/repositories/RoleRepository.cs b/back/persistance/data/repositories/RoleRepository.cs new file mode 100644 index 0000000..206e31f --- /dev/null +++ b/back/persistance/data/repositories/RoleRepository.cs @@ -0,0 +1,27 @@ +using back.DataModels; +using back.persistance.data.repositories.Abstracts; +using MCVIngenieros.Transactional.Implementations.EntityFramework; + +namespace back.persistance.data.repositories; + +public class RoleRepository(DataContext context) : ReadWriteRepository(context), IRoleRepository +{ + // Implement methods specific to Photo repository if needed + public async Task SeedDefaultRoles() + { + var defaultRoles = new List + { + Role.AdminRole, + Role.UserRole, + Role.ContentManagerRole + }; + foreach (var role in defaultRoles) + { + if (!Entities.Any(p => p.Id == role.Id)) + { + Entities.Add(role); + } + } + await SaveChanges(); + } +} diff --git a/back/persistance/data/repositories/UserRepository.cs b/back/persistance/data/repositories/UserRepository.cs index 0a1bc8c..0892aa0 100644 --- a/back/persistance/data/repositories/UserRepository.cs +++ b/back/persistance/data/repositories/UserRepository.cs @@ -1,20 +1,22 @@ using back.DataModels; using back.persistance.data.repositories.Abstracts; +using MCVIngenieros.Transactional.Implementations.EntityFramework; using Microsoft.EntityFrameworkCore; -using Transactional.Implementations.EntityFramework; namespace back.persistance.data.repositories; -public class UserRepository(DataContext context) : ReadWriteRepository(context), IUserRepository +public class UserRepository( + DataContext context + ) : ReadWriteRepository(context), IUserRepository { public async Task GetByEmail(string email) { - try + try { if (string.IsNullOrEmpty(email)) return null; return await Entities.FirstOrDefaultAsync(u => u.Email == email); } - catch + catch { return null; } @@ -37,26 +39,29 @@ public class UserRepository(DataContext context) : ReadWriteRepository(con public async Task Login(string email, string password) { if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password)) return null; - try + try { - return await Entities.FirstOrDefaultAsync(u => u.Email == email && u.Password == password); + return await Entities + .Include(u => u.Roles) + .ThenInclude(r => r.Permissions) + .FirstOrDefaultAsync(u => u.Email == email && u.Password == password); } - catch + catch { - return null; + return null; } } - + public async Task ExistsByEmail(string email) { - try + try { if (string.IsNullOrEmpty(email)) return false; return await Entities.AnyAsync(u => u.Email == email); - } - catch - { - return false; + } + catch + { + return false; } } diff --git a/back/persistance/data/seeders/PermissionSeeder.cs b/back/persistance/data/seeders/PermissionSeeder.cs index 638e3e0..ab777ae 100644 --- a/back/persistance/data/seeders/PermissionSeeder.cs +++ b/back/persistance/data/seeders/PermissionSeeder.cs @@ -1,23 +1,23 @@ -//using back.DataModels; -//using Microsoft.EntityFrameworkCore; +using back.DataModels; +using Microsoft.EntityFrameworkCore; -//namespace back.persistance.data.seeders; +namespace back.persistance.data.seeders; -//public class PermissionSeeder : ISeeder -//{ -// public void Seed(ModelBuilder modelBuilder) -// { -// modelBuilder.Entity().HasData( -// Permission.ViewContentPermission, -// Permission.LikeContentPermission, -// Permission.EditContentPermission, -// Permission.DeleteContentPermission, -// Permission.CreateContentPermission, -// Permission.EditUserPermission, -// Permission.DeleteUserPermission, -// Permission.DisableUserPermission, -// Permission.CreateUserPermission, -// Permission.EditWebConfigPermission -// ); -// } -//} \ No newline at end of file +public class PermissionSeeder : ISeeder +{ + public void Seed(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasData( + Permission.ViewContentPermission, + Permission.LikeContentPermission, + Permission.EditContentPermission, + Permission.DeleteContentPermission, + Permission.CreateContentPermission, + Permission.EditUserPermission, + Permission.DeleteUserPermission, + Permission.DisableUserPermission, + Permission.CreateUserPermission, + Permission.EditWebConfigPermission + ); + } +} \ No newline at end of file diff --git a/back/persistance/data/seeders/RoleSeeder.cs b/back/persistance/data/seeders/RoleSeeder.cs index 3da6ac9..06fcc86 100644 --- a/back/persistance/data/seeders/RoleSeeder.cs +++ b/back/persistance/data/seeders/RoleSeeder.cs @@ -1,16 +1,16 @@ -//using back.DataModels; -//using Microsoft.EntityFrameworkCore; +using back.DataModels; +using Microsoft.EntityFrameworkCore; -//namespace back.persistance.data.seeders; +namespace back.persistance.data.seeders; -//public class RoleSeeder : ISeeder -//{ -// public void Seed(ModelBuilder modelBuilder) -// { -// modelBuilder.Entity().HasData( -// new Role { Id = "1", Name = "User", Description = "Role for regular users", BaseRoleModelId = null }, -// new Role { Id = "2", Name = "Content Manager", Description = "Role for managing content", BaseRoleModelId = "1" }, -// new Role { Id = "3", Name = "Admin", Description = "Administrator role with full permissions", BaseRoleModelId = "2" } -// ); -// } -//} \ No newline at end of file +public class RoleSeeder : ISeeder +{ + public void Seed(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasData( + new Role { Id = "1", Name = "User", Description = "Role for regular users", BaseRoleModelId = null }, + new Role { Id = "2", Name = "Content Manager", Description = "Role for managing content", BaseRoleModelId = "1" }, + new Role { Id = "3", Name = "Admin", Description = "Administrator role with full permissions", BaseRoleModelId = "2" } + ); + } +} \ No newline at end of file diff --git a/back/persistance/data/seeders/SystemUserSeeder.cs b/back/persistance/data/seeders/SystemUserSeeder.cs index b5cb27e..0f7ae32 100644 --- a/back/persistance/data/seeders/SystemUserSeeder.cs +++ b/back/persistance/data/seeders/SystemUserSeeder.cs @@ -1,14 +1,14 @@ -using back.DataModels; -using Microsoft.EntityFrameworkCore; +//using back.DataModels; +//using Microsoft.EntityFrameworkCore; -namespace back.persistance.data.seeders; +//namespace back.persistance.data.seeders; -public class SystemUserSeeder : ISeeder -{ - public void Seed(ModelBuilder modelBuilder) - { - modelBuilder.Entity().HasData( - User.SystemUser - ); - } -} \ No newline at end of file +//public class SystemUserSeeder : ISeeder +//{ +// public void Seed(ModelBuilder modelBuilder) +// { +// modelBuilder.Entity().HasData( +// User.SystemUser +// ); +// } +//} \ No newline at end of file diff --git a/back/services/bussines/PhotoService/IPhotoService.cs b/back/services/bussines/PhotoService/IPhotoService.cs index 8d045bb..c7f725f 100644 --- a/back/services/bussines/PhotoService/IPhotoService.cs +++ b/back/services/bussines/PhotoService/IPhotoService.cs @@ -1,6 +1,6 @@ using back.DataModels; using back.DTO; -using DependencyInjector.Lifetimes; +using DependencyInjector.Abstractions.Lifetimes; namespace back.services.bussines.PhotoService; diff --git a/back/services/bussines/UserService/IUserService.cs b/back/services/bussines/UserService/IUserService.cs index ec9bc48..0d576af 100644 --- a/back/services/bussines/UserService/IUserService.cs +++ b/back/services/bussines/UserService/IUserService.cs @@ -1,5 +1,5 @@ using back.DataModels; -using DependencyInjector.Lifetimes; +using DependencyInjector.Abstractions.Lifetimes; namespace back.services.bussines.UserService; diff --git a/back/services/bussines/UserService/UserService.cs b/back/services/bussines/UserService/UserService.cs index 472dc7c..6f2e4f0 100644 --- a/back/services/bussines/UserService/UserService.cs +++ b/back/services/bussines/UserService/UserService.cs @@ -68,7 +68,7 @@ public class UserService( return existingUser; } - public async Task Login(string email, string decryptedPass) + public async Task Login(string email, string decryptedPass) { var salt = await _repository.GetUserSaltByEmail(email); var hashedPassword = _cryptoService.HashPassword(decryptedPass, salt); @@ -136,6 +136,7 @@ public class UserService( { return null; } - return await Login(user.Email!, decryptedPassword); + var loggedUser = await Login(user.Email!, decryptedPassword); + return loggedUser; } } diff --git a/back/services/engine/Crypto/ICryptoService.cs b/back/services/engine/Crypto/ICryptoService.cs index 2a957fd..224a286 100644 --- a/back/services/engine/Crypto/ICryptoService.cs +++ b/back/services/engine/Crypto/ICryptoService.cs @@ -1,4 +1,4 @@ -using DependencyInjector.Lifetimes; +using DependencyInjector.Abstractions.Lifetimes; namespace back.services.engine.Crypto; diff --git a/back/services/engine/ImageResizer/IImageResizer.cs b/back/services/engine/ImageResizer/IImageResizer.cs index dbb82a2..02f6de5 100644 --- a/back/services/engine/ImageResizer/IImageResizer.cs +++ b/back/services/engine/ImageResizer/IImageResizer.cs @@ -1,4 +1,4 @@ -using DependencyInjector.Lifetimes; +using DependencyInjector.Abstractions.Lifetimes; namespace back.services.engine.ImageResizer; diff --git a/back/services/engine/PasswordGenerator/IPasswordGenerator.cs b/back/services/engine/PasswordGenerator/IPasswordGenerator.cs index aef6e6c..bcc7d97 100644 --- a/back/services/engine/PasswordGenerator/IPasswordGenerator.cs +++ b/back/services/engine/PasswordGenerator/IPasswordGenerator.cs @@ -1,4 +1,4 @@ -using DependencyInjector.Lifetimes; +using DependencyInjector.Abstractions.Lifetimes; namespace back.services.engine.PasswordGenerator; diff --git a/back/services/engine/SystemUser/ISystemUserGenerator.cs b/back/services/engine/SystemUser/ISystemUserGenerator.cs index c148ec4..01cb831 100644 --- a/back/services/engine/SystemUser/ISystemUserGenerator.cs +++ b/back/services/engine/SystemUser/ISystemUserGenerator.cs @@ -1,4 +1,4 @@ -using DependencyInjector.Lifetimes; +using DependencyInjector.Abstractions.Lifetimes; namespace back.services.engine.SystemUser; diff --git a/back/services/engine/SystemUser/SystemUserGenerator.cs b/back/services/engine/SystemUser/SystemUserGenerator.cs index 8f81afa..dd704b9 100644 --- a/back/services/engine/SystemUser/SystemUserGenerator.cs +++ b/back/services/engine/SystemUser/SystemUserGenerator.cs @@ -4,8 +4,8 @@ using back.persistance.data; using back.persistance.data.repositories.Abstracts; using back.services.engine.Crypto; using back.services.engine.PasswordGenerator; +using MCVIngenieros.Transactional.Abstractions.Interfaces; using System.Text.Json; -using Transactional.Abstractions.Interfaces; namespace back.services.engine.SystemUser; @@ -14,17 +14,20 @@ public class SystemUserGenerator( JsonSerializerOptions jsonSerializerOptions, IUserRepository userRepository, IPersonRepository personRepository, + IRoleRepository roleRepository, + IPermissionRepository permissionRepository, ICryptoService cryptoService, IBlobStorageService blobStorageService, IPasswordGenerator passwordGenerator) : ISystemUserGenerator { public async Task GenerateAsync() { - var systemKey = new SystemKey() { + var systemKey = new SystemKey() + { Password = passwordGenerator.Generate(16), }; var systemKeyJson = JsonSerializer.Serialize(systemKey, options: jsonSerializerOptions); - + using Stream stream = new MemoryStream(new System.Text.UTF8Encoding(true).GetBytes(systemKeyJson)); await blobStorageService.Delete("systemkey.lock"); @@ -42,11 +45,13 @@ public class SystemUserGenerator( { await transactional.DoTransaction(async () => { + await permissionRepository.SeedDefaultPermissions(); + await roleRepository.SeedDefaultRoles(); await personRepository.Insert(Person.SystemPerson); await userRepository.Insert(User.SystemUser); }); } - else + else { await userRepository.Update(User.SystemUser); await userRepository.SaveChanges(); diff --git a/back/services/engine/mailing/IEmailService.cs b/back/services/engine/mailing/IEmailService.cs index b8242bf..ecfec68 100644 --- a/back/services/engine/mailing/IEmailService.cs +++ b/back/services/engine/mailing/IEmailService.cs @@ -1,4 +1,4 @@ -using DependencyInjector.Lifetimes; +using DependencyInjector.Abstractions.Lifetimes; namespace back.services.engine.mailing; diff --git a/backend/Presentation/Controllers/AuthController.cs b/backend/Presentation/Controllers/AuthController.cs new file mode 100644 index 0000000..aa5a22e --- /dev/null +++ b/backend/Presentation/Controllers/AuthController.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Presentation.Controllers; + +public class AuthController : Controller +{ + +} diff --git a/backend/Presentation/Infraestructura/Responses/ExecutionError.cs b/backend/Presentation/Infraestructura/Responses/ExecutionError.cs new file mode 100644 index 0000000..61c9d3a --- /dev/null +++ b/backend/Presentation/Infraestructura/Responses/ExecutionError.cs @@ -0,0 +1,6 @@ +namespace Presentation.Infraestructura.Responses; + +public sealed class ExecutionError(string message) +{ + public string Message { get; set; } = message; +} diff --git a/backend/Presentation/Infraestructura/Responses/Response.cs b/backend/Presentation/Infraestructura/Responses/Response.cs new file mode 100644 index 0000000..7c896cd --- /dev/null +++ b/backend/Presentation/Infraestructura/Responses/Response.cs @@ -0,0 +1,29 @@ +using System.Net; + +namespace Presentation.Infraestructura.Responses; + +public sealed class Response +{ + public bool IsSuccess { get; set; } + public HttpStatusCode StatusCode { get; set; } + public T? Data { get; set; } + public ValidationError[]? ValidationErrors { get; set; } + public ExecutionError[]? ExecutionErrors { get; set; } + + public static Response Success(T result, HttpStatusCode statusCode = HttpStatusCode.OK) => + new() + { + IsSuccess = true, + StatusCode = statusCode, + Data = result + }; + + public static Response Failure(HttpStatusCode statusCode = HttpStatusCode.InternalServerError, ValidationError[]? validationErrors = null, ExecutionError[]? executionErrors = null) => + new() + { + IsSuccess = false, + StatusCode = statusCode, + ValidationErrors = validationErrors, + ExecutionErrors = executionErrors + }; +} \ No newline at end of file diff --git a/backend/Presentation/Infraestructura/Responses/ValidationError.cs b/backend/Presentation/Infraestructura/Responses/ValidationError.cs new file mode 100644 index 0000000..fea7ef2 --- /dev/null +++ b/backend/Presentation/Infraestructura/Responses/ValidationError.cs @@ -0,0 +1,7 @@ +namespace Presentation.Infraestructura.Responses; + +public sealed class ValidationError(string fieldName, string message) +{ + public string Field { get; set; } = fieldName; + public string Message { get; set; } = message; +} diff --git a/backend/Presentation/Presentation.csproj b/backend/Presentation/Presentation.csproj new file mode 100644 index 0000000..f1ec019 --- /dev/null +++ b/backend/Presentation/Presentation.csproj @@ -0,0 +1,34 @@ + + + + net9.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/Presentation/Program.cs b/backend/Presentation/Program.cs new file mode 100644 index 0000000..e95a90f --- /dev/null +++ b/backend/Presentation/Program.cs @@ -0,0 +1,96 @@ +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using Serilog; +using Serilog.Events; +using Serilog.Sinks.OpenTelemetry; + +namespace Presentation +{ + public class Program + { + public static void Main(string[] args) + { + // Configura Serilog como logger global antes de crear el builder + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.OpenTelemetry(options => + { + options.Endpoint = "http://localhost:4317"; // OTLP endpoint + options.Protocol = OtlpProtocol.Grpc; + options.ResourceAttributes = new Dictionary + { + ["service.name"] = "mmorales.photo-backend" + }; + }) + .CreateLogger(); + + try + { + Log.Information("Starting up"); + var builder = WebApplication.CreateBuilder(args); + builder.Host.UseSerilog(); // Usa Serilog como proveedor de logs por defecto + + builder.Services.AddProblemDetails(options => + options.CustomizeProblemDetails = + ctx => ctx.ProblemDetails.Extensions.Add("traceId", ctx.HttpContext.TraceIdentifier) + ); + builder.Services.AddOpenTelemetry() + .WithTracing(tracerProviderBuilder => + { + tracerProviderBuilder + .SetResourceBuilder(ResourceBuilder.CreateDefault() + .AddService(builder.Environment.ApplicationName)) + .AddAspNetCoreInstrumentation() // Traza todas las peticiones HTTP entrantes + .AddHttpClientInstrumentation() // Traza las llamadas HttpClient salientes + .AddOtlpExporter(opt => + { + opt.Endpoint = new Uri("http://localhost:4317"); // Dirección del colector OTel + }) + .AddConsoleExporter(); // Exporta trazas también a consola para desarrollo + }); + // Add services to the container. + + builder.Services.AddControllers(); + // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi + builder.Services.AddOpenApi(); + + var app = builder.Build(); + if (!app.Environment.IsDevelopment()) + { + app.UseExceptionHandler(); + app.UseHsts(); + } + app.UseStatusCodePages(); + + app.UseAuthentication(); // Habilita autenticación + app.UseAuthorization(); // Habilita autorización + + // Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) + { + app.MapOpenApi(); + } + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + + + app.MapControllers(); + + app.Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "Application start-up failed"); + throw; + } + finally + { + Log.CloseAndFlush(); + } + } + } +} diff --git a/backend/Presentation/Properties/launchSettings.json b/backend/Presentation/Properties/launchSettings.json new file mode 100644 index 0000000..64271a2 --- /dev/null +++ b/backend/Presentation/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5101", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7265;http://localhost:5101", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/backend/Presentation/appsettings.Development.json b/backend/Presentation/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/backend/Presentation/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/backend/Presentation/appsettings.json b/backend/Presentation/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/backend/Presentation/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/front/v2/src/app/global-components/admin-panel-link/admin-panel-link.html b/front/v2/src/app/global-components/admin-panel-link/admin-panel-link.html index 262a792..620d350 100644 --- a/front/v2/src/app/global-components/admin-panel-link/admin-panel-link.html +++ b/front/v2/src/app/global-components/admin-panel-link/admin-panel-link.html @@ -1,3 +1,6 @@ - + diff --git a/front/v2/src/app/global-components/admin-panel-link/admin-panel-link.ts b/front/v2/src/app/global-components/admin-panel-link/admin-panel-link.ts index d3c5734..ed05c1a 100644 --- a/front/v2/src/app/global-components/admin-panel-link/admin-panel-link.ts +++ b/front/v2/src/app/global-components/admin-panel-link/admin-panel-link.ts @@ -1,11 +1,10 @@ import { Component } from '@angular/core'; +import { SvgButton } from '../../../utils/svg-button/svg-button'; @Component({ selector: 'admin-panel-link', - imports: [], + imports: [SvgButton], templateUrl: './admin-panel-link.html', - styleUrl: './admin-panel-link.scss' + styleUrl: './admin-panel-link.scss', }) -export class AdminPanelLink { - -} +export class AdminPanelLink {} diff --git a/front/v2/src/app/global-components/content-manager-panel-link/content-manager-panel-link.html b/front/v2/src/app/global-components/content-manager-panel-link/content-manager-panel-link.html index 2f4b699..3b5fdc2 100644 --- a/front/v2/src/app/global-components/content-manager-panel-link/content-manager-panel-link.html +++ b/front/v2/src/app/global-components/content-manager-panel-link/content-manager-panel-link.html @@ -1,3 +1,6 @@ - + diff --git a/front/v2/src/app/global-components/content-manager-panel-link/content-manager-panel-link.ts b/front/v2/src/app/global-components/content-manager-panel-link/content-manager-panel-link.ts index 4b70f8d..be62e25 100644 --- a/front/v2/src/app/global-components/content-manager-panel-link/content-manager-panel-link.ts +++ b/front/v2/src/app/global-components/content-manager-panel-link/content-manager-panel-link.ts @@ -1,11 +1,10 @@ import { Component } from '@angular/core'; +import { SvgButton } from '../../../utils/svg-button/svg-button'; @Component({ selector: 'content-manager-panel-link', - imports: [], + imports: [SvgButton], templateUrl: './content-manager-panel-link.html', - styleUrl: './content-manager-panel-link.scss' + styleUrl: './content-manager-panel-link.scss', }) -export class ContentManagerPanelLink { - -} +export class ContentManagerPanelLink {} diff --git a/front/v2/src/app/global-components/header/header.ts b/front/v2/src/app/global-components/header/header.ts index 4435ced..224b338 100644 --- a/front/v2/src/app/global-components/header/header.ts +++ b/front/v2/src/app/global-components/header/header.ts @@ -12,7 +12,6 @@ import { OnInit } from '@angular/core'; import { userModel } from '../../../models/userModel'; import { ServicesLink } from '../services-link/services-link'; import { Router, NavigationEnd } from '@angular/router'; -import { R } from '@angular/cdk/keycodes'; @Component({ selector: 'custom-header', diff --git a/front/v2/src/app/global-components/user-galleries-link/user-galleries-link.html b/front/v2/src/app/global-components/user-galleries-link/user-galleries-link.html index 755c6e1..78af224 100644 --- a/front/v2/src/app/global-components/user-galleries-link/user-galleries-link.html +++ b/front/v2/src/app/global-components/user-galleries-link/user-galleries-link.html @@ -1,3 +1,6 @@ - + diff --git a/front/v2/src/app/global-components/user-galleries-link/user-galleries-link.ts b/front/v2/src/app/global-components/user-galleries-link/user-galleries-link.ts index 7c5520a..bbbb0a0 100644 --- a/front/v2/src/app/global-components/user-galleries-link/user-galleries-link.ts +++ b/front/v2/src/app/global-components/user-galleries-link/user-galleries-link.ts @@ -1,11 +1,10 @@ -import { Component } from '@angular/core'; +import { Component, inject, signal } from '@angular/core'; +import { SvgButton } from '../../../utils/svg-button/svg-button'; @Component({ selector: 'user-galleries-link', - imports: [], + imports: [SvgButton], templateUrl: './user-galleries-link.html', - styleUrl: './user-galleries-link.scss' + styleUrl: './user-galleries-link.scss', }) -export class UserGalleriesLink { - -} +export class UserGalleriesLink {} diff --git a/front/v2/src/app/services/userService/userService.ts b/front/v2/src/app/services/userService/userService.ts index fdb7035..4634ebc 100644 --- a/front/v2/src/app/services/userService/userService.ts +++ b/front/v2/src/app/services/userService/userService.ts @@ -39,9 +39,10 @@ export class userService { systemKey: encryptedSystemKey, }) .then((response) => { - const { jwt, refresh, usermodel } = response.data; - localStorage.setItem('jwt', jwt); - localStorage.setItem('refresh', refresh); + const { id, roles } = response.data; + const usermodel = new userModel(id, roles, true); + // localStorage.setItem('jwt', jwt); + // localStorage.setItem('refresh', refresh); this.setUser(usermodel); return usermodel; }); diff --git a/front/v2/src/models/roleModel.ts b/front/v2/src/models/roleModel.ts index 141f445..05ddb4d 100644 --- a/front/v2/src/models/roleModel.ts +++ b/front/v2/src/models/roleModel.ts @@ -8,6 +8,13 @@ export class roleModel { public permissions: permissionModel[] = [], public baseRoleModel: roleModel | null = null ) { + this.id = id; + this.name = name; + this.description = description; + this.permissions = []; + for (const p of permissions) { + this.permissions.push(new permissionModel(p.id, '', '')); + } if (baseRoleModel) { this.permissions = baseRoleModel.permissions.concat(this.permissions); } diff --git a/front/v2/src/models/userModel.ts b/front/v2/src/models/userModel.ts index 5ceebe4..4b41793 100644 --- a/front/v2/src/models/userModel.ts +++ b/front/v2/src/models/userModel.ts @@ -11,47 +11,59 @@ enum SigningMethods { Microsoft = 'microsoft', } -class userModel extends personModel { +class userModel { constructor( - public override id: string, - public email: string, - public password: string, - public override name: string, + public id: string, public role: roleModel[], - public createdAt: Date, - public updatedAt: Date, - public isLoggedIn: boolean, - public preferredSigningMethod: SigningMethods = SigningMethods.Password + public isLoggedIn: boolean ) { - super({ - id, - name, - profilePicture: null, - avatar: null, - socialMedia: null, - }); + this.id = id; + for (const r of role) { + this.role.push(new roleModel(r.id, '', '', r.permissions, null)); + } + this.isLoggedIn = isLoggedIn; } + // constructor( + // public override id: string, + // public email: string, + // public password: string, + // public override name: string, + // public role: roleModel[], + // public createdAt: Date, + // public updatedAt: Date, + // public isLoggedIn: boolean, + // public preferredSigningMethod: SigningMethods = SigningMethods.Password + // ) { + // super({ + // id, + // name, + // profilePicture: null, + // avatar: null, + // socialMedia: null, + // }); + // } + get isAdmin(): boolean { - return this.role.some((role) => role.isAdmin); + return this.role.some((r) => r.isAdmin); } get isContentManager(): boolean { - return this.role.some((role) => role.isContentManager); + return this.role.some((r) => r.isContentManager); } get isUser(): boolean { - return this.role.some((role) => role.isUser); + return this.role.some((r) => r.isUser); } public static readonly DefaultUser: userModel = new userModel( '0', - 'default@example.com', - '', - 'Default User', + // 'default@example.com', + // '', + // 'Default User', [roleModel.UserRole], - new Date(), - new Date(), + // new Date(), + // new Date(), false ); }