73 lines
2.5 KiB
C#
73 lines
2.5 KiB
C#
using Elternbeirat.Contracts;
|
|
using Elternbeirat.PocketBase;
|
|
using Elternbeirat.Web.Components;
|
|
using Elternbeirat.Web.Features.Events;
|
|
using Elternbeirat.Web.Services;
|
|
using Microsoft.AspNetCore.HttpOverrides;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Add services to the container.
|
|
builder.Services.AddRazorComponents();
|
|
|
|
// The typed client reads content from PocketBase over its REST API. The base URL
|
|
// 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.");
|
|
builder.Services.AddHttpClient<PocketBaseClient>(client =>
|
|
client.BaseAddress = new Uri(pocketBaseUrl));
|
|
|
|
// Content is read once at startup and cached -> singletons.
|
|
builder.Services.AddSingleton<PageService>();
|
|
builder.Services.AddSingleton<PostService>();
|
|
|
|
var app = builder.Build();
|
|
|
|
// NPM terminates TLS and is the only way to reach the container (no port
|
|
// mapping in production). Without UseForwardedHeaders the app sees every request
|
|
// 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 = { }
|
|
});
|
|
|
|
// Configure the HTTP request pipeline.
|
|
if (!app.Environment.IsDevelopment())
|
|
{
|
|
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);
|
|
|
|
app.UseAntiforgery();
|
|
|
|
app.MapStaticAssets();
|
|
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) =>
|
|
{
|
|
IReadOnlyList<Event> events;
|
|
try
|
|
{
|
|
events = await pocketBase.GetEventsAsync();
|
|
}
|
|
catch (HttpRequestException)
|
|
{
|
|
events = [];
|
|
}
|
|
|
|
return Results.Text(IcsCalendar.Build(events), "text/calendar; charset=utf-8");
|
|
});
|
|
|
|
app.Run();
|