Compare commits

...

23 Commits

Author SHA1 Message Date
khwezi edabe266e5 Merge pull request 'Refactored https logni proto handling' (#44) from mock-data into main
Reviewed-on: #44
2026-06-05 06:44:12 +02:00
Khwezi Mngoma 58dc67e680 Refactored https logni proto handling
continuous-integration/drone/pr Build is passing
2026-06-05 06:43:36 +02:00
khwezi 248dd32b1b Merge pull request 'Added support for forwarded headers' (#43) from mock-data into main
Reviewed-on: #43
2026-06-05 06:30:37 +02:00
Khwezi Mngoma 5123a4d3ac Added support for forwarded headers
continuous-integration/drone/pr Build is passing
2026-06-05 06:29:55 +02:00
khwezi 1645b6bbae Merge pull request 'Fixed secrets mappings' (#42) from mock-data into main
Reviewed-on: #42
2026-06-05 06:17:30 +02:00
Khwezi Mngoma ae51a3a864 Fixed secrets mappings
continuous-integration/drone/pr Build is passing
2026-06-05 06:16:42 +02:00
khwezi 72725a302a Merge pull request 'mock-data' (#41) from mock-data into main
Reviewed-on: #41
2026-06-05 05:58:44 +02:00
Khwezi Mngoma 31a640d672 Stable security
continuous-integration/drone/pr Build is passing
2026-06-05 05:58:05 +02:00
Khwezi Mngoma 097ecd6421 Configured security 2026-06-04 14:45:33 +02:00
khwezi f3d79174be Merge pull request 'Upgraded quartz' (#40) from mock-data into main
Reviewed-on: #40
2026-06-03 11:54:17 +02:00
Khwezi Mngoma 9b3e889d89 Upgraded quartz
continuous-integration/drone/pr Build is passing
2026-06-03 11:53:31 +02:00
khwezi c086aa60e4 Merge pull request 'Updated backend' (#39) from mock-data into main
Reviewed-on: #39
2026-06-02 00:31:16 +02:00
Khwezi Mngoma e35a68f7e8 Updated backend
continuous-integration/drone/pr Build is passing
2026-06-02 00:30:47 +02:00
khwezi 66b377bf69 Merge pull request 'Fixed event service discovery issue' (#38) from mock-data into main
Reviewed-on: #38
2026-06-01 23:39:37 +02:00
Khwezi Mngoma b70ecab9ea Fixed event service discovery issue
continuous-integration/drone/pr Build is passing
2026-06-01 23:39:05 +02:00
khwezi 82389a9304 Merge pull request 'Updated backend' (#37) from mock-data into main
Reviewed-on: #37
2026-06-01 22:58:55 +02:00
Khwezi Mngoma 209947d70f Updated backend
continuous-integration/drone/pr Build is passing
2026-06-01 22:58:10 +02:00
khwezi 4bf1d2e77a Merge pull request 'Ensured appsettings aligns with k8s config' (#36) from mock-data into main
Reviewed-on: #36
2026-06-01 16:37:57 +02:00
Khwezi Mngoma edb9c281ef Ensured appsettings aligns with k8s config
continuous-integration/drone/pr Build is passing
2026-06-01 11:57:44 +02:00
khwezi 2b6576de85 Merge pull request 'Refactored app k8s manifest' (#35) from mock-data into main
Reviewed-on: #35
2026-06-01 11:16:54 +02:00
Khwezi Mngoma b189b26b62 Refactored app k8s manifest
continuous-integration/drone/pr Build is passing
2026-06-01 11:15:54 +02:00
khwezi 7de957ed6f Merge pull request 'Refactored manifest to include s3 bucket and HasherService configs and secrets' (#34) from mock-data into main
Reviewed-on: #34
2026-06-01 09:50:59 +02:00
Khwezi Mngoma 52e3ab16bf Refactored manifest to include s3 bucket and HasherService configs and secrets
continuous-integration/drone/pr Build is failing
Upgraded nuget packages to bring in new Payment and Product service functionality
2026-06-01 09:50:14 +02:00
10 changed files with 298 additions and 209 deletions
@@ -167,7 +167,7 @@
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /> <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
<circle cx="12" cy="7" r="4" /> <circle cx="12" cy="7" r="4" />
</svg> </svg>
LogIn Account
</a> </a>
<a href="/profile" class="btn btn-sm btn-dark rounded-circle d-inline-flex d-md-none align-items-center justify-content-center border-0 p-0 shadow-sm" <a href="/profile" class="btn btn-sm btn-dark rounded-circle d-inline-flex d-md-none align-items-center justify-content-center border-0 p-0 shadow-sm"
@@ -1,4 +1,4 @@
using LiteCharms.Features.MidrandBooks; using LiteCharms.Features;
using LiteCharms.Features.MidrandBooks.AuthorBooks; using LiteCharms.Features.MidrandBooks.AuthorBooks;
using LiteCharms.Features.MidrandBooks.Authors; using LiteCharms.Features.MidrandBooks.Authors;
using LiteCharms.Features.MidrandBooks.Categories; using LiteCharms.Features.MidrandBooks.Categories;
@@ -1,4 +1,4 @@
using LiteCharms.Features.MidrandBooks; using LiteCharms.Features;
using LiteCharms.Features.MidrandBooks.Authors; using LiteCharms.Features.MidrandBooks.Authors;
using LiteCharms.Features.MidrandBooks.Authors.Models; using LiteCharms.Features.MidrandBooks.Authors.Models;
using LiteCharms.Features.MidrandBooks.Products; using LiteCharms.Features.MidrandBooks.Products;
@@ -35,7 +35,6 @@ public partial class ProductView : ComponentBase
CurrentAuthor = null; CurrentAuthor = null;
Thumbnails.Clear(); Thumbnails.Clear();
// 1. Resolve product listing details
var productResult = await ProductService.GetProductAsync(BookId); var productResult = await ProductService.GetProductAsync(BookId);
if (productResult.IsSuccess && productResult.Value != null) if (productResult.IsSuccess && productResult.Value != null)
@@ -43,31 +42,24 @@ public partial class ProductView : ComponentBase
CurrentProduct = productResult.Value; CurrentProduct = productResult.Value;
AuthorName = CurrentProduct.Metadata?.Manufacturer ?? "Unknown Author"; AuthorName = CurrentProduct.Metadata?.Manufacturer ?? "Unknown Author";
// 2. Load pricing data
var priceResult = await ProductService.GetProductPriceAsync(BookId); var priceResult = await ProductService.GetProductPriceAsync(BookId);
LivePrice = priceResult.IsSuccess ? priceResult.Value.Amount : 0m; LivePrice = priceResult.IsSuccess ? priceResult.Value.Amount : 0m;
// 3. Extract active catalog categories
var categoryResult = await ProductService.GetProductCategoriesAsync(BookId); var categoryResult = await ProductService.GetProductCategoriesAsync(BookId);
if (categoryResult.IsSuccess && categoryResult.Value.Length > 0) if (categoryResult.IsSuccess && categoryResult.Value.Length > 0)
{
PrimaryCategory = categoryResult.Value[0].Name ?? "General"; PrimaryCategory = categoryResult.Value[0].Name ?? "General";
}
// 4. Retrieve complete contextual model through the newly instantiated AuthorService lookup
var authorResult = await AuthorService.GetAuthorByProductIdAsync(BookId); var authorResult = await AuthorService.GetAuthorByProductIdAsync(BookId);
if (authorResult.IsSuccess && authorResult.Value != null) if (authorResult.IsSuccess && authorResult.Value != null)
{ {
CurrentAuthor = authorResult.Value; CurrentAuthor = authorResult.Value;
// Format fully qualified author text cleanly depending on their publisher model details
if (CurrentAuthor.PublisherType == PublisherTypes.Company && !string.IsNullOrWhiteSpace(CurrentAuthor.Company)) if (CurrentAuthor.PublisherType == PublisherTypes.Company && !string.IsNullOrWhiteSpace(CurrentAuthor.Company))
AuthorName = CurrentAuthor.Company; AuthorName = CurrentAuthor.Company;
else else
AuthorName = $"{CurrentAuthor.Name} {CurrentAuthor.LastName}".Trim(); AuthorName = $"{CurrentAuthor.Name} {CurrentAuthor.LastName}".Trim();
} }
// 5. Build presentation image viewer variables
if (!string.IsNullOrWhiteSpace(CurrentProduct.ImageUrl)) if (!string.IsNullOrWhiteSpace(CurrentProduct.ImageUrl))
Thumbnails.Add(CurrentProduct.ImageUrl); Thumbnails.Add(CurrentProduct.ImageUrl);
+16 -3
View File
@@ -1,6 +1,11 @@
@page "/profile" @page "/profile"
@using Microsoft.AspNetCore.Components.Authorization
@inject NavigationManager Navigation
@rendermode InteractiveServer
<div class="container py-5"> <AuthorizeView>
<Authorized>
<div class="container py-5">
<h2 class="fw-bold mb-5 tracking-tight">My Account</h2> <h2 class="fw-bold mb-5 tracking-tight">My Account</h2>
<div class="row g-5"> <div class="row g-5">
<div class="col-md-3"> <div class="col-md-3">
@@ -9,7 +14,7 @@
<button class="nav-link text-start" data-bs-toggle="pill" data-bs-target="#shipping" role="tab">Shipping Address</button> <button class="nav-link text-start" data-bs-toggle="pill" data-bs-target="#shipping" role="tab">Shipping Address</button>
<button class="nav-link text-start" data-bs-toggle="pill" data-bs-target="#profile" role="tab">Profile Settings</button> <button class="nav-link text-start" data-bs-toggle="pill" data-bs-target="#profile" role="tab">Profile Settings</button>
<hr /> <hr />
<button class="nav-link text-danger text-start">Logout</button> <button class="nav-link text-danger text-start" @onclick="TriggerLogout">Logout</button>
</div> </div>
</div> </div>
@@ -192,7 +197,13 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</Authorized>
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
</AuthorizeView>
@code { @code {
private bool showAddForm = false; private bool showAddForm = false;
@@ -216,6 +227,8 @@
new AddressItem { Id = 3, Name = "Midrand Books Warehouse", Street = "Unit 8, Corporate Park North", City = "Randjespark", PostalCode = "1683", IsBilling = false, IsShipping = true, IsPrimary = false } new AddressItem { Id = 3, Name = "Midrand Books Warehouse", Street = "Unit 8, Corporate Park North", City = "Randjespark", PostalCode = "1683", IsBilling = false, IsShipping = true, IsPrimary = false }
}; };
private void TriggerLogout() => Navigation.NavigateTo("/logout", forceLoad: true);
private void DownloadInvoice(string orderId) private void DownloadInvoice(string orderId)
{ {
// Handle invoice downloading logic here // Handle invoice downloading logic here
@@ -0,0 +1,10 @@
@inject NavigationManager Navigation
@code {
protected override void OnInitialized()
{
var returnUrl = Navigation.ToBaseRelativePath(Navigation.Uri);
Navigation.NavigateTo($"/login?redirectUri={Uri.EscapeDataString(returnUrl)}", forceLoad: true);
}
}
+12 -4
View File
@@ -1,7 +1,14 @@
@using MidrandBookshop.Components.Pages @using Microsoft.AspNetCore.Components.Authorization
<Router AppAssembly="@typeof(Program).Assembly"> @using MidrandBookshop.Components.Pages
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData"> <Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found> </Found>
<NotFound> <NotFound>
@@ -9,4 +16,5 @@
<NotFound /> <NotFound />
</LayoutView> </LayoutView>
</NotFound> </NotFound>
</Router> </Router>
</CascadingAuthenticationState>
+2 -2
View File
@@ -18,13 +18,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="LiteCharms.Features" Version="1.57.0" /> <PackageReference Include="LiteCharms.Features" Version="1.83.0" />
</ItemGroup> </ItemGroup>
<!-- UI --> <!-- UI -->
<ItemGroup> <ItemGroup>
<PackageReference Include="ANM.Blazored.Toast" Version="0.1.1" /> <PackageReference Include="ANM.Blazored.Toast" Version="0.1.1" />
<PackageReference Include="LiteCharms.Features.MidrandBooks" Version="1.57.0" /> <PackageReference Include="LiteCharms.Features.MidrandBooks" Version="1.83.0" />
<!-- Global Usings --> <!-- Global Usings -->
<Using Include="Blazored.Toast.Services" /> <Using Include="Blazored.Toast.Services" />
+16
View File
@@ -1,9 +1,12 @@
using LiteCharms.Features.Extensions; using LiteCharms.Features.Extensions;
using LiteCharms.Features.Mediator; using LiteCharms.Features.Mediator;
using LiteCharms.Features.MidrandBooks.Extensions; using LiteCharms.Features.MidrandBooks.Extensions;
using Microsoft.AspNetCore.HttpOverrides;
using MidrandBookshop.Components; using MidrandBookshop.Components;
using static LiteCharms.Features.Extensions.Quartz; using static LiteCharms.Features.Extensions.Quartz;
AppContext.SetSwitch("Microsoft.IdentityModel.DisableTelemetry", true);
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents() builder.Services.AddRazorComponents()
@@ -13,6 +16,7 @@ builder.AddMonitoring();
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddMediator(); builder.Services.AddMediator();
builder.Services.AddAuthentikUiSecurity(builder.Configuration);
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TelemetryPipelineBehavior<,>)); builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TelemetryPipelineBehavior<,>));
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingPipelineBehavior<,>)); builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingPipelineBehavior<,>));
@@ -22,15 +26,27 @@ builder.Services.AddQuartzSchedulerClient(MidrandShopSchedulerName, builder.Conf
builder.Services.AddEmailServices(builder.Configuration); builder.Services.AddEmailServices(builder.Configuration);
builder.Services.AddEmailServiceBus(); builder.Services.AddEmailServiceBus();
builder.Services.AddHttpClient();
builder.Services.AddShopServices(); builder.Services.AddShopServices();
builder.Services.AddHashServices(builder.Configuration);
builder.Services.AddMidrandShopDatabase(builder.Configuration); builder.Services.AddMidrandShopDatabase(builder.Configuration);
builder.Services.AddMidrandShopPostgresHealthCheck(); builder.Services.AddMidrandShopPostgresHealthCheck();
builder.Services.AddMidrandShopQuartzHealthCheck(); builder.Services.AddMidrandShopQuartzHealthCheck();
builder.Services.AddHealthChecksSupport(builder.Configuration); builder.Services.AddHealthChecksSupport(builder.Configuration);
builder.Services.AddCascadingAuthenticationState();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownProxies.Clear();
});
var app = builder.Build(); var app = builder.Build();
app.UseForwardedHeaders();
app.AddSecurityEndpoints();
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>(); var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
var scheduler = await schedulerFactory.GetScheduler(MidrandShopSchedulerName); var scheduler = await schedulerFactory.GetScheduler(MidrandShopSchedulerName);
+8
View File
@@ -1,4 +1,12 @@
{ {
"AuthentikSettings": {
"Authority": "https://id.khongisa.co.za/application/o/midrand-books-uat/",
"MetadataEndpoint": "https://id.khongisa.co.za/application/o/midrand-books-uat/.well-known/openid-configuration",
"RevokationEndpoint": "https://id.khongisa.co.za/application/o/revoke/"
},
"HasherSettings": {
"MinHashLength": 11
},
"BookshopS3Settings": { "BookshopS3Settings": {
"ServiceUrl": "http://192.168.1.177:30900", "ServiceUrl": "http://192.168.1.177:30900",
"Region": "garage", "Region": "garage",
+52 -10
View File
@@ -14,6 +14,22 @@ data:
ASPNETCORE_URLS: "http://0.0.0.0:8080" ASPNETCORE_URLS: "http://0.0.0.0:8080"
Monitoring__Address: "http://aspire-dashboard-service.aspire.svc.cluster.local:18889" Monitoring__Address: "http://aspire-dashboard-service.aspire.svc.cluster.local:18889"
Monitoring__ServiceName: "MidrandBooks.Uat" Monitoring__ServiceName: "MidrandBooks.Uat"
HasherSettings__MinHashLength: "11"
BookshopS3Settings__ServiceUrl: "http://garage.garage.svc.cluster.local:3900"
BookshopS3Settings__Region: "garage"
BookshopS3Settings__BucketName: "bookshop"
BookshopS3Settings__CdnBaseUrl: "https://bookshop.cdn.khongisa.co.za"
ValidPayfastHosts__0: "www.payfast.co.za"
ValidPayfastHosts__1: "sandbox.payfast.co.za"
ValidPayfastHosts__2: "w1w.payfast.co.za"
ValidPayfastHosts__3: "w2w.payfast.co.za"
ValidPayfastHosts__4: "ips.payfast.co.za"
ValidPayfastHosts__5: "api.payfast.co.za"
ValidPayfastHosts__6: "payment.payfast.io"
AuthentikSettings__Authority: "https://id.khongisa.co.za/application/o/midrand-books-api-uat/"
AuthentikSettings__MetadataEndpoint: "https://id.khongisa.co.za/application/o/midrand-books-uat/.well-known/openid-configuration"
AuthentikSettings__RevokationEndpoint: "https://id.khongisa.co.za/application/o/revoke/"
ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true"
--- ---
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
@@ -25,6 +41,12 @@ data:
connection-string: SG9zdD0xOTIuMTY4LjEuMTcwO0RhdGFiYXNlPW1pZHJhbmRzaG9wLWRldjtVc2VybmFtZT1taWRyYW5kc2hvcC1kZXYtdXNlcjtQYXNzd29yZD1hUFh5a0tnM3RTOWNtRDtQZXJzaXN0IFNlY3VyaXR5IEluZm89VHJ1ZQ== connection-string: SG9zdD0xOTIuMTY4LjEuMTcwO0RhdGFiYXNlPW1pZHJhbmRzaG9wLWRldjtVc2VybmFtZT1taWRyYW5kc2hvcC1kZXYtdXNlcjtQYXNzd29yZD1hUFh5a0tnM3RTOWNtRDtQZXJzaXN0IFNlY3VyaXR5IEluZm89VHJ1ZQ==
connection-string-quartz: SG9zdD0xOTIuMTY4LjEuMTcwO0RhdGFiYXNlPXNjaGVkdWxlci1kZXY7VXNlcm5hbWU9c2NoZWR1bGVyLWRldi11c2VyO1Bhc3N3b3JkPWtWVm1vV0tKM3h6Z1FYO1BlcnNpc3QgU2VjdXJpdHkgSW5mbz1UcnVl connection-string-quartz: SG9zdD0xOTIuMTY4LjEuMTcwO0RhdGFiYXNlPXNjaGVkdWxlci1kZXY7VXNlcm5hbWU9c2NoZWR1bGVyLWRldi11c2VyO1Bhc3N3b3JkPWtWVm1vV0tKM3h6Z1FYO1BlcnNpc3QgU2VjdXJpdHkgSW5mbz1UcnVl
aspire-apikey: bWMzRzYzSzJqNVpPRXNpMEFqTW9qTFRYbTFLRVpGY3R6SUlqU3dEaVRHdXQ4cUdTa1B1V3d4R1AxUmJzY0pVbw== aspire-apikey: bWMzRzYzSzJqNVpPRXNpMEFqTW9qTFRYbTFLRVpGY3R6SUlqU3dEaVRHdXQ4cUdTa1B1V3d4R1AxUmJzY0pVbw==
hasher-salt: VEdsbmFIUWdRMmhoY20xekxDQk5hV1J5WVc1a1FtOXZhM01nYldGclpTQnNiM1J6SUc5bUlHMXZibVY1SUdGdVpDQmhjbVVnWVNCemRXTmpaWE56Wm5Wc0lIWnBjbUZzSUhOMGIzSjVJR2x1SUZOdmRYUm9JRUZtY21sallRPT0=
hasher-payfastpassphrase: OUdBSVIwdFdwaFgwcU8=
bookshop-s3-accesskey: R0s1MTRkMmNlOGRjNjkyMzdhMDVjMDFlZWY=
bookshop-s3-secretkey: ZWFhZmVkYTFhZWQ0MDllY2ZlNjA3MTRlY2RhNTQ5YjgyYmRmNWEzZGFmOWYxOGRkNjFmNjZiNDk3M2E2NDgyZQ==
authentik-clientid: Nm9oZk1lSndQNWR0YWY1RFMzZU9MY2NNSHF6WXlma1YzRTNGeE5Tbw==
authentik-clientsecret: TXV2a0FLQklHR3BkdEsyaFlabVU1dFRaUmNuM2FhRzhoMWhlVE1nazFYOGVwczYyMzNCS0REWGdpNXo0T01RalVzMGZEUEFmakpmVVRNN1h3ZjllMU01MTQyVGlvOXRycUdmZTM1THhPaExEUnp6N2gxSm5jVkNLYXZXUllndmQ=
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
@@ -77,6 +99,36 @@ spec:
- configMapRef: - configMapRef:
name: midrandbooks-config name: midrandbooks-config
env: env:
- name: AuthentikSettings__ClientId
valueFrom:
secretKeyRef:
name: midrandbooks-secrets
key: authentik-clientid
- name: AuthentikSettings__ClientSecret
valueFrom:
secretKeyRef:
name: midrandbooks-secrets
key: authentik-clientsecret
- name: BookshopS3Settings__AccessKey
valueFrom:
secretKeyRef:
name: midrandbooks-secrets
key: bookshop-s3-accesskey
- name: BookshopS3Settings__SecretKey
valueFrom:
secretKeyRef:
name: midrandbooks-secrets
key: bookshop-s3-secretkey
- name: HasherSettings__Salt
valueFrom:
secretKeyRef:
name: midrandbooks-secrets
key: hasher-salt
- name: HasherSettings__PayfastPassphrase
valueFrom:
secretKeyRef:
name: midrandbooks-secrets
key: hasher-payfastpassphrase
- name: ConnectionStrings__PostgresScheduler - name: ConnectionStrings__PostgresScheduler
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
@@ -87,16 +139,6 @@ spec:
secretKeyRef: secretKeyRef:
name: midrandbooks-secrets name: midrandbooks-secrets
key: connection-string key: connection-string
- name: Monitoring__Address
valueFrom:
configMapKeyRef:
name: midrandbooks-config
key: Monitoring__Address
- name: Monitoring__ServiceName
valueFrom:
configMapKeyRef:
name: midrandbooks-config
key: Monitoring__ServiceName
- name: Monitoring__ApiKey - name: Monitoring__ApiKey
valueFrom: valueFrom:
secretKeyRef: secretKeyRef: