Rewrite all folder-level CLAUDE.md files; update root

net10.0 throughout; corrected EntryKey field name; documented *.Services split.
Two new files (DeepDrftWeb.Services, DeepDrftContent.Services). FileDatabase
README inaccuracies fixed (ImageVault, project target).
This commit is contained in:
Daniel Harvey
2026-05-16 21:45:56 -04:00
parent de0909f38f
commit 7cf3d965b6
10 changed files with 1233 additions and 869 deletions
+113 -157
View File
@@ -1,187 +1,143 @@
# CLAUDE.md - DeepDrftContent
This file provides guidance to Claude Code (claude.ai/code) when working with the DeepDrftContent project.
Guidance for working in the DeepDrftContent project (the binary content API host).
## Project Overview
See the root `CLAUDE.md` for full architecture overview. This file covers what is specific to this project.
DeepDrftContent is a **Web API project** that serves as the content management backend for the DeepDrft system. It provides secure API endpoints for managing the FileDatabase system and handles binary media content storage and retrieval.
## One-line purpose
## Architecture
The binary content API host. ApiKey middleware, CORS, forwarded headers. Returns audio bytes (with optional WAV-aware offset) and accepts authenticated PUTs. **FileDatabase implementation lives in `DeepDrftContent.Services`, not here.**
### Technology Stack
- **ASP.NET Core Web API 9.0**: RESTful API framework
- **Custom FileDatabase System**: Binary file storage with vault management
- **API Key Authentication**: Secure endpoint protection
- **OpenAPI/Swagger**: Development API documentation
## What lives here now (only)
### Project Structure
```
DeepDrftContent/
├── Controllers/ # API endpoint controllers
│ ├── TrackController.cs # Track media management
│ └── WeatherForecastController.cs # Demo endpoint
├── FileDatabase/ # Core FileDatabase implementation
│ ├── Services/ # Database and vault services
│ ├── Models/ # Data models and DTOs
│ ├── Utils/ # Utility classes
│ └── Abstractions/ # Interfaces and contracts
├── Middleware/ # Custom middleware
│ ├── ApiKeyAuthenticationMiddleware.cs
│ └── ApiKeyAuthorizeAttribute.cs
├── Models/ # Application models
├── environment/ # Configuration files
│ ├── filedatabase.json # FileDatabase settings
│ └── apikey.json # API authentication (not in repo)
└── Program.cs # Application entry point
```
- `Program.cs`, `Startup.cs`: HTTP host config, DI wiring, middleware setup, port binding.
- `Controllers/TrackController.cs`: Two endpoints (see below).
- `Middleware/ApiKeyAuthenticationMiddleware.cs`, `Middleware/ApiKeyAuthorizeAttribute.cs`: ApiKey validation logic.
- `Models/`: Settings POCOs only (`ApiKeySettings`, `CorsSettings`, `FileDatabaseSettings`). No domain code.
- `environment/filedatabase.json`: FileDatabase vault path config (required).
- `environment/apikey.json`: API key (not in repo, must be created locally or at deployment).
## Core FileDatabase System
## What does NOT live here anymore
### Key Components
- Any `FileDatabase/`, `Services/`, or `Processors/` code — all moved to `DeepDrftContent.Services`.
- Media models (`AudioBinary`, `ImageBinary`, etc.) — in `DeepDrftContent.Services`.
- `WavOffsetService` — in `DeepDrftContent.Services`.
- Don't add new domain code to this project.
#### FileDatabase Service
Main orchestration class managing multiple MediaVaults:
```csharp
public class FileDatabase : DirectoryIndexDirectory
{
// Factory creation
public static async Task<FileDatabase?> FromAsync(string rootPath)
// Vault management
public async Task CreateVaultAsync(string vaultId, MediaVaultType vaultType)
public MediaVault? GetVault(string vaultId)
// Resource operations
public async Task<T?> LoadResourceAsync<T>(string vaultId, string entryId) where T : FileBinary
public async Task<bool> RegisterResourceAsync(string vaultId, string entryId, FileBinary media)
}
```
## The endpoint surface (exactly two endpoints)
#### MediaVault Types
- **MediaVaultType.Media**: General binary media storage
- **MediaVaultType.Audio**: Audio-specific storage with duration/bitrate
- **MediaVaultType.Image**: Image storage with aspect ratio
### GET api/track/{trackId}?offset=0 (unauthenticated)
#### Media Models Hierarchy
```
FileBinary (base)
├── MediaBinary (+ Extension, MIME type)
├── AudioBinary (+ Duration, Bitrate)
└── ImageBinary (+ AspectRatio)
```
Returns the WAV bytes from the `tracks` vault.
### Index System
- **DirectoryIndex**: Manages vault organization
- **VaultIndex**: Individual vault metadata and type information
- **JSON-based storage**: All indexes stored as JSON files
- **Query parameter `offset`** (optional, default 0): byte position to start streaming from.
- If `offset == 0`: streams the entire file from the beginning.
- If `offset > 0`: `WavOffsetService.CreateOffsetStream` block-aligns the offset and synthesises a fresh 44-byte WAV header so the response is a valid standalone WAV starting from that byte position. This is load-bearing for seek-beyond-buffer — the player asks for a new stream at the offset it wants to seek to, gets back a valid WAV that starts there, and tears down/re-initialises the decoder.
- Returns 404 if track not found. Returns 500 if vault operations fail (with error swallowing — the vault returns `null`).
## API Authentication
### PUT api/track/{trackId} ([ApiKeyAuthorize])
### Custom API Key Middleware
```csharp
// Usage in controllers
[ApiKeyAuthorize]
[HttpPut("{trackId}")]
public async Task<ActionResult> PutTrack([FromQuery] string trackId, [FromBody] AudioBinaryDto track)
```
**Authenticated endpoint.** Writes audio bytes to the `tracks` vault.
### Authentication Flow
1. Client sends request with `ApiKey` header
2. `ApiKeyAuthenticationMiddleware` validates against configured key
3. Endpoints marked with `[ApiKeyAuthorize]` require valid API key
4. Returns 401 for missing/invalid keys
- **Header `ApiKey`**: required. Validated by `ApiKeyAuthenticationMiddleware`.
- **Body**: `AudioBinaryDto` (base64 buffer + size + mime + duration + bitrate).
- `TrackService.AddTrackFromWavAsync` (via DI) is NOT called here — the endpoint just validates and writes to the vault. The caller (CLI) is responsible for then saving the returned `TrackEntity` to SQL.
- Actually: the endpoint is rarely used in production (the CLI calls `FileDatabase.RegisterResourceAsync` directly). But the endpoint exists for potential web-side uploads in future.
- Returns 200 on success, 401 if ApiKey invalid, 400 if body invalid.
## Development Commands
**Do not add a third endpoint without product approval.** The surface is intentionally minimal.
## ApiKey middleware behaviour
`ApiKeyAuthenticationMiddleware` runs on every request but only enforces on endpoints with `[ApiKeyAuthorize]` metadata.
- Reads header `ApiKey`.
- Compares against `ApiKeySettings.ApiKey` from `environment/apikey.json`.
- Returns 401 with body `"API Key was not provided"` or `"Unauthorized client"` if validation fails.
- Endpoints without `[ApiKeyAuthorize]` skip the check entirely (e.g., `GET api/track/{id}` is unauthenticated).
## CORS configuration
`CorsSettings.AllowedOrigins` is **required** — the app throws on startup if missing. Policy is named `ContentApiPolicy`:
- `AllowCredentials()`
- `AllowAnyMethod()`
- `AllowAnyHeader()`
Configured in `Startup.ConfigureDomainServices()`, applied to all endpoints via `UseCors()`.
## Forwarded headers
**Enabled only in `Production` mode** (via `if (app.Environment.IsProduction())`). This differs from `DeepDrftWeb`, which enables them always. Be aware when debugging proxy issues.
`UseForwardedHeaders()` processes `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host` so the app knows its real client IP and scheme when sitting behind nginx.
## Startup wiring (Startup.ConfigureDomainServices)
1. Load `environment/filedatabase.json` and bind `FileDatabaseSettings`.
2. Await `FileDatabase.FromAsync(VaultPath)` to load or create the database.
3. Register `FileDatabase` as singleton.
4. Ensure the `tracks` vault exists (type `MediaVaultType.Audio`, created on first boot if missing).
5. Register singleton `WavOffsetService`.
6. Register named `IHttpClientFactory` client (for future use, e.g., fetching from external APIs).
7. Add MudBlazor (if shared components need it in future).
8. Add CORS policy.
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.
## OpenAPI
Mapped in `Development` only. Swagger UI at `/swagger` for testing endpoints locally.
## Configuration files
- `appsettings.json`: Logging, hosting config. **Does not contain secrets.**
- `environment/filedatabase.json` (required):
```json
{
"FileDatabaseSettings": {
"VaultPath": "../Database/Vaults"
}
}
```
- `environment/apikey.json` (required at runtime, not in repo):
```json
{
"ApiKeySettings": {
"ApiKey": "your-secret-key"
}
}
```
## Development commands
### Running the API
```bash
# Run development server
# Run the content API (default https://localhost:5002)
dotnet run --project DeepDrftContent
# Run with specific environment
dotnet run --project DeepDrftContent --environment Development
```
# Watch during development
dotnet watch run --project DeepDrftContent
### Building
```bash
# Build project
# Build
dotnet build DeepDrftContent
# Publish for deployment
dotnet publish DeepDrftContent -c Release
# Test endpoints (requires API key in environment/apikey.json)
curl -H "ApiKey: your-secret-key" -X PUT https://localhost:5002/api/track/test-id \
-H "Content-Type: application/json" \
-d '{"buffer":"base64-encoded-audio","size":1000,"mime":"audio/wav"}'
curl https://localhost:5002/api/track/test-id?offset=0
```
### Testing FileDatabase
```bash
# Run FileDatabase-specific tests
dotnet test DeepDrftTests/ --filter "FileDatabaseTests"
```
## Important patterns
## Configuration
- **Result types**: Controllers return `ActionResult<T>`. Service calls return `Result` or `ResultContainer<T>` from NetBlocks. The controller checks `Success` and returns 200/4xx/5xx accordingly.
- **Error swallowing**: FileDatabase operations return `null` or `false` on failure. The controller surfaces this as 500. Never throw — check return values.
- **Async/await**: All operations are async.
- **Vault operations**: Always use the injected `FileDatabase` singleton. Never construct a new one — it has the `IndexWatcher` and is the source of truth.
### FileDatabase Settings
`environment/filedatabase.json`:
```json
{
"FileDatabaseSettings": {
"VaultPath": "../Database/Vaults"
}
}
```
## The FileDatabase import
### API Key Configuration
`environment/apikey.json` (not in repository):
```json
{
"ApiKeySettings": {
"ApiKey": "your-secret-api-key"
}
}
```
See `DeepDrftContent.Services/CLAUDE.md` for the FileDatabase API and semantics. This host only provides the HTTP surface over it.
## Key Patterns
### Factory Pattern
MediaVault creation uses factory pattern:
```csharp
var directoryVault = await MediaVaultFactory.From(path, vaultType);
```
### Async/Await Throughout
All FileDatabase operations are async with consistent error handling:
```csharp
// Swallow exceptions and return null/false for failed operations
try { /* operation */ }
catch { return null; } // or return false;
```
### Type-Safe Media Handling
Generic methods ensure type safety for media operations:
```csharp
var audio = await fileDatabase.LoadResourceAsync<AudioBinary>("tracks", trackId);
```
### MIME Type Management
Automatic extension/MIME type conversion via `MimeTypeExtensions`:
```csharp
// Supported audio formats: mp3, wav, flac, aac, ogg, m4a
// Supported image formats: jpg, png, gif, webp, svg, bmp
```
## Important Notes
### Vault Organization
- Each vault is a directory with its own index
- Entry IDs are sanitized to create safe filenames
- Media keys generated via regex: `[^a-zA-Z0-9]``-`
### Error Handling Philosophy
FileDatabase uses "swallow and return null" pattern to match TypeScript behavior:
- Failed operations return `null` or `false`
- No exceptions propagated to callers
- Consistent behavior across all async operations
### Binary Data Handling
All media stored as `byte[]` buffers with associated metadata (size, extension, duration, etc.)
When working with this project, focus on the FileDatabase system architecture and maintain the established patterns for vault management, async operations, and API security.
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`.