diff --git a/CLAUDE.md b/CLAUDE.md index 62a55b4..c6c3843 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ DeepDrftHome is a **net10.0** solution consisting of ten projects implementing a ### Core Projects -- **DeepDrftPublic**: ASP.NET Core host. Blazor Web App with Server + WASM render modes. Pure Blazor host with no data layer; fetches track metadata from DeepDrftAPI via HTTP. Owns MudBlazor theme prerender and TypeScript→JS audio interop. Public-facing site for listeners. +- **DeepDrftPublic**: ASP.NET Core host. Blazor Web App with Server + WASM render modes. Owns browser-facing proxy controller for `api/track/*` (metadata listing and audio streaming), MudBlazor theme prerender, and TypeScript→JS audio interop. Public-facing site for listeners. - **DeepDrftPublic.Client**: Blazor WebAssembly assembly. All interactive UI (pages, player stack, dark-mode plumbing, HTTP clients for both backends). Consumed by the public site. - **DeepDrftManager**: ASP.NET Core host. Blazor Web App with server-rendered `InteractiveServer` render mode. Hosts all CMS Razor components and pages under `Components/Pages/Cms/`, `Components/Pages/Tracks/`, `Components/Layout/CmsLayout.razor`, and `Components/Shared/` (all inlined from the former `DeepDrftCms` RCL). Gated by AuthBlocks login and hierarchical `Admin` role authorization. All track operations (upload, metadata read/write, delete) are HTTP proxies via `ICmsTrackService` / `CmsTrackService` injected directly into Blazor components; no in-process data layer. - **DeepDrftShared.Client**: Razor Class Library. Shared Blazor components consumed by both `DeepDrftPublic` and `DeepDrftManager` for consistency across public and admin surfaces. @@ -26,8 +26,10 @@ External: **NetBlocks** (absolute path `C:\lib\NetBlocks\`). Provides `Result`, ``` DeepDrftPublic.Client (WASM) - ├── HttpClient "DeepDrft.API" ──► DeepDrftAPI ──► EF Core / SQLite (metadata) - └── HttpClient "DeepDrft.Content" ──► DeepDrftAPI ──► FileDatabase / disk (binary) + ├── HttpClient "DeepDrft.API" ──► DeepDrftPublic proxy ──► DeepDrftAPI ──► EF Core / SQLite (metadata) + └── HttpClient "DeepDrft.Content" ──► DeepDrftPublic proxy ──► DeepDrftAPI ──► FileDatabase / disk (binary) + +Server-side (SSR): Both clients point directly at DeepDrftAPI (server-to-server, no proxy hop). ``` 1. **SQL Database (SQLite)**: Metadata and track info via Entity Framework @@ -120,7 +122,7 @@ dotnet ef database update --project DeepDrftData --startup-project DeepDrftPubli All projects load secrets via `CredentialTools.ResolvePathOrThrow()` from gitignored `environment/` files: -- `DeepDrftPublic/appsettings.json`: Logging and URL config (DeepDrftAPI base URL). No secrets file dependency. +- `DeepDrftPublic/appsettings.json`: Logging and URL config. Secrets loaded from `environment/api.json` (DeepDrftAPI base URLs for content and SQL metadata). - `DeepDrftManager/appsettings.json`: Logging and URL config. Secrets loaded from `environment/api.json` (DeepDrftAPI base URL and API key). - `DeepDrftAPI/appsettings.json`: Logging and hosting config. Secrets loaded from `environment/filedatabase.json` (FileDatabase vault path), `environment/apikey.json` (API key), `environment/connections.json` (SQL and Auth connection strings), `environment/authblocks.json` (AuthBlocks JWT/email/admin creds). diff --git a/DeepDrftPublic/CLAUDE.md b/DeepDrftPublic/CLAUDE.md index d65a431..37b7dbb 100644 --- a/DeepDrftPublic/CLAUDE.md +++ b/DeepDrftPublic/CLAUDE.md @@ -6,11 +6,12 @@ See the root `CLAUDE.md` for full architecture overview. This file covers what i ## One-line purpose -The Blazor Web App host. Pure HTTP render surface (render-mode wiring, no controllers), MudBlazor theme prerender, TypeScript→JS audio interop. Fetches track metadata from DeepDrftAPI via HTTP. +The Blazor Web App host. Owns a browser-facing proxy controller for `api/track/*` (metadata and audio streaming), MudBlazor theme prerender, and TypeScript→JS audio interop. ## What lives here now (only) - `Program.cs`, `Startup.cs`: HTTP host config, DI wiring, port binding. +- `Controllers/TrackProxyController.cs`: Thin proxy controller at `[Route("api/track")]`. Two actions: `GET api/track/page` (proxies paged track metadata) and `GET api/track/{trackId}` (proxies audio streaming without buffering, forwards `offset` query param for seek-beyond-buffer). Uses `RegisterForDispose` for clean connection cleanup. - `Services/DarkModeService.cs`: Server-side dark-mode prerender (reads `darkMode` cookie, seeds `DarkModeSettings.IsDarkMode` via `IHttpContextAccessor`, carries to WASM via `PersistentComponentState`). - `Components/App.razor`: Root component with `@rendermode="InteractiveAuto"`. Calls `DarkModeService.InitializeAsync()` in `OnInitialized`. - `Components/Pages/Error.razor`: Error fallback. @@ -19,12 +20,10 @@ The Blazor Web App host. Pure HTTP render surface (render-mode wiring, no contro ## What does NOT live here anymore -- `TrackController` — deleted; track metadata now comes from DeepDrftAPI via HTTP. - `TrackDirectDataService` — deleted; no in-process data adapter. - `DeepDrftContext`, `TrackRepository`, `TrackService`, `Configurations/`, `Migrations/` — all in `DeepDrftData` (consumed only by DeepDrftAPI). - Any FileDatabase code — that lives in `DeepDrftContent`. - EF Core registration, SQL connection string — DeepDrftPublic has no data layer. -- `environment/connections.json` dependency — removed. ## Blazor Web App render modes @@ -56,15 +55,16 @@ Blazor calls TypeScript via `AudioInteropService.ts` (a JS interop wrapper in `D ## HTTP client wiring -Configured in `DeepDrftPublic.Client.Startup`: +Dual-mode wiring in `DeepDrftPublic.Client.Startup`: - Named clients `"DeepDrft.API"` (SQL metadata) and `"DeepDrft.Content"` (binary audio). -- Both clients point to DeepDrftAPI. Base addresses passed in from `appsettings.json` (`ApiUrls:ContentApi`, `ApiUrls:SqlApi`). -- `Startup.ConfigureApiHttpClient` and `Startup.ConfigureContentServices` are static methods called from **both** the server `Program.cs` and the WASM `Program.cs` so prerender and runtime see the same DI. +- **Server-side** (SSR prerender): Both clients point directly at DeepDrftAPI via base addresses from `appsettings.json` (`ApiUrls:ContentApi`, `ApiUrls:SqlApi`). These are loaded from `environment/api.json` (via `CredentialTools.ResolvePathOrThrow`) in `Program.cs`. +- **WASM runtime**: Both clients point to `HostEnvironment.BaseAddress` (the Blazor host), so browser requests proxy through `TrackProxyController` to DeepDrftAPI. +- `Startup.ConfigureApiHttpClient` and `Startup.ConfigureContentServices` are static methods called from both the server `Program.cs` and the WASM `Program.cs`. At runtime, WASM overrides the base address to `HostEnvironment.BaseAddress`. Server-side `Program.cs` adds: - MudBlazor (`AddMudServices`) -- Controllers +- Controllers (`AddControllers()` and `MapControllers()`) - Render-mode components - SignalR tuning (if needed) - Forwarded headers @@ -74,9 +74,15 @@ Server-side `Program.cs` adds: `UseForwardedHeaders()` runs first in the pipeline. HTTPS redirect is conditionally disabled via `ForwardedHeaders:DisableHttpsRedirection` so the app can sit behind nginx without forcing HTTPS at the host level. -## No controllers +## The proxy controller -DeepDrftPublic has no HTTP controllers. It is a pure Blazor render host. Track metadata endpoints are served by DeepDrftAPI (see `DeepDrftAPI/CLAUDE.md` for endpoint details). +`TrackProxyController` in `Controllers/` is the only HTTP controller. It is a thin proxy only — no domain logic, no data layer. The WASM client points both named HttpClients (`"DeepDrft.API"` and `"DeepDrft.Content"`) at the Blazor host's base address, so all browser requests route through this controller to DeepDrftAPI. Server-side SSR calls DeepDrftAPI directly (server-to-server) via the same named clients — no proxy hop on the server side. + +The proxy forwards two public, unauthenticated routes: +- `GET api/track/page` — paged metadata listing +- `GET api/track/{trackId}` — WAV audio streaming (handles `offset` param for seek-beyond-buffer) + +Both actions use `HttpCompletionOption.ResponseHeadersRead` for streaming efficiency. Audio streaming registers the upstream response with `HttpContext.Response.RegisterForDispose()` so the stream is properly cleaned up after the response body is sent. ## Development commands @@ -93,11 +99,11 @@ dotnet build DeepDrftPublic ## Configuration -- `appsettings.json`: `ApiUrls:*` (DeepDrftAPI base addresses), `Logging:*`, `AllowedHosts`, `ForwardedHeaders`. Port binding via `Kestrel:Endpoints` or `ASPNETCORE_URLS`. -- No secrets files — DeepDrftPublic has no data layer or API credentials. +- `appsettings.json`: Dev defaults for `ApiUrls:*` (DeepDrftAPI base addresses), `Logging:*`, `AllowedHosts`, `ForwardedHeaders`. Port binding via `Kestrel:Endpoints` or `ASPNETCORE_URLS`. +- `environment/api.json`: Secrets file (via `CredentialTools.ResolvePathOrThrow`) with production DeepDrftAPI URLs (`ApiUrls:ContentApi`, `ApiUrls:SqlApi`). - MudBlazor theme (`MainLayout.razor` in client): bespoke light ("Charleston in the Day") and dark ("Lowcountry Summer Nights") palettes. - No `wwwroot/` changes during normal development — TS → JS compilation is automatic. ## Important patterns -This project is a render host only. All data operations go through HTTP to DeepDrftAPI. When working with this project, focus on the render surface (components, middleware, config) and prerender coordination. New domain logic belongs in `DeepDrftData` / `DeepDrftAPI`. +This project is a Blazor host with a proxy layer. The proxy controller is a thin HTTP boundary — no domain logic, no data layer. All domain operations happen in DeepDrftAPI; this controller only forwards public, unauthenticated track routes. When working here, focus on the render surface (components, middleware, config), prerender coordination, and keeping the proxy transparent. New domain logic belongs in `DeepDrftData` / `DeepDrftAPI`. diff --git a/DeepDrftPublic/connections.example.json b/DeepDrftPublic/connections.example.json deleted file mode 100644 index 058cdc2..0000000 --- a/DeepDrftPublic/connections.example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "DefaultConnection": "Host=localhost;Port=5433;Database=postgres;Username=postgres;Password=your-password-here" - } -}