docs: update CLAUDE.md files for AuthBlocks split, DTO layer boundary, and CLI removal
This commit is contained in:
@@ -12,11 +12,10 @@ DeepDrftHome is a **net10.0** solution consisting of ten projects implementing a
|
|||||||
- **DeepDrftPublic.Client**: Blazor WebAssembly assembly. All interactive UI (pages, player stack, dark-mode plumbing, HTTP clients for both backends). Consumed by the public site.
|
- **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.
|
- **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.
|
- **DeepDrftShared.Client**: Razor Class Library. Shared Blazor components consumed by both `DeepDrftPublic` and `DeepDrftManager` for consistency across public and admin surfaces.
|
||||||
- **DeepDrftData**: Class library. EF Core domain logic: `DeepDrftContext`, `TrackConfiguration`, `Migrations`, `TrackRepository`, `TrackService`, `TrackManager`. Consumed by `DeepDrftContent`, `DeepDrftPublic`, and `DeepDrftCli`.
|
- **DeepDrftData**: Class library. EF Core domain logic: `DeepDrftContext`, `TrackConfiguration`, `Migrations`, `TrackRepository`, `TrackService`, `TrackManager`. Consumed by `DeepDrftAPI`, `DeepDrftPublic`, and tests.
|
||||||
- **DeepDrftContent**: ASP.NET Core host. Dual-database authority (SQL metadata + FileDatabase binary). Seven endpoints: `GET api/track/{id}` unauthenticated streaming; `PUT api/track/{id}` vault write (ApiKey); `POST api/track/upload` upload + SQL persist (ApiKey); `DELETE api/track/{id:long}` SQL delete + vault remove (ApiKey); `GET api/track/page` paged metadata list (ApiKey); `GET api/track/meta/{id:long}` single metadata (ApiKey); `PUT api/track/meta/{id:long}` metadata update (ApiKey).
|
- **DeepDrftAPI**: ASP.NET Core host. Dual-database authority (SQL metadata + FileDatabase binary). AuthBlocks API host (owns registration, migration/seed, JWT endpoints). Seven track endpoints: `GET api/track/{id}` unauthenticated streaming; `PUT api/track/{id}` vault write (ApiKey); `POST api/track/upload` upload + SQL persist (ApiKey); `DELETE api/track/{id:long}` SQL delete + vault remove (ApiKey); `GET api/track/page` paged metadata list (ApiKey); `GET api/track/meta/{id:long}` single metadata (ApiKey); `PUT api/track/meta/{id:long}` metadata update (ApiKey).
|
||||||
- **DeepDrftContent.Services**: Class library. The FileDatabase implementation in full (Models, Services, Utils, Abstractions, Constants), `WavOffsetService`, `AudioProcessor`, content-side `TrackService`. Consumed by hosts and tests.
|
- **DeepDrftContent**: Class library. The FileDatabase implementation in full (Models, Services, Utils, Abstractions, Constants), `WavOffsetService`, `AudioProcessor`, content-side `TrackService`. Consumed by hosts and tests.
|
||||||
- **DeepDrftModels**: Shared contracts. `TrackEntity`, `TrackDto`, `PagingParameters<T>`, `PagedResult<T>`. Every project references this.
|
- **DeepDrftModels**: Shared contracts. `TrackEntity`, `TrackDto`, `PagingParameters<T>`, `PagedResult<T>`. Every project references this.
|
||||||
- **DeepDrftCli**: Console app. Two modes: classic CLI (`add` / `list` / `help`) and Terminal.Gui (`gui`). Direct access to both databases (local admin tool, not a network client).
|
|
||||||
- **DeepDrftTests**: NUnit test suite. Comprehensive FileDatabase tests (vault creation, media storage, indexing, factory patterns, utilities). Integration-focused with temp-directory test isolation.
|
- **DeepDrftTests**: NUnit test suite. Comprehensive FileDatabase tests (vault creation, media storage, indexing, factory patterns, utilities). Integration-focused with temp-directory test isolation.
|
||||||
|
|
||||||
External: **NetBlocks** (absolute path `C:\lib\NetBlocks\`). Provides `Result`, `ResultContainer<T>`, `ApiResult<T>`, `ApiResultDto<T>`.
|
External: **NetBlocks** (absolute path `C:\lib\NetBlocks\`). Provides `Result`, `ResultContainer<T>`, `ApiResult<T>`, `ApiResultDto<T>`.
|
||||||
@@ -56,8 +55,8 @@ The split between host projects (`DeepDrftPublic`, `DeepDrftManager`, `DeepDrftC
|
|||||||
|
|
||||||
`TrackEntity` holds *only* metadata. The link to binary content is `EntryKey` (string) — the entry id inside the `tracks` vault in FileDatabase. Dual-database add flow:
|
`TrackEntity` holds *only* metadata. The link to binary content is `EntryKey` (string) — the entry id inside the `tracks` vault in FileDatabase. Dual-database add flow:
|
||||||
|
|
||||||
1. `DeepDrftContent.Services.TrackService.AddTrackFromWavAsync` processes WAV, generates entry GUID, stores audio in vault, returns unpersisted `TrackEntity`.
|
1. `DeepDrftContent.TrackService.AddTrackFromWavAsync` processes WAV, generates entry GUID, stores audio in vault, returns unpersisted `TrackEntity`.
|
||||||
2. `DeepDrftContent.Services.UnifiedTrackService.UploadAsync` persists the entity to SQL via `DeepDrftData.TrackManager` and returns the persisted entity with `Id`.
|
2. `DeepDrftAPI.Services.UnifiedTrackService.UploadAsync` persists the entity to SQL via `DeepDrftData.TrackManager` and returns the persisted entity with `Id`.
|
||||||
|
|
||||||
If step 1 succeeds and step 2 fails, audio is orphaned in the vault (no rollback today).
|
If step 1 succeeds and step 2 fails, audio is orphaned in the vault (no rollback today).
|
||||||
|
|
||||||
@@ -104,14 +103,8 @@ dotnet test DeepDrftTests/ --filter "ClassName=FileDatabaseTests"
|
|||||||
# Run main web application
|
# Run main web application
|
||||||
dotnet run --project DeepDrftPublic
|
dotnet run --project DeepDrftPublic
|
||||||
|
|
||||||
# Run content API
|
# Run API (dual-database authority and AuthBlocks host)
|
||||||
dotnet run --project DeepDrftContent
|
dotnet run --project DeepDrftAPI
|
||||||
|
|
||||||
# Run CLI (classic mode)
|
|
||||||
dotnet run --project DeepDrftCli -- list
|
|
||||||
|
|
||||||
# Run CLI (GUI mode)
|
|
||||||
dotnet run --project DeepDrftCli -- gui
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Entity Framework (SQL Database)
|
### Entity Framework (SQL Database)
|
||||||
@@ -128,9 +121,8 @@ dotnet ef database update --project DeepDrftData --startup-project DeepDrftPubli
|
|||||||
All projects load secrets via `CredentialTools.ResolvePathOrThrow()` from gitignored `environment/` files:
|
All projects load secrets via `CredentialTools.ResolvePathOrThrow()` from gitignored `environment/` files:
|
||||||
|
|
||||||
- `DeepDrftPublic/appsettings.json`: Logging and URL config. Secrets loaded from `environment/connections.json` (SQL connection string).
|
- `DeepDrftPublic/appsettings.json`: Logging and URL config. Secrets loaded from `environment/connections.json` (SQL connection string).
|
||||||
- `DeepDrftManager/appsettings.json`: Logging and URL config. Secrets loaded from `environment/apikey.json` (DeepDrftContent API key), `environment/connections.json` (SQL and Auth connection strings), `environment/authblocks.json` (AuthBlocks settings).
|
- `DeepDrftManager/appsettings.json`: Logging and URL config. Secrets loaded from `environment/api.json` (DeepDrftAPI base URL and API key).
|
||||||
- `DeepDrftContent/appsettings.json`: Logging and hosting config. Secrets loaded from `environment/filedatabase.json` (FileDatabase vault path), `environment/apikey.json` (API key), `environment/connections.json` (SQL connection string).
|
- `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).
|
||||||
- `DeepDrftCli/environment/connections.json`: CLI config (`ConnectionString`, `VaultPath`). Loaded via CredentialTools.
|
|
||||||
|
|
||||||
## Folder-Level Guidance
|
## Folder-Level Guidance
|
||||||
|
|
||||||
|
|||||||
+70
-37
@@ -1,27 +1,28 @@
|
|||||||
# CLAUDE.md - DeepDrftContent
|
# CLAUDE.md - DeepDrftAPI
|
||||||
|
|
||||||
Guidance for working in the DeepDrftContent project (the binary content API host).
|
Guidance for working in the DeepDrftAPI project (the dual-database authority and AuthBlocks API host).
|
||||||
|
|
||||||
See the root `CLAUDE.md` for full architecture overview. This file covers what is specific to this project.
|
See the root `CLAUDE.md` for full architecture overview. This file covers what is specific to this project.
|
||||||
|
|
||||||
## One-line purpose
|
## One-line purpose
|
||||||
|
|
||||||
The dual-database authority for tracks: SQL metadata and FileDatabase binary. Seven endpoints expose track CRUD with upload+persist, delete+cleanup, paged listing, and metadata operations. ApiKey middleware, CORS, forwarded headers. **FileDatabase implementation lives in `DeepDrftContent.Services`; SQL services in `DeepDrftData`.**
|
Dual-database authority for tracks (SQL metadata + FileDatabase binary), and AuthBlocks API host (JWT auth, role/admin seed). Seven track endpoints expose CRUD with upload+persist, delete+cleanup, paged listing, and metadata operations. ApiKey middleware for track endpoints, JWT + AuthBlocks endpoints for auth. CORS, forwarded headers. **FileDatabase implementation lives in `DeepDrftContent`; SQL services in `DeepDrftData`.**
|
||||||
|
|
||||||
## What lives here now (only)
|
## What lives here now (only)
|
||||||
|
|
||||||
- `Program.cs`, `Startup.cs`: HTTP host config, DI wiring, middleware setup, port binding.
|
- `Program.cs`, `Startup.cs`: HTTP host config, DI wiring, middleware setup, port binding. AuthBlocks startup: `AddAuthBlocks`, `UseAuthBlocksStartupAsync`, `MapAuthBlocks`, authentication/authorization middleware.
|
||||||
- `Services/UnifiedTrackService.cs`: Host-internal orchestrator. Coordinates vault write + SQL persist for upload (`UploadAsync`), and SQL delete + vault remove for delete (`DeleteAsync`).
|
- `Services/UnifiedTrackService.cs`: Host-internal orchestrator. Coordinates vault write + SQL persist for upload (`UploadAsync`), and SQL delete + vault remove for delete (`DeleteAsync`).
|
||||||
- `Controllers/TrackController.cs`: Seven endpoints (see below).
|
- `Controllers/TrackController.cs`: Seven track endpoints (see below).
|
||||||
- `Middleware/ApiKeyAuthenticationMiddleware.cs`, `Middleware/ApiKeyAuthorizeAttribute.cs`: ApiKey validation logic.
|
- `Middleware/ApiKeyAuthenticationMiddleware.cs`, `Middleware/ApiKeyAuthorizeAttribute.cs`: ApiKey validation logic (for track endpoints only).
|
||||||
- `Models/`: Settings POCOs only (`ApiKeySettings`, `CorsSettings`, `FileDatabaseSettings`). No domain code.
|
- `Models/`: Settings POCOs only (`ApiKeySettings`, `CorsSettings`, `FileDatabaseSettings`). No domain code.
|
||||||
- `environment/filedatabase.json`: FileDatabase vault path config (loaded via CredentialTools, not in repo).
|
- `environment/filedatabase.json`: FileDatabase vault path config (loaded via CredentialTools, not in repo).
|
||||||
- `environment/apikey.json`: API key (loaded via CredentialTools, not in repo, must be created locally or at deployment).
|
- `environment/apikey.json`: API key for track endpoints (loaded via CredentialTools, not in repo, must be created locally or at deployment).
|
||||||
- `environment/connections.json`: SQL connection string (loaded via CredentialTools, not in repo, format: `{ "ConnectionStrings": { "DefaultConnection": "..." } }`).
|
- `environment/connections.json`: SQL and Auth connection strings (loaded via CredentialTools, not in repo, format: `{ "ConnectionStrings": { "DefaultConnection": "...", "Auth": "..." } }`).
|
||||||
|
- `environment/authblocks.json`: AuthBlocks configuration (JWT secret, email sender creds, admin seed creds) (loaded via CredentialTools, not in repo).
|
||||||
|
|
||||||
## What does NOT live here anymore
|
## What does NOT live here anymore
|
||||||
|
|
||||||
- `FileDatabase/`, `Processors/`, media models (`AudioBinary`, `ImageBinary`, etc.), `WavOffsetService` — all in `DeepDrftContent.Services`.
|
- `FileDatabase/`, `Processors/`, media models (`AudioBinary`, `ImageBinary`, etc.), `WavOffsetService` — all in `DeepDrftContent` (class library).
|
||||||
- EF Core context and repository — in `DeepDrftData`.
|
- EF Core context and repository — in `DeepDrftData`.
|
||||||
- **Hosts only own HTTP surface and wiring.** New domain code goes in `*.Services` (shared libraries) or host-internal `Services/` folders (e.g., `UnifiedTrackService` here for dual-database orchestration).
|
- **Hosts only own HTTP surface and wiring.** New domain code goes in `*.Services` (shared libraries) or host-internal `Services/` folders (e.g., `UnifiedTrackService` here for dual-database orchestration).
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ Returns the WAV bytes from the `tracks` vault with optional offset support.
|
|||||||
|
|
||||||
### POST api/track/upload ([ApiKeyAuthorize])
|
### POST api/track/upload ([ApiKeyAuthorize])
|
||||||
|
|
||||||
**Authenticated endpoint.** Accepts a raw WAV upload + metadata as `multipart/form-data`, processes the WAV, stores it in the vault, and persists metadata to SQL. Returns the fully persisted `TrackEntity` with `Id` populated.
|
**Authenticated endpoint.** Accepts a raw WAV upload + metadata as `multipart/form-data`, processes the WAV, stores it in the vault, and persists metadata to SQL. Returns the fully persisted `TrackDto` with `Id` populated.
|
||||||
|
|
||||||
- **Header `ApiKey`**: required. Validated by `ApiKeyAuthenticationMiddleware`.
|
- **Header `ApiKey`**: required. Validated by `ApiKeyAuthenticationMiddleware`.
|
||||||
- **Form fields**:
|
- **Form fields**:
|
||||||
@@ -64,7 +65,7 @@ Returns the WAV bytes from the `tracks` vault with optional offset support.
|
|||||||
- The upload stream is copied to a `.wav`-suffixed temp file under `Path.GetTempPath()` (the audio processor requires that extension and reads from disk). The temp file is always deleted in a `finally` block — success or failure.
|
- The upload stream is copied to a `.wav`-suffixed temp file under `Path.GetTempPath()` (the audio processor requires that extension and reads from disk). The temp file is always deleted in a `finally` block — success or failure.
|
||||||
- `[RequestSizeLimit(1 GB)]` + `[RequestFormLimits(MultipartBodyLengthLimit = 1 GB)]` lift the per-request ceiling above the framework default (~28 MB) so production-sized WAVs are accepted. The body is streamed to the temp file, not buffered in memory.
|
- `[RequestSizeLimit(1 GB)]` + `[RequestFormLimits(MultipartBodyLengthLimit = 1 GB)]` lift the per-request ceiling above the framework default (~28 MB) so production-sized WAVs are accepted. The body is streamed to the temp file, not buffered in memory.
|
||||||
- Calls `UnifiedTrackService.UploadAsync`, which orchestrates: `TrackService.AddTrackFromWavAsync` (vault write) → `TrackManager` (SQL persist with `createdByUserId`).
|
- Calls `UnifiedTrackService.UploadAsync`, which orchestrates: `TrackService.AddTrackFromWavAsync` (vault write) → `TrackManager` (SQL persist with `createdByUserId`).
|
||||||
- Returns 200 with the **persisted** `TrackEntity` JSON (Id populated) on success. Returns 400 for missing/invalid form fields. Returns 500 if processing fails.
|
- Returns 200 with the **persisted** `TrackDto` JSON (Id populated) on success. Returns 400 for missing/invalid form fields. Returns 500 if processing fails.
|
||||||
|
|
||||||
### DELETE api/track/{id:long} ([ApiKeyAuthorize])
|
### DELETE api/track/{id:long} ([ApiKeyAuthorize])
|
||||||
|
|
||||||
@@ -86,7 +87,7 @@ Returns the WAV bytes from the `tracks` vault with optional offset support.
|
|||||||
- `sortColumn` (string, optional): sort field. Supported: `"TrackName"`, `"Artist"`, `"Album"`, `"Genre"`, `"ReleaseDate"`. Defaults to `Id`.
|
- `sortColumn` (string, optional): sort field. Supported: `"TrackName"`, `"Artist"`, `"Album"`, `"Genre"`, `"ReleaseDate"`. Defaults to `Id`.
|
||||||
- `sortDescending` (bool, optional, default false): sort direction.
|
- `sortDescending` (bool, optional, default false): sort direction.
|
||||||
- Calls `ITrackService.GetPaged` (via DI), which is actually `TrackManager` from `DeepDrftData`.
|
- Calls `ITrackService.GetPaged` (via DI), which is actually `TrackManager` from `DeepDrftData`.
|
||||||
- Returns 200 with `PagedResult<TrackEntity>` JSON (`Items`, `TotalCount`, `PageNumber`, `PageSize`). Returns 500 on query error.
|
- Returns 200 with `PagedResult<TrackDto>` JSON (`Items`, `TotalCount`, `PageNumber`, `PageSize`). Returns 500 on query error.
|
||||||
|
|
||||||
### GET api/track/meta/{id:long} ([ApiKeyAuthorize])
|
### GET api/track/meta/{id:long} ([ApiKeyAuthorize])
|
||||||
|
|
||||||
@@ -94,8 +95,8 @@ Returns the WAV bytes from the `tracks` vault with optional offset support.
|
|||||||
|
|
||||||
- **Header `ApiKey`**: required. Validated by `ApiKeyAuthenticationMiddleware`.
|
- **Header `ApiKey`**: required. Validated by `ApiKeyAuthenticationMiddleware`.
|
||||||
- **Route parameter `id`** (long): the SQL track ID.
|
- **Route parameter `id`** (long): the SQL track ID.
|
||||||
- Calls `ITrackService.GetById`, which returns the track or null.
|
- Calls `ITrackService.GetById`, which returns the track as `TrackDto` or null.
|
||||||
- Returns 200 with `TrackEntity` JSON on success. Returns 404 if not found. Returns 500 on query error.
|
- Returns 200 with `TrackDto` JSON on success. Returns 404 if not found. Returns 500 on query error.
|
||||||
|
|
||||||
### PUT api/track/meta/{id:long} ([ApiKeyAuthorize])
|
### PUT api/track/meta/{id:long} ([ApiKeyAuthorize])
|
||||||
|
|
||||||
@@ -104,8 +105,8 @@ Returns the WAV bytes from the `tracks` vault with optional offset support.
|
|||||||
- **Header `ApiKey`**: required. Validated by `ApiKeyAuthenticationMiddleware`.
|
- **Header `ApiKey`**: required. Validated by `ApiKeyAuthenticationMiddleware`.
|
||||||
- **Route parameter `id`** (long): the SQL track ID.
|
- **Route parameter `id`** (long): the SQL track ID.
|
||||||
- **Body**: `UpdateTrackMetadataRequest` with fields: `TrackName`, `Artist`, `Album?`, `Genre?`, `ReleaseDate?`.
|
- **Body**: `UpdateTrackMetadataRequest` with fields: `TrackName`, `Artist`, `Album?`, `Genre?`, `ReleaseDate?`.
|
||||||
- Looks up SQL row by ID, updates the provided fields (nulls in the request clear optional fields), and persists via `ITrackService.Update`.
|
- Looks up SQL row by ID (returns `TrackDto`), updates the provided fields (nulls in the request clear optional fields), and persists the DTO via `ITrackService.Update`.
|
||||||
- Returns 200 on success. Returns 404 if track not found. Returns 500 on update error.
|
- Returns 200 with the updated `TrackDto` on success. Returns 404 if track not found. Returns 500 on update error.
|
||||||
|
|
||||||
## ApiKey middleware behaviour
|
## ApiKey middleware behaviour
|
||||||
|
|
||||||
@@ -140,16 +141,20 @@ Configured in `Startup.ConfigureDomainServices()`, applied to all endpoints via
|
|||||||
2. Await `FileDatabase.FromAsync(VaultPath)` to load or create the database.
|
2. Await `FileDatabase.FromAsync(VaultPath)` to load or create the database.
|
||||||
3. Register `FileDatabase` as singleton.
|
3. Register `FileDatabase` as singleton.
|
||||||
4. Ensure the `tracks` vault exists (type `MediaVaultType.Audio`, created on first boot if missing).
|
4. Ensure the `tracks` vault exists (type `MediaVaultType.Audio`, created on first boot if missing).
|
||||||
5. Register singletons: `WavOffsetService`, `AudioProcessor`, `TrackService` (the `DeepDrftContent.Data` version for vault operations).
|
5. Register singletons: `WavOffsetService`, `AudioProcessor`, `TrackService` (the `DeepDrftContent` version for vault operations).
|
||||||
|
|
||||||
**In `Program.cs`** (SQL + wiring):
|
**In `Program.cs`** (SQL + AuthBlocks + wiring):
|
||||||
|
|
||||||
6. Load `environment/connections.json` via `CredentialTools.ResolvePathOrThrow("connections", ...)`.
|
6. Load `environment/connections.json` via `CredentialTools.ResolvePathOrThrow("connections", ...)` — contains both `DefaultConnection` (SQL metadata) and `Auth` (AuthBlocks Identity database).
|
||||||
7. Register `DbContext<DeepDrftContext>` (scoped) with connection string from config.
|
7. **AuthBlocks startup:** Call `builder.Services.AddAuthBlocks(options => { ... })` with `Auth` connection string and settings from `AuthBlocks:*` config keys. Load `environment/authblocks.json` for JWT secret, email sender creds, and admin seed creds. This registers JWT bearer auth scheme and EF Identity.
|
||||||
8. Register scoped: `TrackRepository`, `TrackManager`, `ITrackService` (factory resolves to `TrackManager`), `UnifiedTrackService`.
|
8. Register `DbContext<DeepDrftContext>` (scoped) with `DefaultConnection` from config.
|
||||||
9. Configure forwarded headers (production-only) for reverse proxy support.
|
9. Register scoped: `TrackRepository`, `TrackManager`, `ITrackService` (factory resolves to `TrackManager`), `UnifiedTrackService`.
|
||||||
10. Load `environment/apikey.json` and register API key middleware.
|
10. **After `app.Build()`:** Call `await app.Services.UseAuthBlocksStartupAsync()` to apply migrations and seed roles + admin user to the Auth database.
|
||||||
11. Configure CORS policy (`ContentApiPolicy`): AllowAnyMethod, AllowAnyHeader, AllowCredentials, specific origins from config.
|
11. Configure forwarded headers (production-only) for reverse proxy support.
|
||||||
|
12. Load `environment/apikey.json` and register API key middleware.
|
||||||
|
13. Configure CORS policy (`ContentApiPolicy`): AllowAnyMethod, AllowAnyHeader, AllowCredentials, specific origins from config (includes DeepDrftManager origin for cross-origin auth calls).
|
||||||
|
14. **Map AuthBlocks endpoints:** Call `app.MapAuthBlocks()` to mount `/api/auth/*` and `/api/users/*` endpoints. Ensure `app.UseAuthentication()` and `app.UseAuthorization()` are in the middleware pipeline (required for JWT bearer auth).
|
||||||
|
15. Verify ApiKey middleware ordering: it must not interfere with JWT middleware. ApiKey gates only `[ApiKeyAuthorize]`-decorated track endpoints; JWT gates AuthBlocks endpoints.
|
||||||
|
|
||||||
The singleton `FileDatabase` is thread-safe for reads. Writes are atomic at the vault level (index updates are serialised). The `IndexWatcher` reloads the vault index if an external process (e.g., CLI) writes to it, so a long-running web host stays consistent. SQL services are scoped (DbContext not thread-safe).
|
The singleton `FileDatabase` is thread-safe for reads. Writes are atomic at the vault level (index updates are serialised). The `IndexWatcher` reloads the vault index if an external process (e.g., CLI) writes to it, so a long-running web host stays consistent. SQL services are scoped (DbContext not thread-safe).
|
||||||
|
|
||||||
@@ -159,9 +164,10 @@ Mapped in `Development` only. Swagger UI at `/swagger` for testing endpoints loc
|
|||||||
|
|
||||||
## Configuration files
|
## Configuration files
|
||||||
|
|
||||||
- `appsettings.json`: Logging, hosting, and CORS config. **Does not contain secrets.**
|
- `appsettings.json`: Logging, hosting, CORS, and AuthBlocks config. **Does not contain secrets.**
|
||||||
- `Logging`: standard ASP.NET structure.
|
- `Logging`: standard ASP.NET structure.
|
||||||
- `CorsSettings.AllowedOrigins`: array of origin URLs allowed to call the API (required; throws on startup if missing).
|
- `CorsSettings.AllowedOrigins`: array of origin URLs allowed to call the API (required; throws on startup if missing).
|
||||||
|
- `AuthBlocks:Jwt:Issuer`, `AuthBlocks:Jwt:Audience`: JWT validation settings (loaded from `environment/authblocks.json`).
|
||||||
- `environment/filedatabase.json` (required, loaded via CredentialTools, not in repo):
|
- `environment/filedatabase.json` (required, loaded via CredentialTools, not in repo):
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -182,7 +188,30 @@ Mapped in `Development` only. Swagger UI at `/swagger` for testing endpoints loc
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "Host=localhost;Database=deepdrft;Username=postgres;Password=..."
|
"DefaultConnection": "Host=localhost;Database=deepdrft;Username=postgres;Password=...",
|
||||||
|
"Auth": "Host=localhost;Database=deepdrft_auth;Username=postgres;Password=..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- `environment/authblocks.json` (required, loaded via CredentialTools, not in repo):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"AuthBlocks": {
|
||||||
|
"Jwt": {
|
||||||
|
"Secret": "your-signing-secret-min-32-chars",
|
||||||
|
"Issuer": "https://deepdrft.api",
|
||||||
|
"Audience": "https://deepdrft.com"
|
||||||
|
},
|
||||||
|
"Email": {
|
||||||
|
"Host": "smtp.provider.com",
|
||||||
|
"Token": "your-smtp-password"
|
||||||
|
},
|
||||||
|
"Admin": {
|
||||||
|
"UserName": "admin",
|
||||||
|
"Email": "admin@deepdrft.com",
|
||||||
|
"Password": "initial-admin-password"
|
||||||
|
},
|
||||||
|
"SupportEmail": "support@deepdrft.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -190,21 +219,25 @@ Mapped in `Development` only. Swagger UI at `/swagger` for testing endpoints loc
|
|||||||
## Development commands
|
## Development commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run the content API (default https://localhost:5002)
|
# Run the API host (default https://localhost:5002)
|
||||||
dotnet run --project DeepDrftContent
|
dotnet run --project DeepDrftAPI
|
||||||
|
|
||||||
# Watch during development
|
# Watch during development
|
||||||
dotnet watch run --project DeepDrftContent
|
dotnet watch run --project DeepDrftAPI
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
dotnet build DeepDrftContent
|
dotnet build DeepDrftAPI
|
||||||
|
|
||||||
# Test endpoints (requires API key in environment/apikey.json)
|
# Test track endpoints (requires API key in environment/apikey.json)
|
||||||
curl -H "ApiKey: your-secret-key" -X PUT https://localhost:5002/api/track/test-id \
|
curl -H "ApiKey: your-secret-key" -X GET https://localhost:5002/api/track/page \
|
||||||
|
-H "Accept: application/json"
|
||||||
|
|
||||||
|
curl https://localhost:5002/api/track/test-entry-key?offset=0
|
||||||
|
|
||||||
|
# Test auth endpoints (AuthBlocks API)
|
||||||
|
curl -X POST https://localhost:5002/api/auth/login \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"buffer":"base64-encoded-audio","size":1000,"mime":"audio/wav"}'
|
-d '{"email":"admin@deepdrft.com","password":"initial-admin-password"}'
|
||||||
|
|
||||||
curl https://localhost:5002/api/track/test-id?offset=0
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Important patterns
|
## Important patterns
|
||||||
@@ -216,6 +249,6 @@ curl https://localhost:5002/api/track/test-id?offset=0
|
|||||||
|
|
||||||
## The FileDatabase import
|
## The FileDatabase import
|
||||||
|
|
||||||
See `DeepDrftContent.Services/CLAUDE.md` for the FileDatabase API and semantics. This host only provides the HTTP surface over it.
|
See `DeepDrftContent/CLAUDE.md` for the FileDatabase API and semantics. This host only provides the HTTP surface over it and the AuthBlocks authority.
|
||||||
|
|
||||||
When working with this project, focus on the HTTP surface (controllers, middleware, CORS, forwarded headers) and the wiring that connects the host to the FileDatabase. New domain logic goes in `DeepDrftContent.Services`.
|
When working with this project, focus on the HTTP surface (controllers, middleware, CORS, forwarded headers, AuthBlocks wiring) and the dual-database orchestration via `UnifiedTrackService`. New domain logic goes in `DeepDrftContent` (FileDatabase) or `DeepDrftData` (SQL). Keep this host focused on HTTP boundaries and wiring.
|
||||||
|
|||||||
+11
-4
@@ -45,7 +45,7 @@ DeepDrftData/
|
|||||||
|
|
||||||
## Service → Repository → DbContext shape
|
## Service → Repository → DbContext shape
|
||||||
|
|
||||||
- **Service** (`TrackManager`, implements `ITrackService`): Public contract. Takes `TrackRepository`, catches exceptions at service boundary, returns `ResultContainer<T>`.
|
- **Service** (`TrackManager`, implements `ITrackService`): Public contract. Takes `TrackRepository`, catches exceptions at service boundary, returns `ResultContainer<T>` with DTO results.
|
||||||
- **Repository** (`TrackRepository`): Internal data access. Queries the DbContext. Throws on error (service catches).
|
- **Repository** (`TrackRepository`): Internal data access. Queries the DbContext. Throws on error (service catches).
|
||||||
- **DbContext** (`DeepDrftContext`): EF Core. Directly accessed by repository, never by service (pattern isolation).
|
- **DbContext** (`DeepDrftContext`): EF Core. Directly accessed by repository, never by service (pattern isolation).
|
||||||
|
|
||||||
@@ -53,7 +53,8 @@ Example:
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// TrackManager.GetPaged (public via ITrackService)
|
// TrackManager.GetPaged (public via ITrackService)
|
||||||
public async Task<ResultContainer<PagedResult<TrackEntity>>> GetPaged(
|
// Returns PagedResult<TrackDto> — the repository outputs entities, the service outputs DTOs
|
||||||
|
public async Task<ResultContainer<PagedResult<TrackDto>>> GetPaged(
|
||||||
int pageNumber = 1,
|
int pageNumber = 1,
|
||||||
int pageSize = 20,
|
int pageSize = 20,
|
||||||
string? sortColumn = null,
|
string? sortColumn = null,
|
||||||
@@ -70,11 +71,17 @@ public async Task<ResultContainer<PagedResult<TrackEntity>>> GetPaged(
|
|||||||
IsDescending = sortDescending
|
IsDescending = sortDescending
|
||||||
};
|
};
|
||||||
var result = await _repository.GetPagedAsync(parameters, cancellationToken);
|
var result = await _repository.GetPagedAsync(parameters, cancellationToken);
|
||||||
return ResultContainer<PagedResult<TrackEntity>>.CreatePassResult(result);
|
// Convert to DTO before returning
|
||||||
|
var dtoResult = new PagedResult<TrackDto>(
|
||||||
|
result.Items.Select(TrackConverter.Convert).ToList(),
|
||||||
|
result.TotalCount,
|
||||||
|
result.PageNumber,
|
||||||
|
result.PageSize);
|
||||||
|
return ResultContainer<PagedResult<TrackDto>>.CreatePassResult(dtoResult);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
return ResultContainer<PagedResult<TrackEntity>>.CreateFailResult(e.Message);
|
return ResultContainer<PagedResult<TrackDto>>.CreateFailResult(e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Working plan for two connected workstreams. Standalone execution doc; not part o
|
|||||||
`CONTEXT.md §6` convention, and the `ITrackService` cross-cutting note in `PLAN.md` (line ~192)
|
`CONTEXT.md §6` convention, and the `ITrackService` cross-cutting note in `PLAN.md` (line ~192)
|
||||||
should be retired since this plan resolves it.
|
should be retired since this plan resolves it.
|
||||||
|
|
||||||
**Status:** awaiting Daniel's approval. Engineering is not dispatched until then.
|
**Status:** **Completed** — landed on `dev` (2026-05-25).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user