Add cloudflare purge cache by tag (#12788) - #12789
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThe boilerplate updates identity documentation and services, adds token-type JWT audiences, improves Keycloak claim handling, introduces cache-tag CDN purging across zones, reserves deployment domains for tenants, validates production configuration, and updates integration tests. ChangesIdentity and authentication
Response caching
Reserved tenant domains
Template configuration and tests
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant AppResponseCachePolicy
participant OutputCache
participant ResponseCacheService
participant Cloudflare
Client->>AppResponseCachePolicy: Request culture/query variant
AppResponseCachePolicy->>OutputCache: Store response with canonical cache tag
AppResponseCachePolicy-->>Client: Return Cache-Tag when edge-cacheable
ResponseCacheService->>OutputCache: Evict entries by cache tag
ResponseCacheService->>Cloudflare: Purge cache tags across configured zones
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cs (1)
209-222: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExtract the repeated deployment-host array.
The
[Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host, .. serverSharedSettings.TrustedOrigins.Select(ServerSharedSettings.GetTrustedOriginHost)]array is built twice in this method: once for the name check (line 210) and once for the domain check (line 220). Compute it once and reuse it for both checks.♻️ Proposed refactor
private async Task Validate(Tenant tenant, CancellationToken cancellationToken) { var entry = DbContext.Entry(tenant); + var deploymentHosts = new[] { Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host } + .Concat(serverSharedSettings.TrustedOrigins.Select(ServerSharedSettings.GetTrustedOriginHost)) + .ToArray(); // The custom domain is matched against the request host (case-insensitive), so it's stored as a lowercase host; a blank one becomes null. // SECURITY: this endpoint is self-service (tenant admins), and a custom domain wins over the sub domain during resolution. // Enforcing uniqueness alone is NOT enough for production - verify domain ownership and add it to TrustedOrigins first. tenant.Domain = string.IsNullOrWhiteSpace(tenant.Domain) ? null : tenant.Domain.Trim().ToLowerInvariant(); if ((entry.State is EntityState.Added || entry.Property(t => t.Name).IsModified) - && ReservedTenantNames.IsReserved(tenant.Name, [Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host, .. serverSharedSettings.TrustedOrigins.Select(ServerSharedSettings.GetTrustedOriginHost)])) + && ReservedTenantNames.IsReserved(tenant.Name, deploymentHosts)) throw new ResourceValidationException((nameof(TenantDto.Name), [Localizer[nameof(AppStrings.ReservedTenantName), tenant.Name!]])); // Remote validation example: Any errors thrown here will be displayed in the client's edit form component. if ((entry.State is EntityState.Added || entry.Property(t => t.Name).IsModified) && await DbContext.Tenants.AnyAsync(t => t.Id != tenant.Id && t.Name == tenant.Name, cancellationToken)) throw new ResourceValidationException((nameof(TenantDto.Name), [Localizer[nameof(AppStrings.DuplicateTenantName), tenant.Name!]])); if (tenant.Domain is not null && (entry.State is EntityState.Added || entry.Property(t => t.Domain).IsModified) - && ReservedTenantNames.IsReservedDomain(tenant.Domain, [Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host, .. serverSharedSettings.TrustedOrigins.Select(ServerSharedSettings.GetTrustedOriginHost)])) + && ReservedTenantNames.IsReservedDomain(tenant.Domain, deploymentHosts)) throw new ResourceValidationException((nameof(TenantDto.Domain), [Localizer[nameof(AppStrings.ReservedTenantDomain), tenant.Domain]]));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cs` around lines 209 - 222, In the containing method, extract the repeated deployment-host array into a local variable before the reserved-name and reserved-domain checks, then pass that variable to both ReservedTenantNames.IsReserved and ReservedTenantNames.IsReservedDomain. Preserve the existing host ordering and TrustedOrigins projection.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/IdentityController.ExternalSignIn.cs`:
- Line 93: Update ExternalSignInCallback so provider-asserted email or phone
identifiers are only used for FindUser(...) matching and SetEmailConfirmedAsync
confirmation after validating the provider’s verified-assurance claim (for
example, email_verified == true). If verification is absent or false, do not
link or confirm the identifier; preserve the existing explicit account-link flow
if available.
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/GoogleRecaptchaService.cs`:
- Around line 21-24: Update the response handling in GoogleRecaptchaService
around the PostAsync call to dispose the HttpResponseMessage on every path,
including when IsSuccessStatusCode is false. Use scoped disposal for response so
its resources are released before returning or processing the successful result.
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cs`:
- Around line 218-222: Update TenantManagementController.Validate to add a
ReservedTenantNames.IsReservedDomain check alongside its duplicate-domain
validation, using Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host, and
trusted-origin hosts from serverSharedSettings.TrustedOrigins. Apply the same
reserved-domain validation error as TenantController for added or modified
non-null tenant domains.
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/ResponseCacheService.cs`:
- Around line 81-85: Update the purge request flow in ResponseCacheService to
deserialize the Cloudflare response envelope after SendAsync and validate its
success property, rather than relying only on EnsureSuccessStatusCode(). When
success is false, throw an exception that includes all returned error codes and
messages; preserve HTTP status validation and the existing successful-response
behavior.
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs`:
- Around line 190-198: Update ServerApiSettings and its configuration binding to
preserve the previous Cloudflare:ZoneId key: map it into ZoneIds as a temporary
compatibility alias, or explicitly fail startup with a migration error when it
is supplied. Ensure existing ApiToken plus ZoneId configurations do not silently
make Configured false, and keep ResponseCacheService purge behavior intact.
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Infrastructure/Services/AppResponseCachePolicy.cs`:
- Around line 33-36: Update AppResponseCachePolicy.CreateCacheTag to URI-encode
the relative path, including non-ASCII UTF-8 characters, before applying the
existing cache-tag normalization. Ensure the same encoded representation is used
by both Cache-Tag response headers and ResponseCacheService.PurgeCache so
generated tags remain matchable.
---
Nitpick comments:
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cs`:
- Around line 209-222: In the containing method, extract the repeated
deployment-host array into a local variable before the reserved-name and
reserved-domain checks, then pass that variable to both
ReservedTenantNames.IsReserved and ReservedTenantNames.IsReservedDomain.
Preserve the existing host ordering and TrustedOrigins projection.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 8eb6fb1c-44c7-4976-b81b-300b7b172b6b
📒 Files selected for processing (31)
src/Templates/Boilerplate/Bit.Boilerplate/.docs/07- ASP.NET Core Identity - Authentication & Authorization.mdsrc/Templates/Boilerplate/Bit.Boilerplate/.docs/14- Response Caching System.mdsrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/IdentityController.ExternalSignIn.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/IdentityController.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/RoleManagementController.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/AppIdentityErrorDescriber.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/AppJwtSecureDataFormat.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/AppUserClaimsPrincipalFactory.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/GoogleRecaptchaService.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/ReservedTenantNames.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/ResponseCacheService.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.jsonsrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/Infrastructure/Realms/dev-realm.jsonsrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Infrastructure/Services/AppResponseCachePolicy.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.jsonsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.ar.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.de.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.es.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fr.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.hi.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.sv.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.zh.resxsrc/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Features/Caching/ProductResponseCacheTests.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Features/Identity/TwoFactorAuthTests.cssrc/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Features/Mcp/GetCurrentDateTimeMcpIntegrationTests.cs
closes #12788
Summary by CodeRabbit
New Features
Bug Fixes
Documentation