From 6b2aa8d25b117669ba853308b5472bcbe65f2aa4 Mon Sep 17 00:00:00 2001 From: iammukeshm Date: Wed, 30 Sep 2026 09:52:31 +0530 Subject: [PATCH] docs: Files purge jobs run per tenant; activity page recent history (#1419, #1420) --- src/content/docs/changelog/index.mdx | 2 ++ src/content/docs/frontend/dashboard.mdx | 2 +- src/content/docs/modules/files.mdx | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/content/docs/changelog/index.mdx b/src/content/docs/changelog/index.mdx index c3c9bbce..6cbb9f0d 100644 --- a/src/content/docs/changelog/index.mdx +++ b/src/content/docs/changelog/index.mdx @@ -13,6 +13,8 @@ Notable changes to the kit, newest first. ## 2026-09-30 +- **Files: the purge jobs now clean every tenant and refund storage quota (fix).** `PurgeOrphanedFilesJob` and `PurgeDeletedFilesJob` ran without a tenant, so they only reached the shared database: tenants with a dedicated database kept expired uploads and soft-deleted files (rows and blobs) forever. Both jobs now run per tenant inside that tenant's context, lifting only the soft-delete filter. Purging soft-deleted files also refunds the freed bytes to the owning tenant's storage quota, a refund that was silently lost before. See [#1419](https://github.com/fullstackhero/dotnet-starter-kit/pull/1419). +- **Dashboard: Live activity keeps its history across a refresh.** Users with `Permissions.AuditTrails.View` now see a **Recent history** section on `/activity`: the latest 25 audited events for the tenant, without per-request system activity, loaded once per visit with a manual refresh. Live SSE events stay in memory in their own section and are never written to browser storage. Users without the permission get a live-only view that explains where past activity lives, and the overview's **Recent audits** card no longer calls `/audits` for them (it showed a `403` before). See [#1420](https://github.com/fullstackhero/dotnet-starter-kit/pull/1420). - **Auditing: the retention job now actually purges audit records, in every tenant (fix).** The `auditing-retention` Hangfire job is registered without a tenant, and `AuditRecords` carries the default-on tenant filter, so every run threw a `NullReferenceException` inside the filter and deleted nothing: with `Auditing:Retention:Enabled` on, audit tables still grew without limit. The job now loads every tenant from the tenant store and runs the sweep inside each tenant's context, which also reaches tenants with a dedicated database. A failure in one tenant is logged with its `TenantId` and the other tenants still run. Retention options and batching are unchanged. See [#1413](https://github.com/fullstackhero/dotnet-starter-kit/pull/1413). - **Production mail settings now bind (fix).** `appsettings.Production.json` put `Host`, `Port`, `UserName` and `Password` directly under `MailOptions` instead of `MailOptions:Smtp`, so they bound to nothing and Production silently inherited the base `smtp.ethereal.email` host. They now sit under `MailOptions:Smtp`, with the port defaulting to `587`. Set `MailOptions__Smtp__Host` (and credentials) for Production; with the blank default, sending fails at send time. See [#1414](https://github.com/fullstackhero/dotnet-starter-kit/pull/1414). - **Docker: the API and DbMigrator images build on supported base images.** Both Dockerfiles moved from `mcr.microsoft.com/dotnet/nightly/aspnet:10.0-noble-chiseled` to the supported `mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled`. The SDK container publish (`ContainerFamily`, used by the AWS deploy) stays on full `noble` on purpose. See [#1414](https://github.com/fullstackhero/dotnet-starter-kit/pull/1414). diff --git a/src/content/docs/frontend/dashboard.mdx b/src/content/docs/frontend/dashboard.mdx index 463309f3..7c744244 100644 --- a/src/content/docs/frontend/dashboard.mdx +++ b/src/content/docs/frontend/dashboard.mdx @@ -192,7 +192,7 @@ The context handles: - Automatic reconnect with backoff + jitter (capped at 60 s), with status (`idle / connecting / connected / reconnecting / error`) surfaced as a state pill. - Transport negotiation (WebSockets, then SSE, then long-polling) for proxy-hostile networks. -**SSE** - `clients/dashboard/src/sse/sse-context.tsx` streams the live activity feed. The client first exchanges its JWT for a short-lived stream token (`POST /api/v1/sse/token`), then opens `GET /api/v1/sse/stream?token=...` and parses the `text/event-stream` itself (fetch + ReadableStream, with reconnect/backoff). Overview and the Activity page render the same event buffer. +**SSE** - `clients/dashboard/src/sse/sse-context.tsx` streams the live activity feed. The client first exchanges its JWT for a short-lived stream token (`POST /api/v1/sse/token`), then opens `GET /api/v1/sse/stream?token=...` and parses the `text/event-stream` itself (fetch + ReadableStream, with reconnect/backoff). Overview and the Activity page render the same event buffer. The buffer lives in memory only and is empty after a refresh; the Activity page adds a **Recent history** section (latest 25 audits, loaded once per visit) for users with `Permissions.AuditTrails.View`, and the Overview's Recent audits card is gated on the same permission.