Serve 503 when PocketBase is unavailable across all content pages

This commit is contained in:
tleininger committed 2026-09-24 13:00:14 +02:00
1 parent 0f7e968c16
commit e242eaf79a
25 files changed
+492 -253

No files matched your search

+16 -13
View File
@@ -13,7 +13,7 @@ builder.Services.AddRazorComponents();
// comes from configuration: appsettings for local dev, the PocketBase__BaseUrl
// env var on the server (see compose.yaml).
var pocketBaseUrl = builder.Configuration["PocketBase:BaseUrl"]
?? throw new InvalidOperationException("PocketBase:BaseUrl is not configured.");
?? throw new InvalidOperationException("PocketBase:BaseUrl is not configured.");
builder.Services.AddHttpClient<PocketBaseClient>(client =>
client.BaseAddress = new Uri(pocketBaseUrl));
@@ -24,23 +24,26 @@ var app = builder.Build();
// as HTTP and with the proxy IP instead of the client IP.
// KnownNetworks/KnownProxies are deliberately empty because only NPM reaches
// the container.
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
KnownIPNetworks = { },
KnownProxies = { }
});
app.UseForwardedHeaders(
new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
if (app.Environment.IsDevelopment() is false)
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// No UseHsts() and no UseHttpsRedirection(): NPM sets HSTS and terminates
// TLS. Both here would create a redirect loop behind the proxy.
}
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
// No UseStatusCodePagesWithReExecute: the Router renders the NotFoundPage
// (Pages.NotFound) in process for both an unmatched route and an explicit
// NavigationManager.NotFound() from a component (see Routes.razor). Re-executing
// every error code to /not-found would also turn a 503 (content source down) into
// a "not found" page; leaving it out lets those components keep their own
// "temporarily unavailable" markup and 503 status.
app.UseAntiforgery();
app.MapStaticAssets();
@@ -49,14 +52,14 @@ app.MapRazorComponents<App>();
// Subscribable calendar feed of all events. A minimal API endpoint rather than
// a Razor page because it returns text/calendar, not HTML. Reads from PocketBase
// per request; an unreachable source yields an empty calendar, not a 500.
app.MapGet("/events.ics", async (PocketBaseClient pocketBase) =>
app.MapGet("/events.ics", async (PocketBaseClient pocketBase, CancellationToken token) =>
{
IReadOnlyList<Event> events;
try
{
events = await pocketBase.GetEventsAsync();
events = await pocketBase.GetEventsAsync(token);
}
catch (HttpRequestException)
catch (PocketBaseUnavailableException)
{
events = [];
}