diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml
index 9a8c5c1..325a52a 100644
--- a/.github/workflows/ci-pipeline.yml
+++ b/.github/workflows/ci-pipeline.yml
@@ -4,6 +4,10 @@ on:
branches: [ main ]
push:
branches: [ main ]
+ paths:
+ - "src/**"
+ - "tests/**"
+ - ".github/**"
jobs:
test-and-build:
diff --git a/Directory.Build.props b/Directory.Build.props
index e5753e4..ca0a69b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -15,7 +15,7 @@
- net9.0
+ net10.0
enable
enable
true
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 1435e8a..338da01 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -5,17 +5,17 @@
-
+
-
+
-
+
@@ -76,13 +76,13 @@
-
-
-
+
+
+
-
+
diff --git a/src/Riber.Api/Common/Api/AppExtension.cs b/src/Riber.Api/Common/Api/AppExtension.cs
deleted file mode 100644
index 6bac05d..0000000
--- a/src/Riber.Api/Common/Api/AppExtension.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using HealthChecks.UI.Client;
-using Microsoft.AspNetCore.Diagnostics.HealthChecks;
-using Riber.Api.Middlewares;
-using Scalar.AspNetCore;
-
-namespace Riber.Api.Common.Api;
-
-public static class AppExtension
-{
- public static void UsePipeline(this WebApplication app)
- {
- app.UseConfigurations();
- app.UseSecurity();
- app.UseMiddlewares();
- app.UseHealthChecks();
- app.MapControllers();
- }
-
- private static void UseSecurity(this WebApplication app)
- {
- app.UseAuthentication();
- app.UseAuthorization();
- }
-
- private static void UseMiddlewares(this WebApplication app)
- {
- app.UseMiddleware();
- }
-
- private static void UseConfigurations(this WebApplication app)
- {
- app.UseExceptionHandler();
- app.Use(async (context, next) =>
- {
- context.Response.Headers.Append("X-Content-Type-Options", "nosniff");
- context.Response.Headers.Append("Referrer-Policy", "no-referrer");
- context.Response.Headers.Append("X-XSS-Protection", "1; mode=block");
- context.Response.Headers.Append("X-Frame-Options", "DENY");
- await next();
- });
-
- if (app.Environment.IsDevelopment())
- {
- app.MapOpenApi();
- app.MapScalarApiReference();
- }
-
- if (app.Environment.IsProduction())
- app.UseHttpsRedirection();
-
- app.UseRequestTimeouts();
- }
-
- private static void UseHealthChecks(this WebApplication app)
- {
- app.MapHealthChecks("/", new HealthCheckOptions
- {
- ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
- });
- }
-}
\ No newline at end of file
diff --git a/src/Riber.Api/Common/Api/BuilderExtension.cs b/src/Riber.Api/Common/Api/BuilderExtension.cs
deleted file mode 100644
index 9bd4048..0000000
--- a/src/Riber.Api/Common/Api/BuilderExtension.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-using System.Text;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using Asp.Versioning;
-using Microsoft.AspNetCore.Authorization;
-using Riber.Application;
-using Riber.Infrastructure;
-using Microsoft.AspNetCore.Http.Timeouts;
-using Microsoft.AspNetCore.Identity;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Server.Kestrel.Core;
-using Microsoft.IdentityModel.Tokens;
-using Microsoft.OpenApi.Models;
-using Riber.Api.Authorizations.Permissions;
-using Riber.Api.Middlewares;
-using Riber.Infrastructure.Persistence;
-using Riber.Infrastructure.Persistence.Identity;
-using Riber.Infrastructure.Settings;
-
-namespace Riber.Api.Common.Api;
-
-public static class BuilderExtension
-{
- public static void AddPipeline(this WebApplicationBuilder builder)
- {
- builder.AddJsonConfiguration();
- builder.AddDocumentationApi();
- builder.AddDependencyInjection();
- builder.AddConfigurations();
- builder.AddMiddleware();
- builder.AddSecurity();
- }
-
- private static void AddDependencyInjection(this WebApplicationBuilder builder)
- {
- builder.Services.AddApplication();
- builder.Services.AddInfrastructure(builder.Configuration, builder.Logging);
- }
-
- private static void AddMiddleware(this WebApplicationBuilder builder)
- {
- builder.Services.AddScoped();
- }
-
- private static void AddConfigurations(this WebApplicationBuilder builder)
- {
- builder.WebHost.ConfigureKestrel(options =>
- {
- options.AddServerHeader = false;
- options.ConfigureEndpointDefaults(endpoint
- => endpoint.Protocols = HttpProtocols.Http1AndHttp2AndHttp3);
- });
-
- builder.Configuration.AddEnvironmentVariables();
- builder.Services.AddExceptionHandler();
- builder.Services.AddProblemDetails();
- builder.Services.AddMemoryCache();
-
- builder.Services
- .AddOptions()
- .Bind(builder.Configuration.GetSection(nameof(AccessTokenSettings))) // <- Atribuí os valores
- .ValidateDataAnnotations() // ⇽ Pega as regras de validação
- .ValidateOnStart(); // ← Faz a validação, se não tiver válido, cancela a execução do programa
-
- builder.Services
- .AddOptions()
- .Bind(builder.Configuration.GetSection(nameof(RefreshTokenSettings)))
- .ValidateDataAnnotations()
- .ValidateOnStart();
-
- builder.Services
- .Configure(options
- => options.SuppressModelStateInvalidFilter = true)
- .Configure(options
- => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
-
- // Para uso das políticas, consulte: docs/REQUEST-TIMEOUT.md
- builder.Services.AddRequestTimeouts(options =>
- {
- options.DefaultPolicy = new RequestTimeoutPolicy { Timeout = TimeSpan.FromMinutes(1) };
- options.AddPolicy("fast", TimeSpan.FromSeconds(15));
- options.AddPolicy("standard", TimeSpan.FromSeconds(30));
- options.AddPolicy("slow", TimeSpan.FromMinutes(1));
- options.AddPolicy("upload", TimeSpan.FromMinutes(5));
- });
-
- builder.Services.AddApiVersioning(options =>
- {
- // Define a versão padrão da API (1.0)
- options.DefaultApiVersion = new ApiVersion(1, 0);
- // Se o cliente não especificar a versão, assume a versão padrão
- options.AssumeDefaultVersionWhenUnspecified = true;
- // Define COMO o cliente pode especificar a versão
- options.ApiVersionReader = ApiVersionReader.Combine(
- new UrlSegmentApiVersionReader(), // via URL: /api/v1/company
- new QueryStringApiVersionReader("version"), // via query: ?version=1.0
- new HeaderApiVersionReader("X-Version") // via header: X-Version: 1.0
- );
- }).AddApiExplorer(setup =>
- {
- setup.GroupNameFormat = "'v'VVV";
- setup.SubstituteApiVersionInUrl = true;
- });
- }
-
- private static void AddSecurity(this WebApplicationBuilder builder)
- {
- var accessToken =
- builder.Configuration.GetSection(nameof(AccessTokenSettings)).Get()
- ?? throw new InvalidOperationException($"{nameof(AccessTokenSettings)} configuration not found");
-
- var refreshToken =
- builder.Configuration.GetSection(nameof(RefreshTokenSettings)).Get()
- ?? throw new InvalidOperationException($"{nameof(RefreshTokenSettings)} configuration not found");
-
- builder.Services.AddIdentity()
- .AddEntityFrameworkStores()
- .AddDefaultTokenProviders();
-
- builder.Services
- .AddAuthentication(options =>
- {
- options.DefaultAuthenticateScheme = nameof(AccessTokenSettings);
- options.DefaultChallengeScheme = nameof(AccessTokenSettings);
- })
- .AddJwtBearer(nameof(AccessTokenSettings), options
- => options.TokenValidationParameters = new TokenValidationParameters
- {
- ValidateIssuerSigningKey = true,
- IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(accessToken.SecretKey)),
- ValidateIssuer = true,
- ValidIssuer = accessToken.Issuer,
- ValidateAudience = true,
- ValidAudience = accessToken.Audience,
- ValidateLifetime = true, // ← Valida expiração
- ClockSkew = TimeSpan.Zero, // ← Remove tolerância de tempo
- RequireExpirationTime = true // ← Exige claim 'exp'
- }
- )
- .AddJwtBearer(nameof(RefreshTokenSettings), options
- => options.TokenValidationParameters = new TokenValidationParameters
- {
- ValidateIssuerSigningKey = true,
- IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(refreshToken.SecretKey)),
- ValidateIssuer = true,
- ValidIssuer = refreshToken.Issuer,
- ValidateAudience = true,
- ValidAudience = refreshToken.Audience,
- ValidateLifetime = true,
- ClockSkew = TimeSpan.Zero,
- RequireExpirationTime = true
- });
-
- builder.Services.AddSingleton();
- builder.Services.AddScoped();
- builder.Services.AddAuthorization();
- }
-
- private static void AddDocumentationApi(this WebApplicationBuilder builder)
- {
- builder.Services.AddOpenApi(options
- => options.AddDocumentTransformer((document, _, _) =>
- {
- document.Info = new()
- {
- Title = "Riber Documentation API", Version = "v1", Description = "API do Riber"
- };
-
- document.Components ??= new();
- document.Components.SecuritySchemes = new Dictionary
- {
- ["Bearer"] = new()
- {
- Type = SecuritySchemeType.Http,
- Scheme = "bearer",
- BearerFormat = "JWT",
- Description = "Insert your JWT Token here"
- }
- };
-
- document.SecurityRequirements =
- [
- new OpenApiSecurityRequirement
- {
- [new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }] =
- Array.Empty()
- }
- ];
-
- return Task.CompletedTask;
- }));
- }
-
- private static void AddJsonConfiguration(this WebApplicationBuilder builder)
- {
- builder.Services.ConfigureHttpJsonOptions(options =>
- {
- options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
- options.SerializerOptions.PropertyNameCaseInsensitive = true;
- options.SerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
- options.SerializerOptions.AllowTrailingCommas = true;
- options.SerializerOptions.WriteIndented = true;
- options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.Never;
- });
-
- builder.Services
- .AddControllers()
- .AddJsonOptions(options =>
- {
- options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
- options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
- options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.Never;
- });
- }
-}
\ No newline at end of file
diff --git a/src/Riber.Api/Common/AppExtension.cs b/src/Riber.Api/Common/AppExtension.cs
new file mode 100644
index 0000000..24b1f8d
--- /dev/null
+++ b/src/Riber.Api/Common/AppExtension.cs
@@ -0,0 +1,64 @@
+using HealthChecks.UI.Client;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Riber.Api.Middlewares;
+using Scalar.AspNetCore;
+
+namespace Riber.Api.Common;
+
+internal static class AppExtension
+{
+ extension(WebApplication app)
+ {
+ public void UsePipeline()
+ {
+ app.UseConfigurations();
+ app.UseSecurity();
+ app.UseMiddlewares();
+ app.UseHealthChecks();
+ app.MapControllers();
+ }
+
+ private void UseSecurity()
+ {
+ app.UseAuthentication();
+ app.UseAuthorization();
+ }
+
+ private void UseMiddlewares()
+ {
+ app.UseMiddleware();
+ }
+
+ private void UseConfigurations()
+ {
+ app.UseExceptionHandler();
+ app.Use(async (context, next) =>
+ {
+ context.Response.Headers.Append("X-Content-Type-Options", "nosniff");
+ context.Response.Headers.Append("Referrer-Policy", "no-referrer");
+ context.Response.Headers.Append("X-XSS-Protection", "1; mode=block");
+ context.Response.Headers.Append("X-Frame-Options", "DENY");
+ await next();
+ });
+
+ if (app.Environment.IsDevelopment())
+ {
+ app.MapOpenApi();
+ app.MapScalarApiReference();
+ }
+
+ if (app.Environment.IsProduction())
+ app.UseHttpsRedirection();
+
+ app.UseRequestTimeouts();
+ }
+
+ private void UseHealthChecks()
+ {
+ app.MapHealthChecks("/", new HealthCheckOptions
+ {
+ ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Riber.Api/Common/BuilderExtension.cs b/src/Riber.Api/Common/BuilderExtension.cs
new file mode 100644
index 0000000..31fda96
--- /dev/null
+++ b/src/Riber.Api/Common/BuilderExtension.cs
@@ -0,0 +1,189 @@
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Asp.Versioning;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http.Timeouts;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.IdentityModel.Tokens;
+using Riber.Api.Authorizations.Permissions;
+using Riber.Api.Common.Config;
+using Riber.Api.Common.Transformers;
+using Riber.Api.Middlewares;
+using Riber.Application;
+using Riber.Infrastructure;
+using Riber.Infrastructure.Persistence;
+using Riber.Infrastructure.Persistence.Identity;
+using Riber.Infrastructure.Settings;
+
+namespace Riber.Api.Common;
+
+internal static class BuilderExtension
+{
+ extension(WebApplicationBuilder builder)
+ {
+ public void AddPipeline()
+ {
+ builder.AddJsonConfiguration();
+ builder.AddDocumentationApi();
+ builder.AddDependencyInjection();
+ builder.AddConfigurations();
+ builder.AddMiddleware();
+ builder.AddSecurity();
+ }
+
+ private void AddDependencyInjection()
+ {
+ builder.Services.AddApplication();
+ builder.Services.AddInfrastructure(builder.Configuration, builder.Logging);
+ }
+
+ private void AddMiddleware()
+ {
+ builder.Services.AddScoped();
+ }
+
+ private void AddConfigurations()
+ {
+ builder.WebHost.ConfigureKestrel(options =>
+ {
+ options.AddServerHeader = false;
+ options.ConfigureEndpointDefaults(endpoint
+ => endpoint.Protocols = HttpProtocols.Http1AndHttp2AndHttp3);
+ });
+
+ builder.Configuration.AddEnvironmentVariables();
+ builder.Services.AddExceptionHandler();
+ builder.Services.AddProblemDetails();
+ builder.Services.AddMemoryCache();
+
+ builder.Services
+ .AddOptions()
+ .Bind(builder.Configuration.GetSection(nameof(AccessTokenSettings))) // <- Atribuí os valores
+ .ValidateDataAnnotations() // ⇽ Pega as regras de validação
+ .ValidateOnStart(); // ← Faz a validação, se não tiver válido, cancela a execução do programa
+
+ builder.Services
+ .AddOptions()
+ .Bind(builder.Configuration.GetSection(nameof(RefreshTokenSettings)))
+ .ValidateDataAnnotations()
+ .ValidateOnStart();
+
+ builder.Services
+ .Configure(options
+ => options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
+
+ // Para uso das políticas, consulte: docs/REQUEST-TIMEOUT.md
+ builder.Services.AddRequestTimeouts(options =>
+ {
+ options.DefaultPolicy = new RequestTimeoutPolicy { Timeout = TimeSpan.FromMinutes(1) };
+ options.AddPolicy("fast", TimeSpan.FromSeconds(15));
+ options.AddPolicy("standard", TimeSpan.FromSeconds(30));
+ options.AddPolicy("slow", TimeSpan.FromMinutes(1));
+ options.AddPolicy("upload", TimeSpan.FromMinutes(5));
+ });
+
+ builder.Services.AddApiVersioning(options =>
+ {
+ // Define a versão padrão da API (1.0)
+ options.DefaultApiVersion = new ApiVersion(1, 0);
+ // Se o cliente não especificar a versão, assume a versão padrão
+ options.AssumeDefaultVersionWhenUnspecified = true;
+ // Define COMO o cliente pode especificar a versão
+ options.ApiVersionReader = ApiVersionReader.Combine(
+ new UrlSegmentApiVersionReader(), // via URL: /api/v1/company
+ new QueryStringApiVersionReader("version"), // via query: ?version=1.0
+ new HeaderApiVersionReader("X-Version") // via header: X-Version: 1.0
+ );
+ }).AddApiExplorer(setup =>
+ {
+ setup.GroupNameFormat = "'v'VVV";
+ setup.SubstituteApiVersionInUrl = true;
+ });
+ }
+
+ private void AddSecurity()
+ {
+ var accessToken =
+ builder.Configuration.GetSection(nameof(AccessTokenSettings)).Get()
+ ?? throw new InvalidOperationException($"{nameof(AccessTokenSettings)} configuration not found");
+
+ var refreshToken =
+ builder.Configuration.GetSection(nameof(RefreshTokenSettings)).Get()
+ ?? throw new InvalidOperationException($"{nameof(RefreshTokenSettings)} configuration not found");
+
+ builder.Services.AddIdentity()
+ .AddEntityFrameworkStores()
+ .AddDefaultTokenProviders();
+
+ builder.Services
+ .AddAuthentication(options =>
+ {
+ options.DefaultAuthenticateScheme = nameof(AccessTokenSettings);
+ options.DefaultChallengeScheme = nameof(AccessTokenSettings);
+ })
+ .AddJwtBearer(nameof(AccessTokenSettings), options
+ => options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(accessToken.SecretKey)),
+ ValidateIssuer = true,
+ ValidIssuer = accessToken.Issuer,
+ ValidateAudience = true,
+ ValidAudience = accessToken.Audience,
+ ValidateLifetime = true, // ← Valida expiração
+ ClockSkew = TimeSpan.Zero, // ← Remove tolerância de tempo
+ RequireExpirationTime = true // ← Exige claim 'exp'
+ }
+ )
+ .AddJwtBearer(nameof(RefreshTokenSettings), options
+ => options.TokenValidationParameters = new TokenValidationParameters
+ {
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(refreshToken.SecretKey)),
+ ValidateIssuer = true,
+ ValidIssuer = refreshToken.Issuer,
+ ValidateAudience = true,
+ ValidAudience = refreshToken.Audience,
+ ValidateLifetime = true,
+ ClockSkew = TimeSpan.Zero,
+ RequireExpirationTime = true
+ });
+
+ builder.Services.AddSingleton();
+ builder.Services.AddScoped();
+ builder.Services.AddAuthorization();
+ }
+
+ private void AddDocumentationApi()
+ {
+ builder.Services.AddOpenApi(options
+ => options.AddDocumentTransformer());
+ }
+
+ private void AddJsonConfiguration()
+ {
+ builder.Services.ConfigureHttpJsonOptions(options =>
+ {
+ options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
+ options.SerializerOptions.PropertyNameCaseInsensitive = true;
+ options.SerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
+ options.SerializerOptions.AllowTrailingCommas = true;
+ options.SerializerOptions.WriteIndented = true;
+ options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.Never;
+ });
+
+ builder.Services
+ .AddControllers()
+ .ConfigureInvalidModelStateResponse()
+ .AddJsonOptions(options =>
+ {
+ options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
+ options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
+ options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.Never;
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Riber.Api/Common/Config/ApiBehavior.cs b/src/Riber.Api/Common/Config/ApiBehavior.cs
new file mode 100644
index 0000000..a082b3d
--- /dev/null
+++ b/src/Riber.Api/Common/Config/ApiBehavior.cs
@@ -0,0 +1,39 @@
+using System.Net;
+using Microsoft.AspNetCore.Mvc;
+using Riber.Application.Common;
+using EmptyResult = Microsoft.AspNetCore.Mvc.EmptyResult;
+
+namespace Riber.Api.Common.Config;
+
+internal static class ApiBehavior
+{
+ extension(IMvcBuilder builder)
+ {
+ public IMvcBuilder ConfigureInvalidModelStateResponse()
+ {
+ return builder.ConfigureApiBehaviorOptions(options =>
+ options.InvalidModelStateResponseFactory = CreateInvalidModelStateResponse);
+ }
+ }
+
+ private static IActionResult CreateInvalidModelStateResponse(ActionContext context)
+ {
+ var errors = context.ModelState
+ .Where(e => e.Value?.Errors.Count > 0)
+ .ToDictionary(
+ kvp => kvp.Key.Replace("$.", "").ToLower(),
+ kvp => kvp.Value!.Errors
+ .Select(e => e.ErrorMessage)
+ .ToArray()
+ );
+
+ errors.Remove("command", out var command);
+ var result = Result.Failure(
+ command?[0] ?? "Dados de entrada inválidos.",
+ HttpStatusCode.BadRequest,
+ errors
+ );
+
+ return new BadRequestObjectResult(result);
+ }
+}
\ No newline at end of file
diff --git a/src/Riber.Api/Common/Api/GlobalExceptionHandler.cs b/src/Riber.Api/Common/Config/GlobalExceptionHandler.cs
similarity index 75%
rename from src/Riber.Api/Common/Api/GlobalExceptionHandler.cs
rename to src/Riber.Api/Common/Config/GlobalExceptionHandler.cs
index 5dfa7fe..862c0c1 100644
--- a/src/Riber.Api/Common/Api/GlobalExceptionHandler.cs
+++ b/src/Riber.Api/Common/Config/GlobalExceptionHandler.cs
@@ -1,17 +1,16 @@
-using Microsoft.AspNetCore.Diagnostics;
+using System.Net;
+using Microsoft.AspNetCore.Diagnostics;
using Riber.Api.Extensions;
-using Riber.Application.Exceptions;
using Riber.Domain.Exceptions;
-using System.Net;
using Layer = Riber.Application.Exceptions;
-namespace Riber.Api.Common.Api;
+namespace Riber.Api.Common.Config;
///
/// Trata exceções globais na aplicação registrando erros e formatando
/// uma resposta JSON padronizada para o cliente.
///
-public sealed class GlobalExceptionHandler(
+internal sealed class GlobalExceptionHandler(
ILogger logger)
: IExceptionHandler
{
@@ -31,9 +30,7 @@ public async ValueTask TryHandleAsync(
private static (HttpStatusCode, string, Dictionary?) MapExceptionToError(Exception exception)
=> exception switch
{
- RequestTimeoutException timeoutEx => (timeoutEx.Code, timeoutEx.Message, null),
- ValidationException validationEx => (HttpStatusCode.BadRequest, "Dados Inválidos.", validationEx.Details),
- Layer.ApplicationException applicationEx => (applicationEx.Code, applicationEx.Message, null),
+ Layer.ApplicationException applicationEx => (applicationEx.Code, applicationEx.Message, applicationEx.Details),
DomainException domainEx => (HttpStatusCode.UnprocessableContent, domainEx.Message, null),
_ => (HttpStatusCode.InternalServerError, "Erro inesperado no servidor.", null)
};
diff --git a/src/Riber.Api/Common/Logs/.gitkeep b/src/Riber.Api/Common/Logs/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/src/Riber.Api/Common/Transformers/BearerSecuritySchemeTransformer.cs b/src/Riber.Api/Common/Transformers/BearerSecuritySchemeTransformer.cs
new file mode 100644
index 0000000..60ac568
--- /dev/null
+++ b/src/Riber.Api/Common/Transformers/BearerSecuritySchemeTransformer.cs
@@ -0,0 +1,40 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.OpenApi;
+using Microsoft.OpenApi;
+
+namespace Riber.Api.Common.Transformers;
+
+internal class BearerSecuritySchemeTransformer
+ : IOpenApiDocumentTransformer
+{
+ public Task TransformAsync(
+ OpenApiDocument document,
+ OpenApiDocumentTransformerContext context,
+ CancellationToken cancellationToken)
+ {
+ document.Info = new() { Title = "Riber Documentation API", Version = "v1", Description = "API do Riber" };
+
+ document.Components ??= new OpenApiComponents();
+ document.Components.SecuritySchemes = new Dictionary
+ {
+ ["Bearer"] = new OpenApiSecurityScheme
+ {
+ Type = SecuritySchemeType.Http,
+ Scheme = "bearer",
+ BearerFormat = "JWT",
+ In = ParameterLocation.Header,
+ Description = "Insert your JWT Token here",
+ Name = "Authorization"
+ }
+ };
+ document.Security =
+ [
+ new OpenApiSecurityRequirement
+ {
+ [new OpenApiSecuritySchemeReference("Bearer", document)] = []
+ }
+ ];
+
+ return Task.CompletedTask;
+ }
+}
\ No newline at end of file
diff --git a/src/Riber.Api/Extensions/HttpContextExtension.cs b/src/Riber.Api/Extensions/HttpContextExtension.cs
index 49cf05d..6e004c1 100644
--- a/src/Riber.Api/Extensions/HttpContextExtension.cs
+++ b/src/Riber.Api/Extensions/HttpContextExtension.cs
@@ -20,7 +20,7 @@ public static async Task WriteErrorResponse(
Dictionary? details = null,
CancellationToken cancellationToken = default)
{
- var response = Result.Failure