diff --git a/.gitignore b/.gitignore index b249336..da28481 100644 --- a/.gitignore +++ b/.gitignore @@ -480,4 +480,5 @@ $RECYCLE.BIN/ # Added by me .idea logs -LocalFileStorage \ No newline at end of file +LocalFileStorage +*.db* \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 6463e00..4d5ff92 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ false - net8.0 + net9.0 enable enable diff --git a/Dockerfile b/Dockerfile index 198e537..72f33e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base WORKDIR /app COPY ["./publish/", "./"] ENTRYPOINT ["dotnet", "Pandatech.VerticalSlices.dll"] \ No newline at end of file diff --git a/Dockerfile.Local b/Dockerfile.Local index fc4c4d4..d1b6311 100644 --- a/Dockerfile.Local +++ b/Dockerfile.Local @@ -1,9 +1,9 @@ -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base USER $APP_UID WORKDIR /app EXPOSE 80 -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /build COPY ["Directory.Build.props", "."] diff --git a/docker-compose.yml b/docker-compose.yml index 5787a18..15eee77 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,20 +1,20 @@ -version: '3.8' - volumes: - # elasticsearch: - # driver: local - # kibana: - # driver: local - # pgadmin: - # driver: local rabbitmq: driver: local redis: driver: local - redisinsight: - driver: local postgres: driver: local + elasticsearch: + driver: local + kibana: + driver: local + # pgadmin: + # driver: local + # redisinsight: + # driver: local + # oracle_db: + # driver: local services: @@ -44,68 +44,76 @@ services: volumes: - ~/.aspnet/https:/https:ro - ##################################################################################### - - # elasticsearch: - # container_name: pandatech_vertical_slices_elasticsearch - # image: docker.elastic.co/elasticsearch/elasticsearch:8.12.2 - # restart: always - # ports: - # - 9200:9200 - # environment: - # - bootstrap.memory_lock=true - # - discovery.type=single-node - # - xpack.security.enabled=false - # - xpack.security.http.ssl.enabled=false - # - xpack.security.transport.ssl.enabled=false - # ulimits: - # memlock: - # soft: -1 - # hard: -1 - # volumes: - # - elasticsearch:/usr/share/elasticsearch/data - # healthcheck: - # test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"] - # interval: 30s - # timeout: 10s - # retries: 5 - - ################################################################################# - - # kibana: - # depends_on: - # elasticsearch: - # condition: service_healthy - # container_name: pandatech_vertical_slices_kibana - # image: docker.elastic.co/kibana/kibana:8.12.2 - # restart: always - # ports: - # - 5601:5601 - # environment: - # - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 - # volumes: - # - kibana:/usr/share/kibana/data - - ################################a################################################### + # --------------------------------------------------------------------------------- + # Elastic search + # --------------------------------------------------------------------------------- + + elasticsearch: + container_name: elasticsearch + image: docker.elastic.co/elasticsearch/elasticsearch:8.16.1 + restart: always + ports: + - 9200:9200 + environment: + - bootstrap.memory_lock=true + - discovery.type=single-node + - xpack.security.enabled=false + - xpack.security.http.ssl.enabled=false + - xpack.security.transport.ssl.enabled=false + ulimits: + memlock: + soft: -1 + hard: -1 + volumes: + - elasticsearch:/usr/share/elasticsearch/data + healthcheck: + test: [ "CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1" ] + interval: 30s + timeout: 10s + retries: 5 + + # --------------------------------------------------------------------------------- + # Kibana + # --------------------------------------------------------------------------------- + + kibana: + depends_on: + elasticsearch: + condition: service_healthy + container_name: kibana + image: docker.elastic.co/kibana/kibana:8.16.1 + restart: always + ports: + - 5601:5601 + environment: + - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 + volumes: + - kibana:/usr/share/kibana/data + # --------------------------------------------------------------------------------- + # PgAdmin + # --------------------------------------------------------------------------------- + # pg-admin: - # container_name: pandatech_vertical_slices_pg_admin - # image: dpage/pgadmin4:8.4 + # container_name: pg_admin + # image: dpage/pgadmin4:latest # restart: always # ports: # - "5050:80" - # #in pgAdmin set Host name/address to host.docker.internal + # #in pgAdmin set Host name/address to host.docker.internal or postgres_db in linux # environment: # - PGADMIN_DEFAULT_EMAIL=test@pandatech.it # - PGADMIN_DEFAULT_PASSWORD=test # volumes: # - pgadmin:/var/lib/pgadmin - - ################################################################################### - + + # --------------------------------------------------------------------------------- + # RabbitMQ + # --------------------------------------------------------------------------------- + rabbitmq: - container_name: pandatech_vertical_slices_rabbitmq - image: rabbitmq:3.13.0-management-alpine + container_name: rabbitmq + image: rabbitmq:4-management-alpine restart: always ports: - "5672:5672" @@ -115,34 +123,40 @@ services: RABBITMQ_DEFAULT_PASS: test volumes: - rabbitmq:/var/lib/rabbitmq - - ################################################################################### - + + # --------------------------------------------------------------------------------- + # Redis + # --------------------------------------------------------------------------------- + redis: - container_name: pandatech_vertical_slices_redis - image: redis:7.2.4 + container_name: redis + image: redis:latest restart: always ports: - "6379:6379" volumes: - redis:/data - - ################################################################################### - - redisinsight: - container_name: pandatech_vertical_slices_redisinsight - image: redislabs/redisinsight:1.14.0 - restart: always - ports: - - "8001:8001" - volumes: - - redisinsight:/db - - ################################################################################### - + + # --------------------------------------------------------------------------------- + # Redis Insight + # --------------------------------------------------------------------------------- + + # redisinsight: + # container_name: redisinsight + # image: redis/redisinsight:latest + # restart: always + # ports: + # - "5540:5540" + # volumes: + # - redisinsight:/db + + # --------------------------------------------------------------------------------- + # Postgres + # --------------------------------------------------------------------------------- + postgres_db: - container_name: pandatech_vertical_slices_postgres - image: postgres:16.2 + container_name: postgres + image: postgres:latest restart: always environment: - POSTGRES_USER=test @@ -151,70 +165,44 @@ services: - "5432:5432" volumes: - postgres:/var/lib/postgresql/data - - #Check optimal configurations with PandaPostgres - command: - - "-c" - - "log_connections=on" - - "-c" - - "log_disconnections=on" - - "-c" - - "log_duration=on" - - "-c" - - "log_line_prefix=%m [%p-%l] %u@%d app=%a " - - "-c" - - "log_lock_waits=on" - - "-c" - - "log_min_duration_statement=1s" - - "-c" - - "log_statement=all" - - "-c" - - "log_timezone=Asia/Yerevan" - - "-c" - - "log_temp_files=0" - - "-c" - - "shared_preload_libraries=pg_stat_statements" - - "-c" - - "track_commit_timestamp=on" - - "-c" - - "track_functions=pl" - - "-c" - - "track_io_timing=on" - - "-c" - - "hot_standby_feedback=on" - - "-c" - - "TimeZone=Asia/Yerevan" - - "-c" - - "effective_io_concurrency=200" - - "-c" - - "cpu_tuple_cost=0.03" - - "-c" - - "random_page_cost=1.1" - - "-c" - - "max_connections=24" - - "-c" - - "idle_in_transaction_session_timeout=5min" - - "-c" - - "max_locks_per_transaction=128" - - "-c" - - "synchronous_commit=off" - - "-c" - - "default_transaction_read_only=off" - - "-c" - - "transaction_read_only=off" - - "-c" - - "archive_mode=on" - - "-c" - - "checkpoint_timeout=15min" - - "-c" - - "checkpoint_completion_target=0.9" - - "-c" - - "max_wal_senders=64" - - "-c" - - "wal_buffers=-1" - - "-c" - - "wal_compression=on" - - "-c" - - "wal_level=logical" - - "-c" - - "wal_log_hints=on" \ No newline at end of file + + # --------------------------------------------------------------------------------- + # Oracle + # --------------------------------------------------------------------------------- + + # oracle_db: + # container_name: oracle-free + # image: container-registry.oracle.com/database/free:latest + # restart: always + # ports: + # - "1521:1521" + # - "5500:5500" + # environment: + # - ORACLE_PWD=test + # volumes: + # - oracle_db:/opt/oracle/oradata + + # --------------------------------------------------------------------------------- + # Some .Net application + # --------------------------------------------------------------------------------- + + # pandawebapi: + # build: + # context: . + # dockerfile: Dockerfile.Local + # container_name: dotnet_application + # hostname: pandawebapi + # restart: always + # ports: + # - "80:80" # Change the ports as necessary for your application + # depends_on: + # - postgres_db + # - rabbitmq + # - redis + # - elasticsearch + # environment: + # - ASPNETCORE_ENVIRONMENT=Local + # - PANDAVAULT_URL=https://bevault.pandatech.it + # - PANDAVAULT_SECRET=0c579832-968e-463e-a69e-f9634332d29e + # volumes: + # - ~/.aspnet/https:/https:ro \ No newline at end of file diff --git a/global.json b/global.json index c96d045..2bc13e8 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.200", - "rollForward": "latestMajor" + "version": "9.0.100", + "rollForward": "latestMinor" } } diff --git a/src/Pandatech.VerticalSlices/AssemblyReference.cs b/src/Pandatech.VerticalSlices/AssemblyReference.cs index fd4e75e..0b998d0 100644 --- a/src/Pandatech.VerticalSlices/AssemblyReference.cs +++ b/src/Pandatech.VerticalSlices/AssemblyReference.cs @@ -1,3 +1,3 @@ namespace Pandatech.VerticalSlices; -public record AssemblyReference(); \ No newline at end of file +public record AssemblyReference; \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/Context/SeedDatabase/User/SystemUser.cs b/src/Pandatech.VerticalSlices/Context/SeedDatabase/User/SystemUser.cs index d854d57..211d8ad 100644 --- a/src/Pandatech.VerticalSlices/Context/SeedDatabase/User/SystemUser.cs +++ b/src/Pandatech.VerticalSlices/Context/SeedDatabase/User/SystemUser.cs @@ -1,8 +1,8 @@ using System.Collections; -using Pandatech.Crypto; +using Microsoft.EntityFrameworkCore; +using Pandatech.Crypto.Helpers; using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.SharedKernel.Extensions; -using Pandatech.VerticalSlices.SharedKernel.Helpers; namespace Pandatech.VerticalSlices.Context.SeedDatabase.User; @@ -14,27 +14,22 @@ public static WebApplication SeedSystemUser(this WebApplication app) var services = scope.ServiceProvider; var context = services.GetRequiredService(); var configuration = services.GetRequiredService(); - var argon2Id = services.GetRequiredService(); var username = configuration.GetSuperUsername(); - ValidateConfiguration(username, configuration.GetSuperuserPassword()); var normalizedUsername = username.ToLowerInvariant(); - var existingUsers = context.Users - .Where(u => u.Username == normalizedUsername || u.Role == UserRole.SuperAdmin) - .ToList(); - ValidateSuperUserUniqueness(existingUsers); + var existingUsers = context.Users + .Count(u => u.Username == normalizedUsername || u.Role == UserRole.SuperAdmin); - if (existingUsers.Count == 1) + if (existingUsers >= 1) { return app; } var userPassword = configuration.GetSuperuserPassword(); - ValidateConfiguration(userPassword, configuration.GetSuperuserPassword()); - var passwordHash = argon2Id.HashPassword(userPassword!); + var passwordHash = Argon2Id.HashPassword(userPassword); var newUser = CreateNewUser(normalizedUsername, passwordHash); context.Users.Add(newUser); @@ -43,22 +38,6 @@ public static WebApplication SeedSystemUser(this WebApplication app) return app; } - private static void ValidateConfiguration(string? configValue, string configName) - { - if (string.IsNullOrWhiteSpace(configValue)) - { - throw new ArgumentException($"{configName} is not set in appsettings.json"); - } - } - - private static void ValidateSuperUserUniqueness(ICollection users) - { - if (users.Count > 1) - { - throw new InvalidOperationException("There are multiple super users in the database."); - } - } - private static Domain.Entities.User CreateNewUser(string username, byte[] passwordHash) { return new Domain.Entities.User diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Api/AuthenticationEndpoints.cs b/src/Pandatech.VerticalSlices/Features/Auth/Api/AuthenticationEndpoints.cs index 635bfef..0b9333d 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Api/AuthenticationEndpoints.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Api/AuthenticationEndpoints.cs @@ -33,14 +33,14 @@ public void AddRoutes(IEndpointRouteBuilder app) groupApp.MapPost("/login", async (ISender sender, LoginCommand command, - IHttpContextAccessor httpContextAccessor, + HttpContext httpContext, IHostEnvironment environment, IConfiguration configuration, CancellationToken token) => { var response = await sender.Send(command, token); - var clientType = httpContextAccessor.HttpContext!.TryParseClientType() - .ConvertToEnum(); + var clientType = httpContext.TryParseClientType() + .ConvertToEnum(); if (clientType != ClientType.Browser) { @@ -48,7 +48,7 @@ public void AddRoutes(IEndpointRouteBuilder app) } var domain = configuration.GetCookieDomain(); - httpContextAccessor.HttpContext!.PrepareAndSetCookies(response, environment, domain); + httpContext.PrepareAndSetCookies(response, environment, domain); return TypedResults.Ok(response); }) @@ -60,16 +60,16 @@ public void AddRoutes(IEndpointRouteBuilder app) groupApp.MapPost("/refresh-token", async (ISender sender, - IHttpContextAccessor httpContextAccessor, + HttpContext httpContext, IHostEnvironment environment, IConfiguration configuration, CancellationToken token) => { var refreshTokenSignature = - httpContextAccessor.HttpContext!.TryParseRefreshTokenSignature(environment); + httpContext.TryParseRefreshTokenSignature(environment); var response = await sender.Send(new RefreshTokenCommand(refreshTokenSignature), token); - var clientType = httpContextAccessor.HttpContext!.TryParseClientType() - .ConvertToEnum(); + var clientType = httpContext.TryParseClientType() + .ConvertToEnum(); if (clientType != ClientType.Browser) { @@ -77,7 +77,7 @@ public void AddRoutes(IEndpointRouteBuilder app) } var domain = configuration.GetCookieDomain(); - httpContextAccessor.HttpContext!.PrepareAndSetCookies(response, environment, domain); + httpContext.PrepareAndSetCookies(response, environment, domain); return TypedResults.Ok(response); }) diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/Auth/AuthQuery.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/Auth/AuthQuery.cs index e1e640b..f10c208 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/Auth/AuthQuery.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/Auth/AuthQuery.cs @@ -1,5 +1,5 @@ using Pandatech.VerticalSlices.Domain.Enums; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.Auth; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/Auth/AuthQueryHandler.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/Auth/AuthQueryHandler.cs index 5093320..db4c5fc 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/Auth/AuthQueryHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/Auth/AuthQueryHandler.cs @@ -1,5 +1,5 @@ using Microsoft.EntityFrameworkCore; -using Pandatech.Crypto; +using Pandatech.Crypto.Helpers; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.Features.Auth.Contracts.Authenticate; @@ -8,6 +8,7 @@ using Pandatech.VerticalSlices.SharedKernel.Helpers; using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.Auth; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/CreateToken/CreateTokenCommand.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/CreateToken/CreateTokenCommand.cs index fbb8676..a9c5d96 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/CreateToken/CreateTokenCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/CreateToken/CreateTokenCommand.cs @@ -1,5 +1,5 @@ using Pandatech.VerticalSlices.Features.Auth.Contracts.CreateToken; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.CreateToken; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/CreateToken/CreateTokenCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/CreateToken/CreateTokenCommandHandler.cs index 76a4ff9..17950f7 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/CreateToken/CreateTokenCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/CreateToken/CreateTokenCommandHandler.cs @@ -1,9 +1,9 @@ -using Pandatech.Crypto; +using Pandatech.Crypto.Helpers; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Entities; using Pandatech.VerticalSlices.Features.Auth.Contracts.CreateToken; using Pandatech.VerticalSlices.Features.Auth.Helpers; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.CreateToken; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/IdentityState/GetIdentityStateQuery.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/IdentityState/GetIdentityStateQuery.cs index 48da578..1bd88de 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/IdentityState/GetIdentityStateQuery.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/IdentityState/GetIdentityStateQuery.cs @@ -1,5 +1,5 @@ using Pandatech.VerticalSlices.Features.Auth.Contracts.IdentityState; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.IdentityState; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/IdentityState/GetIdentityStateQueryHandler.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/IdentityState/GetIdentityStateQueryHandler.cs index b1d5ec3..078d966 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/IdentityState/GetIdentityStateQueryHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/IdentityState/GetIdentityStateQueryHandler.cs @@ -1,5 +1,6 @@ using Pandatech.VerticalSlices.Features.Auth.Contracts.IdentityState; using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.IdentityState; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/Login/LoginCommand.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/Login/LoginCommand.cs index ac9f428..181d3a7 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/Login/LoginCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/Login/LoginCommand.cs @@ -1,6 +1,14 @@ +using System.ComponentModel; using Pandatech.VerticalSlices.Features.Auth.Contracts.Login; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.Login; -public record LoginCommand(string Username, string Password) : ICommand; \ No newline at end of file +public class LoginCommand : ICommand +{ + [DefaultValue("admin@admin.com")] + public required string Username { get; set; } + + [DefaultValue("Qwertyui123@")] + public required string Password { get; set; } +} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/Login/LoginCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/Login/LoginCommandHandler.cs index a517909..ebf1e24 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/Login/LoginCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/Login/LoginCommandHandler.cs @@ -1,25 +1,27 @@ using MediatR; using Microsoft.EntityFrameworkCore; -using Pandatech.Crypto; +using Pandatech.Crypto.Helpers; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.Features.Auth.Application.CreateToken; using Pandatech.VerticalSlices.Features.Auth.Contracts.Login; using Pandatech.VerticalSlices.SharedKernel.Helpers; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.Login; -public class LoginCommandHandler(PostgresContext dbContext, Argon2Id argon2Id, ISender sender) +public class LoginCommandHandler(PostgresContext dbContext, ISender sender) : ICommandHandler { public async Task Handle(LoginCommand request, CancellationToken cancellationToken) { - var user = await dbContext.Users.FirstOrDefaultAsync(u => u.Username == request.Username.ToLower(), cancellationToken); + var user = await dbContext.Users.FirstOrDefaultAsync(u => u.Username == request.Username.ToLower(), + cancellationToken); + if (user is null || user.Status != UserStatus.Active || - !argon2Id.VerifyHash(request.Password, user.PasswordHash)) + !Argon2Id.VerifyHash(request.Password, user.PasswordHash)) { throw new BadRequestException(ErrorMessages.InvalidCredentials); } diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/RefreshToken/RefreshTokenCommand.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/RefreshToken/RefreshTokenCommand.cs index d9f23a6..464bb21 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/RefreshToken/RefreshTokenCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/RefreshToken/RefreshTokenCommand.cs @@ -1,5 +1,5 @@ using Pandatech.VerticalSlices.Features.Auth.Contracts.RefreshToken; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.RefreshToken; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/RefreshToken/RefreshTokenCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/RefreshToken/RefreshTokenCommandHandler.cs index 51ef921..71553e9 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/RefreshToken/RefreshTokenCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/RefreshToken/RefreshTokenCommandHandler.cs @@ -1,13 +1,13 @@ using Microsoft.EntityFrameworkCore; -using Pandatech.Crypto; +using Pandatech.Crypto.Helpers; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Entities; using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.Features.Auth.Contracts.RefreshToken; using Pandatech.VerticalSlices.Features.Auth.Helpers; using Pandatech.VerticalSlices.SharedKernel.Helpers; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.RefreshToken; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokens/RevokeAllTokensCommand.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokens/RevokeAllTokensCommand.cs index f0b1bd5..36b141e 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokens/RevokeAllTokensCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokens/RevokeAllTokensCommand.cs @@ -1,4 +1,4 @@ -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.RevokeAllTokens; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokens/RevokeAllTokensCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokens/RevokeAllTokensCommandHandler.cs index 46399bf..edaddff 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokens/RevokeAllTokensCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokens/RevokeAllTokensCommandHandler.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Pandatech.VerticalSlices.Context; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.RevokeAllTokens; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokensExceptCurrentSession/RevokeAllTokensExceptCurrentCommand.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokensExceptCurrentSession/RevokeAllTokensExceptCurrentCommand.cs index 508e155..3fcd0d6 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokensExceptCurrentSession/RevokeAllTokensExceptCurrentCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokensExceptCurrentSession/RevokeAllTokensExceptCurrentCommand.cs @@ -1,4 +1,4 @@ -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.RevokeAllTokensExceptCurrentSession; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokensExceptCurrentSession/RevokeAllTokensExceptCurrentCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokensExceptCurrentSession/RevokeAllTokensExceptCurrentCommandHandler.cs index 344bd84..4153755 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokensExceptCurrentSession/RevokeAllTokensExceptCurrentCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/RevokeAllTokensExceptCurrentSession/RevokeAllTokensExceptCurrentCommandHandler.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.RevokeAllTokensExceptCurrentSession; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/UpdatePasswordForced/UpdatePasswordForcedCommand.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/UpdatePasswordForced/UpdatePasswordForcedCommand.cs index a992eff..a9ef307 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/UpdatePasswordForced/UpdatePasswordForcedCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/UpdatePasswordForced/UpdatePasswordForcedCommand.cs @@ -1,4 +1,4 @@ -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.UpdatePasswordForced; diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Application/UpdatePasswordForced/UpdatePasswordForcedCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/Auth/Application/UpdatePasswordForced/UpdatePasswordForcedCommandHandler.cs index a1ae436..77e7322 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Application/UpdatePasswordForced/UpdatePasswordForcedCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Application/UpdatePasswordForced/UpdatePasswordForcedCommandHandler.cs @@ -1,19 +1,19 @@ using Hangfire; using MediatR; using Microsoft.EntityFrameworkCore; -using Pandatech.Crypto; +using Pandatech.Crypto.Helpers; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Features.Auth.Application.RevokeAllTokensExceptCurrentSession; using Pandatech.VerticalSlices.SharedKernel.Helpers; using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.Auth.Application.UpdatePasswordForced; public class UpdatePasswordForcedCommandHandler( IRequestContext requestContext, - PostgresContext dbContext, - Argon2Id argon2Id) + PostgresContext dbContext) : ICommandHandler { public async Task Handle(UpdatePasswordForcedCommand request, CancellationToken cancellationToken) @@ -23,11 +23,11 @@ public async Task Handle(UpdatePasswordForcedCommand request, CancellationToken InternalServerErrorException.ThrowIfNull(user, "User not found"); - var sameWithOldPassword = argon2Id.VerifyHash(request.NewPassword, user.PasswordHash); + var sameWithOldPassword = Argon2Id.VerifyHash(request.NewPassword, user.PasswordHash); BadRequestException.ThrowIf(sameWithOldPassword, ErrorMessages.NewPasswordMustBeDifferentFromOldPassword); - user.PasswordHash = argon2Id.HashPassword(request.NewPassword); + user.PasswordHash = Argon2Id.HashPassword(request.NewPassword); user.ForcePasswordChange = false; user.MarkAsUpdated(requestContext.Identity.UserId); diff --git a/src/Pandatech.VerticalSlices/Features/Auth/Helpers/CookieHelper.cs b/src/Pandatech.VerticalSlices/Features/Auth/Helpers/CookieHelper.cs index a668d1e..76b1549 100644 --- a/src/Pandatech.VerticalSlices/Features/Auth/Helpers/CookieHelper.cs +++ b/src/Pandatech.VerticalSlices/Features/Auth/Helpers/CookieHelper.cs @@ -1,7 +1,7 @@ using Pandatech.VerticalSlices.Features.Auth.Contracts.Authenticate; using Pandatech.VerticalSlices.Features.Auth.Contracts.Login; using Pandatech.VerticalSlices.Features.Auth.Contracts.RefreshToken; -using Pandatech.VerticalSlices.SharedKernel.Extensions; +using SharedKernel.Extensions; namespace Pandatech.VerticalSlices.Features.Auth.Helpers; diff --git a/src/Pandatech.VerticalSlices/Features/MyAccount/Api/MyAccountEndpoints.cs b/src/Pandatech.VerticalSlices/Features/MyAccount/Api/MyAccountEndpoints.cs index 4ad2700..ccd4b15 100644 --- a/src/Pandatech.VerticalSlices/Features/MyAccount/Api/MyAccountEndpoints.cs +++ b/src/Pandatech.VerticalSlices/Features/MyAccount/Api/MyAccountEndpoints.cs @@ -50,14 +50,14 @@ public void AddRoutes(IEndpointRouteBuilder app) groupApp.MapPost("/logout", async (ISender sender, - IHttpContextAccessor httpContextAccessor, + HttpContext httpContext, IHostEnvironment environment, IConfiguration configuration, CancellationToken token) => { var domain = configuration.GetCookieDomain(); await sender.Send(new LogoutCommand(), token); - httpContextAccessor.HttpContext!.DeleteAllCookies(environment, domain); + httpContext.DeleteAllCookies(environment, domain); return TypedResults.Ok(); }) .Authorize(UserRole.User) diff --git a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/Logout/LogoutCommand.cs b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/Logout/LogoutCommand.cs index 890b5a9..d119e79 100644 --- a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/Logout/LogoutCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/Logout/LogoutCommand.cs @@ -1,4 +1,4 @@ -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.MyAccount.Application.Logout; diff --git a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/Logout/LogoutCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/Logout/LogoutCommandHandler.cs index 8d2612e..3ad1488 100644 --- a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/Logout/LogoutCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/Logout/LogoutCommandHandler.cs @@ -2,6 +2,7 @@ using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.MyAccount.Application.Logout; diff --git a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/PersonalInformation/GetPersonalInformationQuery.cs b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/PersonalInformation/GetPersonalInformationQuery.cs index 0f0d05c..4916039 100644 --- a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/PersonalInformation/GetPersonalInformationQuery.cs +++ b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/PersonalInformation/GetPersonalInformationQuery.cs @@ -1,5 +1,5 @@ using Pandatech.VerticalSlices.Features.MyAccount.Contracts; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.MyAccount.Application.PersonalInformation; diff --git a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/PersonalInformation/GetPersonalInformationQueryHandler.cs b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/PersonalInformation/GetPersonalInformationQueryHandler.cs index 23c64e4..bcc9ac5 100644 --- a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/PersonalInformation/GetPersonalInformationQueryHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/PersonalInformation/GetPersonalInformationQueryHandler.cs @@ -1,5 +1,6 @@ using Pandatech.VerticalSlices.Features.MyAccount.Contracts; using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.MyAccount.Application.PersonalInformation; diff --git a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/UpdateOwnPassword/UpdateOwnPasswordCommand.cs b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/UpdateOwnPassword/UpdateOwnPasswordCommand.cs index 682591f..3209100 100644 --- a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/UpdateOwnPassword/UpdateOwnPasswordCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/UpdateOwnPassword/UpdateOwnPasswordCommand.cs @@ -1,4 +1,4 @@ -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.MyAccount.Application.UpdateOwnPassword; diff --git a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/UpdateOwnPassword/UpdateOwnPasswordCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/UpdateOwnPassword/UpdateOwnPasswordCommandHandler.cs index 36a095a..83aaba4 100644 --- a/src/Pandatech.VerticalSlices/Features/MyAccount/Application/UpdateOwnPassword/UpdateOwnPasswordCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/MyAccount/Application/UpdateOwnPassword/UpdateOwnPasswordCommandHandler.cs @@ -1,19 +1,19 @@ using Hangfire; using MediatR; using Microsoft.EntityFrameworkCore; -using Pandatech.Crypto; +using Pandatech.Crypto.Helpers; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.Features.Auth.Application.RevokeAllTokensExceptCurrentSession; using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.MyAccount.Application.UpdateOwnPassword; public class UpdateOwnPasswordCommandHandler( IRequestContext requestContext, - PostgresContext postgresContext, - Argon2Id argon2Id) + PostgresContext postgresContext) : ICommandHandler { public async Task Handle(UpdateOwnPasswordCommand request, CancellationToken cancellationToken) @@ -27,7 +27,7 @@ public async Task Handle(UpdateOwnPasswordCommand request, CancellationToken can InternalServerErrorException.ThrowIfNull(user, "User not found"); - user.PasswordHash = argon2Id.HashPassword(request.NewPassword); + user.PasswordHash = Argon2Id.HashPassword(request.NewPassword); user.MarkAsUpdated(requestContext.Identity.UserId); diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/Create/CreateUserCommand.cs b/src/Pandatech.VerticalSlices/Features/User/Application/Create/CreateUserCommand.cs index 33f7ba9..d42ce41 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/Create/CreateUserCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/Create/CreateUserCommand.cs @@ -1,5 +1,5 @@ using Pandatech.VerticalSlices.Domain.Enums; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.Create; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/Create/CreateUserCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/User/Application/Create/CreateUserCommandHandler.cs index 0fea587..74b3309 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/Create/CreateUserCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/Create/CreateUserCommandHandler.cs @@ -1,13 +1,14 @@ using Microsoft.EntityFrameworkCore; -using Pandatech.Crypto; +using Pandatech.Crypto.Helpers; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.SharedKernel.Helpers; using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.Create; -public class CreateUserCommandHandler(PostgresContext dbContext, Argon2Id argon, IRequestContext requestContext) +public class CreateUserCommandHandler(PostgresContext dbContext, IRequestContext requestContext) : ICommandHandler { public async Task Handle(CreateUserCommand request, CancellationToken cancellationToken) @@ -20,7 +21,7 @@ public async Task Handle(CreateUserCommand request, CancellationToken cancellati BadRequestException.ThrowIf(isDuplicateUsername, ErrorMessages.DuplicateUsername); - var passwordHash = argon.HashPassword(request.Password); + var passwordHash = Argon2Id.HashPassword(request.Password); var user = new Domain.Entities.User { Username = request.Username.ToLower(), diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/Delete/DeleteUsersCommand.cs b/src/Pandatech.VerticalSlices/Features/User/Application/Delete/DeleteUsersCommand.cs index 45dc698..fe1b2db 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/Delete/DeleteUsersCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/Delete/DeleteUsersCommand.cs @@ -1,4 +1,4 @@ -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.Delete; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/Delete/DeleteUsersCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/User/Application/Delete/DeleteUsersCommandHandler.cs index 93cdb4a..e02d06e 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/Delete/DeleteUsersCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/Delete/DeleteUsersCommandHandler.cs @@ -4,6 +4,7 @@ using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.Delete; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/GetColumnDistinctValues/GetUserColumnDistinctValuesQuery.cs b/src/Pandatech.VerticalSlices/Features/User/Application/GetColumnDistinctValues/GetUserColumnDistinctValuesQuery.cs index eab3b36..e0c4a38 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/GetColumnDistinctValues/GetUserColumnDistinctValuesQuery.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/GetColumnDistinctValues/GetUserColumnDistinctValuesQuery.cs @@ -1,5 +1,5 @@ using GridifyExtensions.Models; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.GetColumnDistinctValues; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/GetColumnDistinctValues/GetUserColumnDistinctValuesQueryHandler.cs b/src/Pandatech.VerticalSlices/Features/User/Application/GetColumnDistinctValues/GetUserColumnDistinctValuesQueryHandler.cs index 5e1121f..8a49b1f 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/GetColumnDistinctValues/GetUserColumnDistinctValuesQueryHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/GetColumnDistinctValues/GetUserColumnDistinctValuesQueryHandler.cs @@ -2,7 +2,7 @@ using GridifyExtensions.Models; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Enums; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.GetColumnDistinctValues; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/GetUser/GetUserQuery.cs b/src/Pandatech.VerticalSlices/Features/User/Application/GetUser/GetUserQuery.cs index 63b43a7..03674bd 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/GetUser/GetUserQuery.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/GetUser/GetUserQuery.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; using Pandatech.VerticalSlices.Features.User.Contracts.GetUser; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.GetUser; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/GetUser/GetUserQueryHandler.cs b/src/Pandatech.VerticalSlices/Features/User/Application/GetUser/GetUserQueryHandler.cs index ae3a683..14ff3f8 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/GetUser/GetUserQueryHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/GetUser/GetUserQueryHandler.cs @@ -2,8 +2,8 @@ using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.Features.User.Contracts.GetUser; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.GetUser; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/GetUsers/GetUsersQuery.cs b/src/Pandatech.VerticalSlices/Features/User/Application/GetUsers/GetUsersQuery.cs index c857419..87c6bbd 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/GetUsers/GetUsersQuery.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/GetUsers/GetUsersQuery.cs @@ -1,6 +1,6 @@ using GridifyExtensions.Models; using Pandatech.VerticalSlices.Features.User.Contracts.GetUser; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.GetUsers; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/GetUsers/GetUsersQueryHandler.cs b/src/Pandatech.VerticalSlices/Features/User/Application/GetUsers/GetUsersQueryHandler.cs index 4e6aee7..b842a5e 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/GetUsers/GetUsersQueryHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/GetUsers/GetUsersQueryHandler.cs @@ -3,7 +3,7 @@ using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.Features.User.Contracts.GetUser; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.GetUsers; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/Update/UpdateUserCommand.cs b/src/Pandatech.VerticalSlices/Features/User/Application/Update/UpdateUserCommand.cs index f32b70c..88663d6 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/Update/UpdateUserCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/Update/UpdateUserCommand.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; using Pandatech.VerticalSlices.Domain.Enums; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.Update; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/Update/UpdateUserCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/User/Application/Update/UpdateUserCommandHandler.cs index a620ce4..8da98a4 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/Update/UpdateUserCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/Update/UpdateUserCommandHandler.cs @@ -4,6 +4,7 @@ using Pandatech.VerticalSlices.SharedKernel.Helpers; using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.Update; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/UpdatePassword/UpdateUserPasswordCommand.cs b/src/Pandatech.VerticalSlices/Features/User/Application/UpdatePassword/UpdateUserPasswordCommand.cs index 4249feb..ae90406 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/UpdatePassword/UpdateUserPasswordCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/UpdatePassword/UpdateUserPasswordCommand.cs @@ -1,5 +1,5 @@ using System.Text.Json.Serialization; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.UpdatePassword; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/UpdatePassword/UpdateUserPasswordCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/User/Application/UpdatePassword/UpdateUserPasswordCommandHandler.cs index 8ae6f04..337eeb3 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/UpdatePassword/UpdateUserPasswordCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/UpdatePassword/UpdateUserPasswordCommandHandler.cs @@ -1,18 +1,18 @@ using Hangfire; using MediatR; using Microsoft.EntityFrameworkCore; -using Pandatech.Crypto; +using Pandatech.Crypto.Helpers; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.Features.Auth.Application.RevokeAllTokens; using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.UpdatePassword; public class UpdateUserPasswordCommandHandler( PostgresContext postgresContext, - Argon2Id argon2Id, IRequestContext requestContext) : ICommandHandler { @@ -25,7 +25,7 @@ public async Task Handle(UpdateUserPasswordCommand request, CancellationToken ca NotFoundException.ThrowIfNull(user); - user.PasswordHash = argon2Id.HashPassword(request.NewPassword); + user.PasswordHash = Argon2Id.HashPassword(request.NewPassword); user.ForcePasswordChange = true; user.MarkAsUpdated(requestContext.Identity.UserId); diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/UpdateStatus/UpdateUserStatusCommand.cs b/src/Pandatech.VerticalSlices/Features/User/Application/UpdateStatus/UpdateUserStatusCommand.cs index 5bfb113..4110303 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/UpdateStatus/UpdateUserStatusCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/UpdateStatus/UpdateUserStatusCommand.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; using Pandatech.VerticalSlices.Domain.Enums; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.UpdateStatus; diff --git a/src/Pandatech.VerticalSlices/Features/User/Application/UpdateStatus/UpdateUserStatusCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/User/Application/UpdateStatus/UpdateUserStatusCommandHandler.cs index 57478d4..933e267 100644 --- a/src/Pandatech.VerticalSlices/Features/User/Application/UpdateStatus/UpdateUserStatusCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/User/Application/UpdateStatus/UpdateUserStatusCommandHandler.cs @@ -3,6 +3,7 @@ using Pandatech.VerticalSlices.Domain.Enums; using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.HttpExceptions; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.User.Application.UpdateStatus; diff --git a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/CreateOrUpdate/CreateOrUpdateUserConfigCommand.cs b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/CreateOrUpdate/CreateOrUpdateUserConfigCommand.cs index b543bc9..ac58ff9 100644 --- a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/CreateOrUpdate/CreateOrUpdateUserConfigCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/CreateOrUpdate/CreateOrUpdateUserConfigCommand.cs @@ -1,4 +1,4 @@ -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.UserConfig.Application.CreateOrUpdate; diff --git a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/CreateOrUpdate/CreateOrUpdateUserConfigCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/CreateOrUpdate/CreateOrUpdateUserConfigCommandHandler.cs index c9ebfa3..8b9aecd 100644 --- a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/CreateOrUpdate/CreateOrUpdateUserConfigCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/CreateOrUpdate/CreateOrUpdateUserConfigCommandHandler.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.UserConfig.Application.CreateOrUpdate; diff --git a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Delete/DeleteUserConfigsCommand.cs b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Delete/DeleteUserConfigsCommand.cs index 35da3de..1208208 100644 --- a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Delete/DeleteUserConfigsCommand.cs +++ b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Delete/DeleteUserConfigsCommand.cs @@ -1,4 +1,4 @@ -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.UserConfig.Application.Delete; diff --git a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Delete/DeleteUserConfigsCommandHandler.cs b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Delete/DeleteUserConfigsCommandHandler.cs index 943d4df..34f542b 100644 --- a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Delete/DeleteUserConfigsCommandHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Delete/DeleteUserConfigsCommandHandler.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.UserConfig.Application.Delete; diff --git a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Get/GetUserConfigsQuery.cs b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Get/GetUserConfigsQuery.cs index 5cbe52d..69e17d8 100644 --- a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Get/GetUserConfigsQuery.cs +++ b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Get/GetUserConfigsQuery.cs @@ -1,4 +1,4 @@ -using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.UserConfig.Application.Get; diff --git a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Get/GetUserConfigsQueryHandler.cs b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Get/GetUserConfigsQueryHandler.cs index cbc70b6..fb5cbdf 100644 --- a/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Get/GetUserConfigsQueryHandler.cs +++ b/src/Pandatech.VerticalSlices/Features/UserConfig/Application/Get/GetUserConfigsQueryHandler.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.SharedKernel.Interfaces; +using SharedKernel.ValidatorAndMediatR; namespace Pandatech.VerticalSlices.Features.UserConfig.Application.Get; diff --git a/src/Pandatech.VerticalSlices/Pandatech.VerticalSlices.csproj b/src/Pandatech.VerticalSlices/Pandatech.VerticalSlices.csproj index 1d84698..36f081d 100644 --- a/src/Pandatech.VerticalSlices/Pandatech.VerticalSlices.csproj +++ b/src/Pandatech.VerticalSlices/Pandatech.VerticalSlices.csproj @@ -1,48 +1,26 @@ - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + <_ContentIncludedByDefault Remove="wwwroot\assets\css\panda-style.css" /> + <_ContentIncludedByDefault Remove="wwwroot\assets\images\favicon.svg" /> + <_ContentIncludedByDefault Remove="wwwroot\assets\images\logo-wording.svg" /> + <_ContentIncludedByDefault Remove="wwwroot\assets\images\logo.svg" /> + <_ContentIncludedByDefault Remove="wwwroot\assets\js\docs.js" /> diff --git a/src/Pandatech.VerticalSlices/Program.cs b/src/Pandatech.VerticalSlices/Program.cs index 4e68c92..15c51e8 100644 --- a/src/Pandatech.VerticalSlices/Program.cs +++ b/src/Pandatech.VerticalSlices/Program.cs @@ -1,68 +1,68 @@ using Communicator.Extensions; -using DistributedCache.Extensions; +using DistributedCache.Options; using FluentMinimalApiMapper; -using GridifyExtensions.Extensions; using MassTransit.PostgresOutbox.Extensions; +using Pandatech.Crypto.Extensions; using Pandatech.VerticalSlices.Context; using Pandatech.VerticalSlices.Context.SeedDatabase.User; +using Pandatech.VerticalSlices.Features.Auth.Contracts.Authenticate; using Pandatech.VerticalSlices.SharedKernel.Extensions; -using Pandatech.VerticalSlices.SharedKernel.Helpers; -using Pandatech.VerticalSlices.SharedKernel.SharedEndpoints; -using PandaVaultClient; +using Pandatech.VerticalSlices.SharedKernel.Interfaces; using ResponseCrafter.Enums; using ResponseCrafter.Extensions; +using SharedKernel.Extensions; +using SharedKernel.Helpers; +using SharedKernel.Logging; +using SharedKernel.OpenApi; +using SharedKernel.Postgres.Extensions; +using SharedKernel.Resilience; +using SharedKernel.ValidatorAndMediatR; var builder = WebApplication.CreateBuilder(args); builder.LogStartAttempt(); - -if (!builder.Environment.IsLocal()) -{ - builder.Configuration.AddPandaVault(); -} +AssemblyRegistry.Add(typeof(Program).Assembly); builder + .ConfigureWithPandaVault() .AddSerilog() - .AddHangfireServer() - .AddPostgresContext() - .AddPandaCrypto() - .AddMassTransit(typeof(Program).Assembly) - .AddHealthChecks() - .AddCors() - .RegisterAllServices() - .AddSwagger() .AddResponseCrafter(NamingConvention.ToSnakeCase) - .ConfigureOpenTelemetry() - .AddEndpoints() - .AddGridify() + .AddOpenApi() + .AddOpenTelemetry() + .AddMinimalApis(AssemblyRegistry.ToArray()) + .AddControllers(AssemblyRegistry.ToArray()) + .AddMediatrWithBehaviors(AssemblyRegistry.ToArray()) .AddResilienceDefaultPipeline() + .AddRedis(KeyPrefix.AssemblyNamePrefix) + .AddDistributedSignalR("DistributedSignalR") + .MapDefaultTimeZone() + .AddCors() + .AddPostgresContext(builder.Configuration.GetPostgresUrl()) + .AddMassTransit(AssemblyRegistry.ToArray()) + .AddAes256Key(builder.Configuration.GetAesKey()) .AddCommunicator() - .AddDistributedCache(options => - { - options.RedisConnectionString = builder.Configuration.GetRedisUrl(); - }) - .AddMediatrWithBehaviors(); + .AddHangfireServer() + .AddHealthChecks(); builder.Services.AddOutboxInboxServices(); -builder.Services.AddHttpContextAccessor(); -builder.Services.AddHttpClient(); -builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddScoped(); var app = builder.Build(); -app.UseStaticFiles(); app .UseRequestResponseLogging() .UseResponseCrafter() - .MigrateDatabase() + .UseCors() + .MapMinimalApis() + .MapHealthCheckEndpoints() + .MapPrometheusExporterEndpoints() + .MigrateDatabase() .EnsureHealthy() .UseHangfireServer() + .ClearAssemblyRegistry() + .UseOpenApi() .SeedSystemUser() - .UseCors() - .UseSwagger(app.Configuration); - -app.MapPandaEndpoints(); -app.MapEndpoints(); + .MapControllers(); app.LogStartSuccess(); app.Run(); @@ -72,8 +72,7 @@ //todo Rename application name using PascalCase (ex. PandatechWebsite). //todo After renaming application adjust namespaces in .csproj file and refactor namespaces in all files (hint: bulk IDE function). //todo Configure dockerfile using application name (ex. PandatechWebsite). -//todo Delete unrelated nuggets and services. For example you might not need RMQ or Redis in this project. Or you might not need some regex and etc. -//todo Delete health checks and other configs of unrelated services. For example you might not need RMQ or Redis in this project. +//todo Delete unrelated services. For example you might not need RMQ or Redis in this project. //todo Update all Nuget packages. -//todo Include all required configurations in appsettings{environment}.json. -//todo Update ReadMm.md file. \ No newline at end of file +//todo Change configurations (appsettings). +//todo Update ReadMe.md file. \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Behaviors/ValidationBehaviorWithResponse.cs b/src/Pandatech.VerticalSlices/SharedKernel/Behaviors/ValidationBehaviorWithResponse.cs deleted file mode 100644 index 9236c6d..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Behaviors/ValidationBehaviorWithResponse.cs +++ /dev/null @@ -1,37 +0,0 @@ -using FluentValidation; -using MediatR; -using ResponseCrafter.HttpExceptions; - -namespace Pandatech.VerticalSlices.SharedKernel.Behaviors; - -public class ValidationBehaviorWithResponse(IEnumerable> validators) - : IPipelineBehavior - where TRequest : IRequest -{ - public async Task Handle(TRequest request, - RequestHandlerDelegate next, - CancellationToken cancellationToken) - { - if (!validators.Any()) - { - return await next(); - } - - var context = new ValidationContext(request); - - var validationResults = await Task.WhenAll(validators.Select(v => v.ValidateAsync(context, cancellationToken))); - var failures = validationResults.SelectMany(r => r.Errors) - .Where(f => f != null) - .ToList(); - - if (failures.Count == 0) - { - return await next(); - } - - var errors = failures.GroupBy(e => e.PropertyName, e => e.ErrorMessage) - .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.First()); - - throw new BadRequestException(errors); - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Behaviors/ValidationBehaviorWithoutResponse.cs b/src/Pandatech.VerticalSlices/SharedKernel/Behaviors/ValidationBehaviorWithoutResponse.cs deleted file mode 100644 index 6ee6ca3..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Behaviors/ValidationBehaviorWithoutResponse.cs +++ /dev/null @@ -1,37 +0,0 @@ -using FluentValidation; -using MediatR; -using ResponseCrafter.HttpExceptions; - -namespace Pandatech.VerticalSlices.SharedKernel.Behaviors; - -public class ValidationBehaviorWithoutResponse(IEnumerable> validators) - : IPipelineBehavior - where TRequest : IRequest -{ - public async Task Handle(TRequest request, - RequestHandlerDelegate next, - CancellationToken cancellationToken) - { - if (!validators.Any()) - { - return await next(); - } - - var context = new ValidationContext(request); - - var validationResults = await Task.WhenAll(validators.Select(v => v.ValidateAsync(context, cancellationToken))); - var failures = validationResults.SelectMany(r => r.Errors) - .Where(f => f != null) - .ToList(); - - if (failures.Count == 0) - { - return await next(); - } - - var errors = failures.GroupBy(e => e.PropertyName, e => e.ErrorMessage) - .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.First()); - - throw new BadRequestException(errors); - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Configurations/Options/SwaggerOptions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Configurations/Options/SwaggerOptions.cs deleted file mode 100644 index 4b4bdb0..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Configurations/Options/SwaggerOptions.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Pandatech.VerticalSlices.SharedKernel.Configurations.Options; - -public class SwaggerOptions -{ - public required Dictionary Versions { get; set; } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Configurations/Options/SwaggerVersionOptions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Configurations/Options/SwaggerVersionOptions.cs deleted file mode 100644 index da2e383..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Configurations/Options/SwaggerVersionOptions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Pandatech.VerticalSlices.SharedKernel.Configurations.Options; - -public class SwaggerVersionOptions -{ - public required string Title { get; set; } - public required string Description { get; set; } - - public bool Separate { get; set; } - public string? RoutePrefix { get; set; } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/ConfigurationExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/ConfigurationExtensions.cs index f723e12..cff454c 100644 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/ConfigurationExtensions.cs +++ b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/ConfigurationExtensions.cs @@ -3,29 +3,15 @@ namespace Pandatech.VerticalSlices.SharedKernel.Extensions; public static class ConfigurationExtensions { private const string AesKeyConfigurationPath = "Security:AESKey"; - private const string RedisConfigurationPath = "Redis"; private const string RabbitMqConfigurationPath = "RabbitMq"; private const string PostgresConfigurationPath = "Postgres"; - private const string CorsOriginsConfigurationPath = "Security:AllowedCorsOrigins"; private const string HangfireUserConfigurationPath = "Security:Hangfire:Username"; private const string HangfirePasswordConfigurationPath = "Security:Hangfire:Password"; private const string SuperUsernameConfigurationPath = "Security:SuperUser:Username"; private const string SuperUserPasswordConfigurationPath = "Security:SuperUser:Password"; private const string PersistentConfigurationPath = "PersistentStorage"; - private const string RepositoryNameConfigurationPath = "RepositoryName"; private const string CookieDomainConfigurationPath = "Security:CookieDomain"; - public static string GetFileStoragePath(this IConfiguration configuration) - { - var persistencePath = configuration.GetPersistentPath(); - return $"{persistencePath}/files"; - } - - public static string GetRepositoryName(this IConfiguration configuration) - { - return configuration[RepositoryNameConfigurationPath]!; - } - public static string GetPersistentPath(this IConfiguration configuration) { return configuration.GetConnectionString(PersistentConfigurationPath)!; @@ -36,11 +22,6 @@ public static string GetAesKey(this IConfiguration configuration) return configuration[AesKeyConfigurationPath]!; } - public static string GetRedisUrl(this IConfiguration configuration) - { - return configuration.GetConnectionString(RedisConfigurationPath)!; - } - public static string GetRabbitMqUrl(this IConfiguration configuration) { return configuration.GetConnectionString(RabbitMqConfigurationPath)!; @@ -50,17 +31,10 @@ public static string GetPostgresUrl(this IConfiguration configuration) { return configuration.GetConnectionString(PostgresConfigurationPath)!; } - - public static string GetAllowedCorsOrigins(this IConfiguration configuration) - { - return configuration[CorsOriginsConfigurationPath]!; - } - public static string GetHangfireUsername(this IConfiguration configuration) { return configuration[HangfireUserConfigurationPath]!; } - public static string GetHangfirePassword(this IConfiguration configuration) { return configuration[HangfirePasswordConfigurationPath]!; @@ -75,7 +49,7 @@ public static string GetSuperuserPassword(this IConfiguration configuration) { return configuration[SuperUserPasswordConfigurationPath]!; } - + public static string GetCookieDomain(this IConfiguration configuration) { return configuration[CookieDomainConfigurationPath]!; diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/CorsExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/CorsExtensions.cs deleted file mode 100644 index badbef0..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/CorsExtensions.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Pandatech.VerticalSlices.SharedKernel.Helpers; -using RegexBox; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class CorsExtension -{ - public static WebApplicationBuilder AddCors(this WebApplicationBuilder builder) - { - if (builder.Environment.IsProduction()) - { - var allowedOrigins = builder.Configuration - .GetAllowedCorsOrigins() - .SplitOrigins() - .EnsureWwwAndNonWwwVersions(); - - builder.Services.AddCors(options => options.AddPolicy("AllowSpecific", - p => p - .WithOrigins(allowedOrigins) - .AllowCredentials() - .AllowAnyMethod() - .AllowAnyHeader())); - } - else - { - builder.Services.AddCors(options => options.AddPolicy("AllowAll", - p => p - .SetIsOriginAllowed(_ => true) - .AllowCredentials() - .AllowAnyMethod() - .AllowAnyHeader())); - } - - return builder; - } - - public static WebApplication UseCors(this WebApplication app) - { - app.UseCors(app.Environment.IsProduction() ? "AllowSpecific" : "AllowAll"); - return app; - } - - private static string[] SplitOrigins(this string input) - { - if (string.IsNullOrEmpty(input)) - { - throw new ArgumentException("Cors Origins cannot be null or empty."); - } - - var result = input.Split([';', ','], StringSplitOptions.RemoveEmptyEntries); - - for (var i = 0; i < result.Length; i++) - { - result[i] = result[i] - .Trim(); - - if (PandaValidator.IsUri(result[i], false)) - { - continue; - } - - Console.WriteLine($"Removed invalid cors origin: {result[i]}"); - result[i] = string.Empty; - } - - return result.Where(x => !string.IsNullOrEmpty(x)) - .ToArray(); - } - - private static string[] EnsureWwwAndNonWwwVersions(this string[] uris) - { - var result = new HashSet(StringComparer.OrdinalIgnoreCase); - - foreach (var uri in uris) - { - if (!Uri.TryCreate(uri, UriKind.Absolute, out var parsedUri)) - { - continue; - } - - var uriString = parsedUri.ToString() - .TrimEnd('/'); - - result.Add(uriString); - - - var hostWithoutWww = parsedUri.Host.StartsWith("www.") - ? parsedUri.Host.Substring(4) - : parsedUri.Host; - - var uriWithoutWww = new UriBuilder(parsedUri) - { - Host = hostWithoutWww - }.Uri - .ToString() - .TrimEnd('/'); - - var uriWithWww = new UriBuilder(parsedUri) - { - Host = "www." + hostWithoutWww - }.Uri - .ToString() - .TrimEnd('/'); - - result.Add(uriWithoutWww); - result.Add(uriWithWww); - } - - return new List(result).ToArray(); - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/CryptoExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/CryptoExtensions.cs deleted file mode 100644 index 3a2b1ea..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/CryptoExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Pandatech.Crypto; -using Pandatech.VerticalSlices.SharedKernel.Helpers; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class CryptoExtensions -{ - public static WebApplicationBuilder AddPandaCrypto(this WebApplicationBuilder builder) - { - builder.Services.AddPandatechCryptoAes256(o => o.Key = builder.Configuration.GetAesKey()); - builder.Services.AddPandatechCryptoArgon2Id(); - - return builder; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/DatabaseExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/DatabaseExtensions.cs deleted file mode 100644 index 9be48cd..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/DatabaseExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using EFCore.PostgresExtensions.Extensions; -using EntityFramework.Exceptions.PostgreSQL; -using Microsoft.EntityFrameworkCore; -using Pandatech.VerticalSlices.Context; -using Pandatech.VerticalSlices.SharedKernel.Helpers; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class DatabaseExtensions -{ - public static WebApplicationBuilder AddPostgresContext(this WebApplicationBuilder builder) - { - var configuration = builder.Configuration; - - var connectionString = configuration.GetPostgresUrl(); - builder.Services.AddDbContextPool(options => - options.UseNpgsql(connectionString) - .UseQueryLocks() - .UseExceptionProcessor() - .UseSnakeCaseNamingConvention()); - return builder; - } - - public static WebApplication MigrateDatabase(this WebApplication app) - { - using var scope = app.Services.CreateScope(); - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.Database.Migrate(); - return app; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HangfireDashboardExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HangfireDashboardExtensions.cs deleted file mode 100644 index d9ae9f9..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HangfireDashboardExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Hangfire; -using HangfireBasicAuthenticationFilter; -using Pandatech.VerticalSlices.SharedKernel.Helpers; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class HangfireDashboardExtensions -{ - public static WebApplication UseHangfireServer(this WebApplication app) - { - var user = app.Configuration.GetHangfireUsername(); - var pass = app.Configuration.GetHangfirePassword(); - - app.UseHangfireDashboard("/hangfire", - new DashboardOptions - { - DashboardTitle = "JobMaster Dashboard", - Authorization = - [ - new HangfireCustomBasicAuthenticationFilter - { - User = user, - Pass = pass - } - ] - }); - app.MapHangfireDashboard(); - - return app; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HangfireServerExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HangfireExtensions.cs similarity index 51% rename from src/Pandatech.VerticalSlices/SharedKernel/Extensions/HangfireServerExtensions.cs rename to src/Pandatech.VerticalSlices/SharedKernel/Extensions/HangfireExtensions.cs index d2d3c3a..40a9ad2 100644 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HangfireServerExtensions.cs +++ b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HangfireExtensions.cs @@ -1,9 +1,10 @@ using Hangfire; using Hangfire.PostgreSql; +using HangfireBasicAuthenticationFilter; namespace Pandatech.VerticalSlices.SharedKernel.Extensions; -public static class HangfireServerExtensions +public static class HangfireExtensions { public static WebApplicationBuilder AddHangfireServer(this WebApplicationBuilder builder) { @@ -19,4 +20,27 @@ public static WebApplicationBuilder AddHangfireServer(this WebApplicationBuilder builder.Services.AddHangfireServer(); return builder; } + + public static WebApplication UseHangfireServer(this WebApplication app) + { + var user = app.Configuration.GetHangfireUsername(); + var pass = app.Configuration.GetHangfirePassword(); + + app.UseHangfireDashboard("/hangfire", + new DashboardOptions + { + DashboardTitle = "JobMaster Dashboard", + Authorization = + [ + new HangfireCustomBasicAuthenticationFilter + { + User = user, + Pass = pass + } + ] + }); + app.MapHangfireDashboard(); + + return app; + } } \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HealthCheckBuilderExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HealthCheckBuilderExtensions.cs deleted file mode 100644 index 536341e..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HealthCheckBuilderExtensions.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Pandatech.VerticalSlices.SharedKernel.Helpers; -using RabbitMQ.Client; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class HealthCheckBuilderExtensions -{ - public static WebApplicationBuilder AddHealthChecks(this WebApplicationBuilder builder) - { - var configuration = builder.Configuration; - var timeoutSeconds = TimeSpan.FromSeconds(5); - var postgresConnectionString = configuration.GetPostgresUrl(); - var redisConnectionString = configuration.GetRedisUrl(); - var rabbitMqUri = configuration.GetRabbitMqUrl(); - - //This part is only for RMQ health check - ConnectionFactory factory = new() - { - Uri = new Uri(rabbitMqUri) - }; - var connection = factory.CreateConnection(); - - - if (builder.Environment.IsLocal()) - { - builder.Services - .AddSingleton(connection) - .AddHealthChecks() - .AddRabbitMQ(name: "rabbit_mq") - .AddNpgSql(postgresConnectionString, timeout: timeoutSeconds, name: "postgres") - .AddRedis(redisConnectionString, timeout: timeoutSeconds); - } - - else if (builder.Environment.IsProduction()) - { - builder.Services - .AddSingleton(connection) - .AddHealthChecks() - .AddNpgSql(postgresConnectionString, timeout: timeoutSeconds, name: "postgres") - .AddRedis(redisConnectionString, timeout: timeoutSeconds) - .AddRabbitMQ(); - } - else - { - builder.Services - .AddSingleton(connection) - .AddHealthChecks() - .AddNpgSql(postgresConnectionString, timeout: timeoutSeconds, name: "postgres") - .AddRedis(redisConnectionString, timeout: timeoutSeconds) - .AddRabbitMQ(); - } - - return builder; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HealthCheckRunnerExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HealthCheckRunnerExtensions.cs deleted file mode 100644 index 1ebaa88..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HealthCheckRunnerExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.Extensions.Diagnostics.HealthChecks; -using ResponseCrafter.HttpExceptions; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class HealthCheckRunnerExtensions -{ - public static WebApplication EnsureHealthy(this WebApplication app) - { - var healthCheckService = app.Services.GetRequiredService(); - var report = healthCheckService.CheckHealthAsync() - .Result; - - // "masstransit-bus" entry is only becoming healthy after app.run - var relevantEntries = report.Entries - .Where(e => e.Key != "masstransit-bus") - .ToList(); - - // Determine overall status based on filtered entries - var overallStatus = relevantEntries.Exists(e => e.Value.Status == HealthStatus.Unhealthy) - ? HealthStatus.Unhealthy - : HealthStatus.Healthy; - - if (overallStatus != HealthStatus.Unhealthy) - { - return app; - } - - var unhealthyChecks = relevantEntries - .Where(e => e.Value.Status != HealthStatus.Healthy) - .Select(e => $"{e.Key}: {e.Value.Status}") - .ToList(); - - if (unhealthyChecks.Count == 0) - { - return app; - } - - var message = $"Unhealthy services detected: {string.Join(", ", unhealthyChecks)}"; - throw new ServiceUnavailableException(message); - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HostEnvironmentExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HostEnvironmentExtensions.cs deleted file mode 100644 index 74719c1..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/HostEnvironmentExtensions.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class HostEnvironmentExtensions -{ - public static bool IsQa(this IHostEnvironment hostEnvironment) - { - ArgumentNullException.ThrowIfNull(hostEnvironment); - - return hostEnvironment.IsEnvironment("QA"); - } - - public static bool IsLocal(this IHostEnvironment hostEnvironment) - { - ArgumentNullException.ThrowIfNull(hostEnvironment); - - return hostEnvironment.IsEnvironment("Local"); - } - - public static bool IsLocalOrDevelopment(this IHostEnvironment hostEnvironment) - { - ArgumentNullException.ThrowIfNull(hostEnvironment); - - return hostEnvironment.IsLocal() || hostEnvironment.IsDevelopment(); - } - - public static bool IsLocalOrDevelopmentOrQa(this IHostEnvironment hostEnvironment) - { - ArgumentNullException.ThrowIfNull(hostEnvironment); - - return hostEnvironment.IsLocal() || hostEnvironment.IsDevelopment() || hostEnvironment.IsQa(); - } - - public static string GetShortEnvironmentName(this IHostEnvironment environment) - { - ArgumentNullException.ThrowIfNull(environment); - - if (environment.IsLocal()) - { - return "local"; - } - - if (environment.IsDevelopment()) - { - return "dev"; - } - - if (environment.IsQa()) - { - return "qa"; - } - - if (environment.IsStaging()) - { - return "staging"; - } - - return ""; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/LoggingExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/LoggingExtensions.cs deleted file mode 100644 index 21605ba..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/LoggingExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Pandatech.VerticalSlices.SharedKernel.Middlewares; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class LoggingExtensions -{ - public static WebApplication UseRequestResponseLogging(this WebApplication app) - { - if (app.Logger.IsEnabled(LogLevel.Information)) - { - app.UseMiddleware(); - } - - return app; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/MassTransitExtension.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/MassTransitExtension.cs index e9caf1d..f2288e8 100644 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/MassTransitExtension.cs +++ b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/MassTransitExtension.cs @@ -1,6 +1,7 @@ using System.Reflection; using MassTransit; -using Pandatech.VerticalSlices.SharedKernel.Helpers; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using RabbitMQ.Client; namespace Pandatech.VerticalSlices.SharedKernel.Extensions; @@ -15,12 +16,41 @@ public static WebApplicationBuilder AddMassTransit(this WebApplicationBuilder bu x.UsingRabbitMq((context, cfg) => { - cfg.Host(builder.Configuration.GetConnectionString(builder.Configuration.GetRabbitMqUrl())); + cfg.Host(builder.Configuration.GetRabbitMqUrl()); cfg.ConfigureEndpoints(context); cfg.UseMessageRetry(r => r.Exponential(5, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(2))); }); }); + + builder + .Services + .AddHealthChecks() + .AddCheck("rabbit_mq", timeout: TimeSpan.FromSeconds(3)); + return builder; } +} + +public class RabbitMqHealthCheck(IConfiguration configuration) : IHealthCheck +{ + public async Task CheckHealthAsync(HealthCheckContext context, + CancellationToken cancellationToken = new()) + { + var rmqConnectionString = configuration.GetRabbitMqUrl(); + var factory = new ConnectionFactory + { + Uri = new Uri(rmqConnectionString), + AutomaticRecoveryEnabled = true + }; + try + { + await factory.CreateConnectionAsync(cancellationToken); + return HealthCheckResult.Healthy("RabbitMQ is healthy."); + } + catch (Exception e) + { + return HealthCheckResult.Unhealthy("RabbitMQ is unhealthy.", e); + } + } } \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/MediatRExtension.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/MediatRExtension.cs deleted file mode 100644 index 1400984..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/MediatRExtension.cs +++ /dev/null @@ -1,18 +0,0 @@ -using FluentValidation; -using MediatR; -using Pandatech.VerticalSlices.SharedKernel.Behaviors; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class MediatrExtension -{ - public static WebApplicationBuilder AddMediatrWithBehaviors(this WebApplicationBuilder builder) - { - var assembly = typeof(Program).Assembly; - builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(assembly)); - builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviorWithoutResponse<,>)); - builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviorWithResponse<,>)); - builder.Services.AddValidatorsFromAssembly(assembly); - return builder; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/OpenTelemetryExtension.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/OpenTelemetryExtension.cs deleted file mode 100644 index 3bc14fd..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/OpenTelemetryExtension.cs +++ /dev/null @@ -1,35 +0,0 @@ -using OpenTelemetry.Metrics; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class OpenTelemetryExtension -{ - public static WebApplicationBuilder ConfigureOpenTelemetry(this WebApplicationBuilder builder) - { - builder.Logging.AddOpenTelemetry(x => - { - x.IncludeScopes = true; - x.IncludeFormattedMessage = true; - }); - - builder.Services - .AddOpenTelemetry() - .ConfigureResource(resource => resource.AddService(builder.Environment.ApplicationName)) - .WithMetrics(metrics => - { - metrics.AddRuntimeInstrumentation() - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddPrometheusExporter(); - }) - .WithTracing(tracing => - { - tracing.AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation(); - }); - - return builder; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/RegisterServicesExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/RegisterServicesExtensions.cs deleted file mode 100644 index 3803975..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/RegisterServicesExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Pandatech.VerticalSlices.Features.Auth.Contracts.Authenticate; -using Pandatech.VerticalSlices.SharedKernel.Helpers; -using Pandatech.VerticalSlices.SharedKernel.Interfaces; -using PandaVaultClient; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class RegisterServicesExtensions -{ - public static WebApplicationBuilder RegisterAllServices(this WebApplicationBuilder builder) - { - builder.AddServices(); - builder.RegisterPandaVaultEndpoint(); //optional - - - return builder; - } - - private static WebApplicationBuilder AddServices(this WebApplicationBuilder builder) - { - if (builder.Environment.IsLocal()) - { - builder.Services.AddSingleton(); - } - - - builder.Services.AddScoped(); - return builder; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/ResilienceExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/ResilienceExtensions.cs deleted file mode 100644 index 5b3331f..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/ResilienceExtensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.Extensions.Http.Resilience; -using Pandatech.VerticalSlices.SharedKernel.Helpers; -using Polly; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class ResilienceExtensions -{ - public static WebApplicationBuilder AddResilienceDefaultPipeline(this WebApplicationBuilder builder) - { - builder.Services.AddResiliencePipeline(ResilienceDefaultPipelineProvider.DefaultPipelineName, - pipelineBuilder => - { - pipelineBuilder.AddRetry(ResilienceDefaultPipelineProvider.DefaultNetworkRetryOptions) - .AddRetry(ResilienceDefaultPipelineProvider.TooManyRequestsRetryOptions) - .AddCircuitBreaker(ResilienceDefaultPipelineProvider.DefaultCircuitBreakerOptions) - .AddTimeout(TimeSpan.FromSeconds(8)); - }); - return builder; - } - - public static IHttpResiliencePipelineBuilder AddResilienceDefaultPipeline(this IHttpClientBuilder builder) - { - return builder.AddResilienceHandler("DefaultPipeline", - resilienceBuilder => - { - resilienceBuilder.AddRetry(ResilienceHttpOptions.DefaultTooManyRequestsRetryOptions) - .AddRetry(ResilienceHttpOptions.DefaultNetworkRetryOptions) - .AddCircuitBreaker(ResilienceHttpOptions.DefaultCircuitBreakerOptions) - .AddTimeout(TimeSpan.FromSeconds(8)); - }); - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/SerilogExtension.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/SerilogExtension.cs deleted file mode 100644 index 1f95332..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/SerilogExtension.cs +++ /dev/null @@ -1,115 +0,0 @@ -using Elastic.CommonSchema.Serilog; -using Pandatech.VerticalSlices.SharedKernel.Helpers; -using Serilog; -using Serilog.Configuration; -using Serilog.Events; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class SerilogExtension -{ - public static WebApplicationBuilder AddSerilog(this WebApplicationBuilder builder) - { - builder.Logging.ClearProviders(); - - - var loggerConfig = new LoggerConfiguration() - .FilterOutUnwantedLogs() - .Enrich - .FromLogContext() - .ConfigureEnvironmentSpecificSettings(builder) - .ReadFrom - .Configuration(builder.Configuration); - - Log.Logger = loggerConfig.CreateLogger(); - builder.Logging.AddSerilog(); - builder.Services.AddSingleton(Log.Logger); - - builder.Host.UseSerilog(); - return builder; - } - - private static LoggerConfiguration ConfigureEnvironmentSpecificSettings(this LoggerConfiguration loggerConfig, - WebApplicationBuilder builder) - { - if (builder.Environment.IsLocal()) - { - loggerConfig - .WriteTo - .Console(); - } - else if (builder.Environment.IsDevelopment()) - { - loggerConfig - .WriteTo - .Console() - .WriteTo - .File(builder); - } - else - { - loggerConfig - .WriteTo - .File(builder); - } - - return loggerConfig; - } - - private static void File(this LoggerSinkConfiguration loggerConfig, WebApplicationBuilder builder) - { - loggerConfig - .File(new EcsTextFormatter(), - builder.GetLogsPath(), - rollingInterval: RollingInterval.Day); - } - - private static LoggerConfiguration FilterOutUnwantedLogs(this LoggerConfiguration loggerConfig) - { - loggerConfig - .Filter - .ByExcluding(logEvent => logEvent.ShouldExcludeHangfireDashboardLogs()) - .Filter - .ByExcluding(logEvent => logEvent.ShouldExcludeOutboxDbCommandLogs()) - .Filter - .ByExcluding(logEvent => logEvent.ShouldExcludeSwaggerLogs()); - return loggerConfig; - } - - private static bool ShouldExcludeHangfireDashboardLogs(this LogEvent logEvent) - { - return logEvent.Properties.TryGetValue("RequestPath", out var requestPathValue) - && requestPathValue is ScalarValue requestPath - && requestPath - .Value - ?.ToString() - ?.Contains("/hangfire") == true; - } - - private static bool ShouldExcludeOutboxDbCommandLogs(this LogEvent logEvent) - { - var message = logEvent.RenderMessage(); - return message.Contains("outbox_messages") || message.Contains("OutboxMessages"); - } - - private static bool ShouldExcludeSwaggerLogs(this LogEvent logEvent) - { - return logEvent.Properties.TryGetValue("RequestPath", out var requestPathValue) - && requestPathValue is ScalarValue requestPath - && requestPath - .Value - ?.ToString() - ?.Contains("/swagger") == true; - } - - private static string GetLogsPath(this WebApplicationBuilder builder) - { - var persistencePath = builder.Configuration.GetPersistentPath(); - var repoName = builder.Configuration.GetRepositoryName(); - var envName = builder.Environment.GetShortEnvironmentName(); - var instanceId = Guid.NewGuid() - .ToString(); - var fileName = $"logs-{instanceId}-.json"; - return Path.Combine(persistencePath, repoName, envName, "logs", fileName); - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/StartupLoggerExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/StartupLoggerExtensions.cs deleted file mode 100644 index 79cf284..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/StartupLoggerExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using Newtonsoft.Json; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class StartupLoggerExtensions -{ - private static readonly Stopwatch Stopwatch = new(); - - public static WebApplicationBuilder LogStartAttempt(this WebApplicationBuilder builder) - { - Stopwatch.Start(); - var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); - Console.WriteLine(JsonConvert.SerializeObject(new - { - Timestamp = now, - Event = "ApplicationStartAttempt", - Application = builder.Environment.ApplicationName, - Environment = builder.Environment.EnvironmentName - })); - return builder; - } - - public static WebApplication LogStartSuccess(this WebApplication app) - { - Stopwatch.Stop(); - var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); - var initializationTime = Math.Round(Stopwatch.Elapsed.TotalMilliseconds / 1000, 2); - Console.WriteLine(JsonConvert.SerializeObject(new - { - Timestamp = now, - Event = "ApplicationStartSuccess", - InitializationTime = $"{initializationTime} seconds" - })); - - return app; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/StringExtensions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/StringExtensions.cs deleted file mode 100644 index ea583b2..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/StringExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class StringExtensions -{ - public static string RemovePhoneFormatParenthesesAndAdditionSign(this string phoneString) - { - return phoneString.Replace("(", "") - .Replace(")", "") - .Replace("+", ""); - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/SwaggerExtension.cs b/src/Pandatech.VerticalSlices/SharedKernel/Extensions/SwaggerExtension.cs deleted file mode 100644 index d5ed29d..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Extensions/SwaggerExtension.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Microsoft.OpenApi.Models; -using Pandatech.VerticalSlices.SharedKernel.Configurations.Options; -using Pandatech.VerticalSlices.SharedKernel.Helpers; -using Swashbuckle.AspNetCore.SwaggerGen; -using Swashbuckle.AspNetCore.SwaggerUI; - -namespace Pandatech.VerticalSlices.SharedKernel.Extensions; - -public static class SwaggerExtension -{ - private static SwaggerOptions GetSwaggerOptions(IConfiguration configuration) - { - var swaggerOptions = configuration.GetSection("Swagger") - .Get(); - - return swaggerOptions!; - } - - public static WebApplicationBuilder AddSwagger(this WebApplicationBuilder builder) - { - builder.Services.AddSwaggerGen(options => - { - var swaggerOptions = GetSwaggerOptions(builder.Configuration); - - foreach (var version in swaggerOptions.Versions) - { - options.SwaggerDoc(version.Key, - new OpenApiInfo - { - Version = version.Key, - Title = version.Value.Title, - Description = version.Value.Description, - Contact = new OpenApiContact - { - Name = "PandaTech LLC", - Email = "info@pandatech.it", - Url = new Uri("https://pandatech.it") - } - }); - } - - options.SchemaFilter(); - - // Add the custom token authentication option - options.AddSecurityDefinitions(); - }); - - return builder; - } - - public static void UseSwagger(this WebApplication app, IConfiguration configuration) - { - if (app.Environment.IsProduction()) - { - return; - } - - var swaggerOptions = configuration.GetSection("Swagger") - .Get(); - - app.UseSwagger(); - app.UseSwaggerUI(options => - { - if (swaggerOptions != null) - { - foreach (var version in swaggerOptions.Versions) - { - options.SwaggerEndpoint($"/swagger/{version.Key}/swagger.json", version.Value.Title); - } - } - - options.RoutePrefix = "swagger"; - options.DocumentTitle = $"Swagger - {AppDomain.CurrentDomain.FriendlyName}"; - - options.InjectStylesheet("/assets/css/panda-style.css"); - options.InjectJavascript("/assets/js/docs.js"); - options.DocExpansion(DocExpansion.None); - }); - - if (swaggerOptions == null) - { - return; - } - - foreach (var version in swaggerOptions.Versions.Where(version => version.Value.Separate)) - { - app.UseSwaggerUI(options => - { - options.SwaggerEndpoint($"/swagger/{version.Key}/swagger.json", version.Value.Title); - - options.RoutePrefix = $"swagger/{version.Value.RoutePrefix}"; - - options.InjectStylesheet("/assets/css/panda-style.css"); - options.InjectJavascript("/assets/js/docs.js"); - options.DocExpansion(DocExpansion.None); - }); - } - } - - private static void AddSecurityDefinitions(this SwaggerGenOptions options) - { - var securityHeaders = new List<(string Name, string Description)> - { - ("Client-Type", "Client type, e.g., '2'"), - ("Authorization", "Access token for the API"), - ("Refresh-Token", "Refresh token for the access token refresh"), - ("Accept-Language", "Language, e.g., 'en-US'") - }; - - foreach (var (name, description) in securityHeaders) - { - options.AddSecurityDefinition(name, - new OpenApiSecurityScheme - { - Type = SecuritySchemeType.ApiKey, - In = ParameterLocation.Header, - Name = name, - Description = description - }); - - options.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = name - }, - In = ParameterLocation.Header - }, - new List() - } - }); - } - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ApiHelper.cs b/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ApiHelper.cs index b88bd37..c50b9c6 100644 --- a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ApiHelper.cs +++ b/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ApiHelper.cs @@ -4,7 +4,7 @@ public static class ApiHelper { private const string BaseApiPath = "/api/v"; - public const string GroupVertical = "Vertical"; + public const string GroupVertical = "vertical-v1"; public static string GetRoutePrefix(int version, string baseRoute) diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/DatabaseHelper.cs b/src/Pandatech.VerticalSlices/SharedKernel/Helpers/DatabaseHelper.cs deleted file mode 100644 index 60961aa..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/DatabaseHelper.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace Pandatech.VerticalSlices.SharedKernel.Helpers; - -public class DatabaseHelper(IServiceProvider serviceProvider) -{ - public string ResetDatabase() where T : DbContext - { - using var scope = serviceProvider.CreateScope(); - var dbContext = scope.ServiceProvider.GetRequiredService(); - dbContext.Database.EnsureDeleted(); - dbContext.Database.Migrate(); - - return "Database reset success!"; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/EnumSwaggerSchemaFilter.cs b/src/Pandatech.VerticalSlices/SharedKernel/Helpers/EnumSwaggerSchemaFilter.cs deleted file mode 100644 index d936431..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/EnumSwaggerSchemaFilter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Pandatech.VerticalSlices.SharedKernel.Helpers; - -public class EnumSwaggerSchemaFilter : ISchemaFilter -{ - public void Apply(OpenApiSchema schema, SchemaFilterContext context) - { - if (context.Type.IsEnum) - { - var type = context.Type; - - var list = Enum.GetValues(type) - .Cast() - .ToList(); - - var enumDescriptions = list.Select(x => $"{x}: {(int)x}") - .ToList(); - - schema.Description = string.Join(", ", enumDescriptions); - } - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/PasswordHelper.cs b/src/Pandatech.VerticalSlices/SharedKernel/Helpers/PasswordHelper.cs index df6dd39..f567331 100644 --- a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/PasswordHelper.cs +++ b/src/Pandatech.VerticalSlices/SharedKernel/Helpers/PasswordHelper.cs @@ -1,4 +1,4 @@ -using Pandatech.Crypto; +using Pandatech.Crypto.Helpers; namespace Pandatech.VerticalSlices.SharedKernel.Helpers; diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ResilienceDefaultPipelineProvider.cs b/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ResilienceDefaultPipelineProvider.cs deleted file mode 100644 index c5c180a..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ResilienceDefaultPipelineProvider.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Net; -using Polly; -using Polly.CircuitBreaker; -using Polly.Registry; -using Polly.Retry; - -namespace Pandatech.VerticalSlices.SharedKernel.Helpers; - -public static class ResilienceDefaultPipelineProvider -{ - public static ResiliencePipeline GetDefaultPipeline( - this ResiliencePipelineProvider resiliencePipelineProvider) - { - return resiliencePipelineProvider.GetPipeline(DefaultPipelineName); - } - - internal const string DefaultPipelineName = "DefaultPipeline"; - - internal static RetryStrategyOptions TooManyRequestsRetryOptions => - new() - { - MaxRetryAttempts = 5, - BackoffType = DelayBackoffType.Exponential, - UseJitter = true, - Delay = TimeSpan.FromMilliseconds(3000), - ShouldHandle = new PredicateBuilder() - .Handle(exception => exception.StatusCode == HttpStatusCode.TooManyRequests) - }; - - internal static RetryStrategyOptions DefaultNetworkRetryOptions => - new() - { - MaxRetryAttempts = 7, - BackoffType = DelayBackoffType.Exponential, - UseJitter = true, - Delay = TimeSpan.FromMilliseconds(800), - ShouldHandle = new PredicateBuilder() - .Handle(exception => exception.StatusCode == HttpStatusCode.RequestTimeout || - (int)exception.StatusCode! >= 500) - }; - - internal static CircuitBreakerStrategyOptions DefaultCircuitBreakerOptions => - new() - { - FailureRatio = 0.5, - SamplingDuration = TimeSpan.FromSeconds(30), - MinimumThroughput = 200, - BreakDuration = TimeSpan.FromSeconds(45), - ShouldHandle = new PredicateBuilder().Handle() - }; -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ResilienceHttpOptions.cs b/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ResilienceHttpOptions.cs deleted file mode 100644 index 7c54c3e..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Helpers/ResilienceHttpOptions.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Net; -using Microsoft.Extensions.Http.Resilience; -using Polly; - -namespace Pandatech.VerticalSlices.SharedKernel.Helpers; - -internal static class ResilienceHttpOptions -{ - public static HttpRetryStrategyOptions DefaultTooManyRequestsRetryOptions => - new() - { - MaxRetryAttempts = 5, - BackoffType = DelayBackoffType.Exponential, - UseJitter = true, - Delay = TimeSpan.FromMilliseconds(3000), - ShouldHandle = args => - { - if (args.Outcome.Exception is HttpRequestException httpException) - { - return ValueTask.FromResult((int)httpException.StatusCode! == 429); - } - - if (args.Outcome.Result is not null && args.Outcome.Result.StatusCode == HttpStatusCode.TooManyRequests) - { - return ValueTask.FromResult(true); - } - - return ValueTask.FromResult(false); - }, - DelayGenerator = args => - { - if (args.Outcome.Result is null) - { - return ValueTask.FromResult(null); - } - - if (!args.Outcome.Result.Headers.TryGetValues("Retry-After", out var values)) - { - return ValueTask.FromResult(null); - } - - var retryAfterValue = values.FirstOrDefault(); - - if (int.TryParse(retryAfterValue, out var retryAfterSeconds)) - { - return ValueTask.FromResult(TimeSpan.FromSeconds(retryAfterSeconds)); - } - - if (!DateTimeOffset.TryParseExact(retryAfterValue, - "R", // RFC1123 pattern - System.Globalization.CultureInfo.InvariantCulture, - System.Globalization.DateTimeStyles.None, - out var retryAfterDate)) - { - return ValueTask.FromResult(null); - } - - var retryDelay = retryAfterDate - DateTimeOffset.UtcNow; - return ValueTask.FromResult(retryDelay > TimeSpan.Zero ? retryDelay : TimeSpan.MinValue); - } - }; - - public static HttpRetryStrategyOptions DefaultNetworkRetryOptions => - new() - { - MaxRetryAttempts = 7, - BackoffType = DelayBackoffType.Exponential, - UseJitter = true, - Delay = TimeSpan.FromMilliseconds(800), - ShouldHandle = args => - { - if (args.Outcome.Exception is HttpRequestException httpException) - { - return ValueTask.FromResult((int)httpException.StatusCode! >= 500 || - (int)httpException.StatusCode! == 408); - } - - return ValueTask.FromResult(args.Outcome.Result is not null && - (args.Outcome.Result.StatusCode == HttpStatusCode.RequestTimeout || - (int)args.Outcome.Result.StatusCode >= 500)); - } - }; - - public static HttpCircuitBreakerStrategyOptions DefaultCircuitBreakerOptions => - new() - { - FailureRatio = 0.5, - SamplingDuration = TimeSpan.FromSeconds(30), - MinimumThroughput = 200, - BreakDuration = TimeSpan.FromSeconds(45), - ShouldHandle = args => - { - if (args.Outcome.Exception is not null) - { - return ValueTask.FromResult(true); - } - - return args.Outcome.Result is null - ? ValueTask.FromResult(false) - : ValueTask.FromResult(!args.Outcome.Result.IsSuccessStatusCode); - } - }; -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Interfaces/MediatRInterfaces.cs b/src/Pandatech.VerticalSlices/SharedKernel/Interfaces/MediatRInterfaces.cs deleted file mode 100644 index fde90fa..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Interfaces/MediatRInterfaces.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MediatR; - -namespace Pandatech.VerticalSlices.SharedKernel.Interfaces; - -public interface ICommand : IRequest; - -public interface ICommand : IRequest; - -public interface IQuery : IRequest; - -public interface IQuery : IRequest; - -public interface ICommandHandler : IRequestHandler - where TCommand : ICommand; - -public interface ICommandHandler : IRequestHandler - where TCommand : ICommand; - -public interface IQueryHandler : IRequestHandler - where TQuery : IQuery; - -public interface IQueryHandler : IRequestHandler - where TQuery : IQuery; \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/SharedKernel/Middlewares/RequestResponseLoggingMiddleware.cs b/src/Pandatech.VerticalSlices/SharedKernel/Middlewares/RequestResponseLoggingMiddleware.cs deleted file mode 100644 index 6238b5b..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/Middlewares/RequestResponseLoggingMiddleware.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System.Diagnostics; -using System.Text.Json; - -namespace Pandatech.VerticalSlices.SharedKernel.Middlewares; - -public class RequestResponseLoggingMiddleware(RequestDelegate next, ILogger logger) -{ - private static readonly HashSet SensitiveKeywords = new(StringComparer.OrdinalIgnoreCase) - { - "pwd", - "pass", - "secret", - "token", - "cookie", - "auth" - }; - - public async Task InvokeAsync(HttpContext context) - { - if (context.Request.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase)) - { - await next(context); - return; - } - - var requestLog = await CaptureRequestAsync(context.Request); - - // Swap the response stream to capture the response - var originalBodyStream = context.Response.Body; - await using var responseBody = new MemoryStream(); - context.Response.Body = responseBody; - var stopwatch = Stopwatch.GetTimestamp(); - try - { - await next(context); - } - finally - { - var delta = Stopwatch.GetElapsedTime(stopwatch).TotalMilliseconds; - var responseLog = await CaptureResponseAsync(context.Response); - - logger.LogInformation( - "Request {Method} {Query} responded {StatusCode} in {ElapsedMilliseconds}ms. RequestHeaders: {RequestHeaders}, RequestBody: {RequestBody}, ResponseHeaders: {ResponseHeaders}, ResponseBody: {ResponseBody}", - context.Request.Method, - context.Request.QueryString, - context.Response.StatusCode, - delta, - requestLog.Headers, - requestLog.Body, - responseLog.Headers, - responseLog.Body); - - // Reset the response body to the original stream - await responseBody.CopyToAsync(originalBodyStream); - } - } - - private static async Task<(string Headers, string Body)> CaptureRequestAsync(HttpRequest request) - { - request.EnableBuffering(); - var (headers, bodyContent) = await CaptureLogAsync(request.Body, request.Headers); - request.Body.Position = 0; - return (headers, bodyContent); - } - - private static async Task<(string Headers, string Body)> CaptureResponseAsync(HttpResponse response) - { - response.Body.Seek(0, SeekOrigin.Begin); - var (headers, bodyContent) = await CaptureLogAsync(response.Body, response.Headers); - response.Body.Seek(0, SeekOrigin.Begin); - return (headers, bodyContent); - } - - private static async Task<(string Headers, string Body)> CaptureLogAsync(Stream bodyStream, IHeaderDictionary headers) - { - using var reader = new StreamReader(bodyStream, leaveOpen: true); - var body = await reader.ReadToEndAsync(); - var sanitizedHeaders = JsonSerializer.Serialize(RedactSensitiveData(headers)); - var bodyContent = JsonSerializer.Serialize(ParseAndRedactJson(body)); - - return (sanitizedHeaders, bodyContent); - } - - private static object ParseAndRedactJson(string body) - { - if (string.IsNullOrWhiteSpace(body)) - return string.Empty; - - try - { - var jsonElement = JsonSerializer.Deserialize(body); - return RedactSensitiveData(jsonElement); - } - catch (JsonException) - { - // Return the body as a string if it’s not valid JSON, - // but still wrapped in an object to avoid conversion to a string - return RedactSensitiveString(body); - } - } - - private static Dictionary RedactSensitiveData(IHeaderDictionary headers) - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var header in headers) - { - var key = header.Key; - var value = SensitiveKeywords.Any(keyword => key.Contains(keyword, StringComparison.OrdinalIgnoreCase)) - ? "[REDACTED]" - : header.Value.ToString(); - - result[key] = value; - } - - return result; - } - - private static object RedactSensitiveData(JsonElement element) - { - switch (element.ValueKind) - { - case JsonValueKind.Object: - var maskedObject = new Dictionary(); - foreach (var property in element.EnumerateObject()) - { - var propertyName = property.Name; - - if (SensitiveKeywords.Any(keyword => propertyName.Contains(keyword, StringComparison.OrdinalIgnoreCase))) - { - maskedObject[propertyName] = "[REDACTED]"; - } - else - { - maskedObject[propertyName] = RedactSensitiveData(property.Value); - } - } - return maskedObject; - - case JsonValueKind.Array: - var maskedArray = element.EnumerateArray() - .Select(RedactSensitiveData) - .ToArray(); - return maskedArray; - - case JsonValueKind.String: - var stringValue = element.GetString(); - return RedactSensitiveString(stringValue); - - case JsonValueKind.Undefined: - case JsonValueKind.Number: - case JsonValueKind.True: - case JsonValueKind.False: - case JsonValueKind.Null: - default: - return element.GetRawText(); - } - } - - private static string RedactSensitiveString(string? value) - { - if (string.IsNullOrWhiteSpace(value)) - return string.Empty; - - return SensitiveKeywords.Any(keyword => value.Contains(keyword, StringComparison.OrdinalIgnoreCase)) - ? "[REDACTED]" - : value; - } -} diff --git a/src/Pandatech.VerticalSlices/SharedKernel/SharedEndpoints/PandaEndpoints.cs b/src/Pandatech.VerticalSlices/SharedKernel/SharedEndpoints/PandaEndpoints.cs deleted file mode 100644 index 4368601..0000000 --- a/src/Pandatech.VerticalSlices/SharedKernel/SharedEndpoints/PandaEndpoints.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Net; -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Pandatech.VerticalSlices.Context; -using Pandatech.VerticalSlices.SharedKernel.Extensions; -using Pandatech.VerticalSlices.SharedKernel.Helpers; -using PandaVaultClient; - -namespace Pandatech.VerticalSlices.SharedKernel.SharedEndpoints; - -public static class PandaEndpoints -{ - private const string TagName = "above-board"; - private const string BasePath = $"/{TagName}"; - - public static void MapPandaEndpoints(this WebApplication app) - { - if (!app.Environment.IsProduction()) - { - app.MapPandaVaultApi($"{BasePath}/configuration", TagName, ApiHelper.GroupVertical); - } - - if (app.Environment.IsLocal()) - { - app.MapDatabaseResetEndpoint(); - } - - app.MapPingEndpoint(true) - .MapHealthEndpoint(true) - .MapPrometheusEndpoints(true); - } - - private static WebApplication MapDatabaseResetEndpoint(this WebApplication app) - { - app.MapGet($"{BasePath}/reset-database", - ([FromServices] DatabaseHelper helper) => helper.ResetDatabase()) - .WithTags(TagName) - .WithGroupName(ApiHelper.GroupVertical); - - - return app; - } - - private static WebApplication MapPrometheusEndpoints(this WebApplication app, bool enabled) - { - if (!enabled) - { - return app; - } - - app.MapPrometheusScrapingEndpoint($"{BasePath}/metrics"); - - app.UseHealthChecksPrometheusExporter($"{BasePath}/metrics/health", - options => options.ResultStatusCodes[HealthStatus.Unhealthy] = (int)HttpStatusCode.OK); - - return app; - } - - private static WebApplication MapPingEndpoint(this WebApplication app, bool enabled) - { - if (!enabled) - { - return app; - } - - app.MapGet($"{BasePath}/ping", () => "pong") - .Produces() - .WithTags(TagName) - .WithGroupName(ApiHelper.GroupVertical) - .WithOpenApi(); - return app; - } - - private static WebApplication MapHealthEndpoint(this WebApplication app, bool enabled) - { - if (!enabled) - { - return app; - } - - app.MapHealthChecks($"{BasePath}/health", - new HealthCheckOptions - { - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - - return app; - } -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/appsettings.Development.json b/src/Pandatech.VerticalSlices/appsettings.Development.json index 7ad1e0a..4d5eb19 100644 --- a/src/Pandatech.VerticalSlices/appsettings.Development.json +++ b/src/Pandatech.VerticalSlices/appsettings.Development.json @@ -1,12 +1,4 @@ { - "AllowedHosts": "*", - "Kestrel": { - "EndPoints": { - "Http": { - "Url": "http://*:80" - } - } - }, "Serilog": { "MinimumLevel": { "Default": "Information", @@ -17,7 +9,7 @@ } }, "ResponseCrafterVisibility": "Private", - "RepositoryName": "be-tmp-vertical-slices", + "DefaultTimeZone": "Caucasus Standard Time", "ConnectionStrings": { "Postgres": "**", "PersistentStorage": "/persistence", diff --git a/src/Pandatech.VerticalSlices/appsettings.Local.json b/src/Pandatech.VerticalSlices/appsettings.Local.json index cb6a606..cb670bc 100644 --- a/src/Pandatech.VerticalSlices/appsettings.Local.json +++ b/src/Pandatech.VerticalSlices/appsettings.Local.json @@ -1,12 +1,4 @@ { - "AllowedHosts": "*", - "Kestrel": { - "EndPoints": { - "Http": { - "Url": "http://*:80" - } - } - }, "Serilog": { "MinimumLevel": { "Default": "Information", @@ -17,6 +9,7 @@ } }, "ResponseCrafterVisibility": "Private", + "DefaultTimeZone": "Caucasus Standard Time", "ConnectionStrings": { "Postgres": "Server=localhost;Port=5432;Database=pandatech_vertical_slices;User Id=test;Password=test;Pooling=true;", "Redis": "localhost:6379", diff --git a/src/Pandatech.VerticalSlices/appsettings.Production.json b/src/Pandatech.VerticalSlices/appsettings.Production.json index 2e59959..9a9b3c0 100644 --- a/src/Pandatech.VerticalSlices/appsettings.Production.json +++ b/src/Pandatech.VerticalSlices/appsettings.Production.json @@ -1,12 +1,4 @@ { - "AllowedHosts": "*", - "Kestrel": { - "EndPoints": { - "Http": { - "Url": "http://*:80" - } - } - }, "Serilog": { "MinimumLevel": { "Default": "Information", @@ -17,7 +9,7 @@ } }, "ResponseCrafterVisibility": "Public", - "RepositoryName": "be-tmp-vertical-slices", + "DefaultTimeZone": "Caucasus Standard Time", "ConnectionStrings": { "Postgres": "**", "PersistentStorage": "/persistence", diff --git a/src/Pandatech.VerticalSlices/appsettings.QA.json b/src/Pandatech.VerticalSlices/appsettings.QA.json index 7ad1e0a..4d5eb19 100644 --- a/src/Pandatech.VerticalSlices/appsettings.QA.json +++ b/src/Pandatech.VerticalSlices/appsettings.QA.json @@ -1,12 +1,4 @@ { - "AllowedHosts": "*", - "Kestrel": { - "EndPoints": { - "Http": { - "Url": "http://*:80" - } - } - }, "Serilog": { "MinimumLevel": { "Default": "Information", @@ -17,7 +9,7 @@ } }, "ResponseCrafterVisibility": "Private", - "RepositoryName": "be-tmp-vertical-slices", + "DefaultTimeZone": "Caucasus Standard Time", "ConnectionStrings": { "Postgres": "**", "PersistentStorage": "/persistence", diff --git a/src/Pandatech.VerticalSlices/appsettings.json b/src/Pandatech.VerticalSlices/appsettings.json index 20395fc..53560a3 100644 --- a/src/Pandatech.VerticalSlices/appsettings.json +++ b/src/Pandatech.VerticalSlices/appsettings.json @@ -1,13 +1,49 @@ { - "Swagger": { - "Enabled": true, - "JSONRoutePrefix": "", - "ApiBasePath": "/", - "Versions": { - "Vertical": { - "Title": "Vertical", - "Description": "Powered by PandaTech LLC: Where precision meets innovation. Let's build the future, one endpoint at a time." + "AllowedHosts": "*", + "Kestrel": { + "EndPoints": { + "Http": { + "Url": "http://*:80" } } + }, + "RepositoryName": "be-tmp-vertical-slices", + + "OpenApi": { + "DisabledEnvironments": [ + "Production" + ], + "SecuritySchemes": [ + { + "HeaderName": "Client-Type", + "Description": "Specifies the client type, e.g., '2'." + }, + { + "HeaderName": "Authorization", + "Description": "Access token for the API." + }, + { + "HeaderName": "Refresh-Token", + "Description": "Refresh token for renewing the access token." + }, + { + "HeaderName": "Accept-Language", + "Description": "Language preference, e.g., 'en-US'." + } + ], + "Documents": [ + { + "Title": "Administrative Panel Partners", + "Description": "This document describes the API endpoints for the Administrative Panel Partners.", + "GroupName": "vertical-v1", + "Version": "v1", + "ForExternalUse": false + } + ], + "Contact": { + "Name": "Pandatech", + "Url": "https://pandatech.it", + "Email": "info@pandatech.it" + } } } \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/wwwroot/assets/css/panda-style.css b/src/Pandatech.VerticalSlices/wwwroot/assets/css/panda-style.css deleted file mode 100644 index ebcc69f..0000000 --- a/src/Pandatech.VerticalSlices/wwwroot/assets/css/panda-style.css +++ /dev/null @@ -1,157 +0,0 @@ -.swagger-ui .auth-container .btn.modal-btn.auth { - display: flex; - justify-content: center; -} - -.topbar-wrapper > a > svg { - opacity: 0; -} - -.swagger-ui .topbar .download-url-wrapper { - display: flex; -} - -.swagger-ui .topbar .download-url-wrapper .select-label select { - border-color: #4E37D3; -} - -.swagger-ui .topbar .download-url-wrapper .select-label span { - color: transparent; - user-select: none; -} - - -.swagger-ui .topbar-wrapper img { - visibility: hidden; -} - -element.style { -} - -.swagger-ui .auth-btn-wrapper { - gap: 20px; - margin-top: 20px; -} - -.swagger-ui .auth-wrapper .authorize { - margin: 0; -} - -.swagger-ui .btn.authorize.locked, .swagger-ui .btn.authorize.unlocked { - display: flex; - align-items: center; - justify-content: center; - gap: 7px - -} - -.swagger-ui .btn.authorize span { - clear: both; - padding: 0; - margin-top: 3px; -} - -.swagger-ui .auth-container .btn.modal-btn.auth { - min-width: 250px; - padding: 10px 0; -} - -.swagger-ui .topbar-wrapper .link::before { - content: ""; - background: url("../images/logo.svg") no-repeat center; - background-size: contain; - width: 36.402px; - height: 28.8px; - display: inline-block; - position: relative; -} - -/* Add your second image */ -.swagger-ui .topbar-wrapper .link::after { - content: ""; - background: url("../images/logo-wording.svg") no-repeat center; - background-size: contain; - width: 90.0276px; - height: 20.2044px; - display: inline-block; - position: relative; - left: -132px; -} - -div.topbar { - background: transparent !important; - backdrop-filter: blur(3px); - position: fixed; - width: 100%; - top: 0; - border-bottom: 1px solid #ffffff; - z-index: 1000; -} - -.swagger-ui { - margin-top: 128px; -} - -.swagger-ui .btn.authorize.unlocked { - background-color: white !important; - color: black !important; - border: 2px solid #4E37D3 !important; -} - -.swagger-ui .btn.authorize.unlocked svg { - fill: black !important; -} - -.swagger-ui .btn.authorize.locked { - background-color: #4E37D3 !important; - color: #ffffff !important; - border: 2px solid #4E37D3 !important; -} - -.swagger-ui .btn.authorize.locked svg { - fill: #ffffff !important; -} - -.swagger-ui .auth-container .btn.modal-btn.auth { - background-color: #4E37D3 !important; - color: #ffffff !important; - border: 1px solid #4E37D3 !important; - width: 100%; -} - -.btn.modal-btn.auth.btn-done.button { - display: none; -} - -.auth-container { - padding: 0 !important; -} - -.swagger-ui .dialog-ux .modal-ux { - max-width: 600px; -} - -.opblock-summary-get:hover { - background-color: #b3e0ff !important; -} - -.swagger-ui .auth-container input[type=text] { - - width: 100%; -} - -.opblock-summary-post:hover { - background-color: #83e697 !important; -} - -.opblock-summary-put:hover { - background-color: #f0cc9f !important; -} - -.opblock-summary-patch:hover { - background-color: #a2eede !important; -} - -.opblock-summary-delete:hover { - background-color: #faa3a3 !important; -} \ No newline at end of file diff --git a/src/Pandatech.VerticalSlices/wwwroot/assets/images/favicon.svg b/src/Pandatech.VerticalSlices/wwwroot/assets/images/favicon.svg deleted file mode 100644 index 1ea8a48..0000000 --- a/src/Pandatech.VerticalSlices/wwwroot/assets/images/favicon.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/Pandatech.VerticalSlices/wwwroot/assets/images/logo-wording.svg b/src/Pandatech.VerticalSlices/wwwroot/assets/images/logo-wording.svg deleted file mode 100644 index 155ef48..0000000 --- a/src/Pandatech.VerticalSlices/wwwroot/assets/images/logo-wording.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/Pandatech.VerticalSlices/wwwroot/assets/images/logo.svg b/src/Pandatech.VerticalSlices/wwwroot/assets/images/logo.svg deleted file mode 100644 index e69157c..0000000 --- a/src/Pandatech.VerticalSlices/wwwroot/assets/images/logo.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/src/Pandatech.VerticalSlices/wwwroot/assets/js/docs.js b/src/Pandatech.VerticalSlices/wwwroot/assets/js/docs.js deleted file mode 100644 index 91b8cc1..0000000 --- a/src/Pandatech.VerticalSlices/wwwroot/assets/js/docs.js +++ /dev/null @@ -1,24 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - - var link = document.querySelector("link[rel*='icon']") || document.createElement("link"); - document.head.removeChild(link); - link = document.createElement("link"); - link.type = "image/x-icon"; - link.rel = "shortcut icon"; - link.href = "../assets/images/favicon.svg"; - document.getElementsByTagName("head")[0].appendChild(link); - - // Adjusted MutationObserver code - const observer = new MutationObserver((mutations) => { - const modal = document.querySelector('.modal-ux-content'); - if (modal) { - modal.scrollTo(0, 0); - observer.disconnect(); - } - }); - - observer.observe(document.body, { - childList: true, - subtree: true, - }); -}); \ No newline at end of file diff --git a/test/Pandatech.VerticalSlices.Tests/Configurations/ApiFactory.cs b/test/Pandatech.VerticalSlices.Tests/Configurations/ApiFactory.cs index 0a3acdd..4b7845f 100644 --- a/test/Pandatech.VerticalSlices.Tests/Configurations/ApiFactory.cs +++ b/test/Pandatech.VerticalSlices.Tests/Configurations/ApiFactory.cs @@ -22,19 +22,6 @@ public class ApiFactory : WebApplicationFactory, IAsyncLifeti public HttpClient HttpClient { get; private set; } = default!; - public async Task InitializeAsync() - { - SetEnvironments(); - await _dbContainer.StartAsync(); - await CreateDatabase(); - HttpClient = CreateClient(); - await InitializeRespawner(); - } - - public new async Task DisposeAsync() - { - await _dbContainer.StopAsync(); - } protected override void ConfigureWebHost(IWebHostBuilder builder) { @@ -49,6 +36,19 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) }); }); } + public async Task InitializeAsync() + { + SetEnvironments(); + await _dbContainer.StartAsync(); + await CreateDatabase(); + HttpClient = CreateClient(); + await InitializeRespawner(); + } + + public new async Task DisposeAsync() + { + await _dbContainer.StopAsync(); + } private async Task InitializeRespawner() { diff --git a/test/Pandatech.VerticalSlices.Tests/Configurations/SharedPostgresTestCollection.cs b/test/Pandatech.VerticalSlices.Tests/Configurations/SharedPostgresTestCollection.cs index 84740a4..e93726b 100644 --- a/test/Pandatech.VerticalSlices.Tests/Configurations/SharedPostgresTestCollection.cs +++ b/test/Pandatech.VerticalSlices.Tests/Configurations/SharedPostgresTestCollection.cs @@ -1,6 +1,4 @@ namespace Pandatech.VerticalSlices.Tests.Configurations; [CollectionDefinition("Shared Postgres")] -public class SharedPostgresTestCollection : ICollectionFixture -{ -} \ No newline at end of file +public class SharedPostgresTestCollection : ICollectionFixture; \ No newline at end of file diff --git a/test/Pandatech.VerticalSlices.Tests/Pandatech.VerticalSlices.Tests.csproj b/test/Pandatech.VerticalSlices.Tests/Pandatech.VerticalSlices.Tests.csproj index e7dbc94..f5c902f 100644 --- a/test/Pandatech.VerticalSlices.Tests/Pandatech.VerticalSlices.Tests.csproj +++ b/test/Pandatech.VerticalSlices.Tests/Pandatech.VerticalSlices.Tests.csproj @@ -7,16 +7,16 @@ - - - + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all