Used shared components
continuous-integration/drone/pr Build is passing

Built loopbackip check override based on environment
This commit is contained in:
Khwezi Mngoma
2026-06-03 00:47:54 +02:00
parent 8eedf16a49
commit 8be8eb52bc
9 changed files with 11 additions and 143 deletions
@@ -1,7 +0,0 @@
namespace MidrandBooksApi;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class ApiVersionTargetAttribute(int majorVersion) : Attribute
{
public int MajorVersion { get; } = majorVersion;
}
-7
View File
@@ -1,7 +0,0 @@
namespace MidrandBooksApi;
public static class EndpointTags
{
public const string Books = nameof(Books);
public const string Payments = nameof(Payments);
}
-6
View File
@@ -1,6 +0,0 @@
namespace MidrandBooksApi;
public interface IEndpoint
{
void Map(IEndpointRouteBuilder builder);
}
+3 -2
View File
@@ -34,6 +34,7 @@
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="10.0.0" />
<Using Include="Asp.Versioning" />
<Using Include="Asp.Versioning.Builder" />
</ItemGroup>
<!-- API Documentation -->
@@ -53,13 +54,13 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="LiteCharms.Features" Version="1.65.0" />
<PackageReference Include="LiteCharms.Features" Version="1.68.0" />
</ItemGroup>
<!-- UI -->
<ItemGroup>
<PackageReference Include="ANM.Blazored.Toast" Version="0.1.1" />
<PackageReference Include="LiteCharms.Features.MidrandBooks" Version="1.65.0" />
<PackageReference Include="LiteCharms.Features.MidrandBooks" Version="1.68.0" />
<!-- Global Usings -->
<Using Include="Blazored.Toast.Services" />
@@ -1,16 +0,0 @@
namespace MidrandBooksApi;
public sealed class OpenApiBearerSecuritySchemeTransformer : IOpenApiDocumentTransformer
{
public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
{
var bearerScheme = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "bearer",
Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\""
};
document.AddComponent("Bearer", bearerScheme);
}
}
@@ -1,7 +1,11 @@
using LiteCharms.Features.MidrandBooks.Payments;
using LiteCharms.Features.Abstractions;
using LiteCharms.Features.Api;
using LiteCharms.Features.Extensions;
using LiteCharms.Features.MidrandBooks.Payments;
using LiteCharms.Features.MidrandBooks.Payments.Events;
using LiteCharms.Features.MidrandBooks.Payments.Models;
using LiteCharms.Features.Quartz.Abstractions;
using static LiteCharms.Features.Extensions.Api;
namespace MidrandBooksApi.Payments.Endpoints;
@@ -22,7 +26,7 @@ public sealed class ConfirmationEndpoint : IEndpoint
string? remoteIp = request.HttpContext.Connection.RemoteIpAddress?.ToString();
var ipValidation = await payfastService.ValidateReferrerIpAsync(remoteIp!, cancellationToken);
var ipValidation = await payfastService.ValidateReferrerIpAsync(remoteIp!, !hostEnvironment.IsProduction(), cancellationToken);
if (ipValidation.IsFailed || !ipValidation.Value) return Results.Unauthorized();
@@ -54,7 +58,7 @@ public sealed class ConfirmationEndpoint : IEndpoint
return Results.Unauthorized();
var notification = PayfastPaymentConfirmationReceivedEvent.Create(payload, payload.MerchantPaymentId!,
performBackgroundChecks: false); // Set to false because comprehensive checks are completed inline above
allowLoopback: !hostEnvironment.IsProduction(), performBackgroundChecks: false); // Set to false because comprehensive checks are completed inline above
await jobOrchestrator.SendAsync(notification, cancellationToken);
@@ -68,7 +72,7 @@ public sealed class ConfirmationEndpoint : IEndpoint
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Payments);
.WithTags(Api.Payments);
}
private static PayfastWebhookPayload ParseForm(IFormCollection formCollection, string incomingSignature) => new()
-3
View File
@@ -1,9 +1,6 @@
using Asp.Versioning.Builder;
using k8s.Models;
using LiteCharms.Features.Extensions;
using LiteCharms.Features.Mediator;
using LiteCharms.Features.MidrandBooks.Extensions;
using MidrandBooksApi;
using static LiteCharms.Features.Extensions.Quartz;
var builder = WebApplication.CreateBuilder(args);
-90
View File
@@ -1,90 +0,0 @@
namespace MidrandBooksApi;
public static class Setup
{
public static IApplicationBuilder MapEndpoints(this WebApplication app, Dictionary<int, RouteGroupBuilder> versionGroups)
{
var endpoints = app.Services.GetRequiredService<IEnumerable<IEndpoint>>();
foreach (var endpoint in endpoints)
{
var versionAttributes = endpoint.GetType().GetCustomAttributes<ApiVersionTargetAttribute>().ToList();
if (versionAttributes.Count != 0)
{
foreach (var attr in versionAttributes)
if (versionGroups.TryGetValue(attr.MajorVersion, out var targetGroup))
endpoint.Map(targetGroup);
}
else
endpoint.Map(app);
}
return app;
}
public static IServiceCollection AddEndpoints(this IServiceCollection services, Assembly assembly)
{
ServiceDescriptor[] discriptors = [.. assembly.DefinedTypes
.Where(t => t is { IsInterface: false, IsAbstract: false })
.Where(t => t.IsAssignableTo(typeof(IEndpoint)))
.Select(t => ServiceDescriptor.Transient(typeof(IEndpoint), t))];
services.TryAddEnumerable(discriptors);
return services;
}
public static string ToEndpointName(this Type target, string? annotation = "") =>
$"{target.Name.Replace("Endpoint", string.Empty)}{annotation}".ToLower();
public static IServiceCollection AddApiServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddHttpClient();
services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1);
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(),
new QueryStringApiVersionReader("version"),
new QueryStringApiVersionReader("version"),
new MediaTypeApiVersionReader("version"));
})
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
var urls = configuration["ASPNETCORE_URLS"] ?? configuration["Urls"];
var healthUrl = "http://localhost:8080/health";
if (!string.IsNullOrWhiteSpace(urls))
{
string firstUrl = urls.Split(';').FirstOrDefault(s => s.Contains("http://"))!
.Replace("*", "localhost").Replace("+", "localhost");
healthUrl = $"{firstUrl.TrimEnd('/')}/health";
}
services.AddHealthChecksUI(setup =>
{
setup.SetNotifyUnHealthyOneTimeUntilChange();
setup.AddHealthCheckEndpoint("primary, heal", healthUrl);
setup.SetHeaderText("Midrand Books");
})
.AddInMemoryStorage();
services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
options.DefaultExpirationTimeSpan = TimeSpan.FromSeconds(10);
});
services.AddOpenApi(options => options.AddDocumentTransformer<OpenApiBearerSecuritySchemeTransformer>());
return services;
}
}
-8
View File
@@ -1,8 +0,0 @@
## Payfast Payment Confirmation
# This endpoint is used by Payfast to confirm the payment status of a transaction.
# It receives a POST request with the payment details and updates the order status accordingly.
POST {{baseUrl}}/v1/payments/payfast/confirm
Content-Type: application/x-www-form-urlencoded
amount={{amount}}&item_name={{item_name}}&m_payment_id={{paymentId}}&signature={{signature}}