Files
satellite-provider/_docs/02_document/modules/api_program.md
T
Oleksandr Bezdieniezhnykh 9cfd80babe
ci/woodpecker/push/01-test Pipeline was successful
ci/woodpecker/push/02-build-push Pipeline was successful
[AZ-495] [AZ-496] Cycle 3 batch 1: doc convention + AspNetCore 8.0.25
AZ-495 (1 SP): formalize the modules-only documentation convention for
the WebApi component. _docs/02_document/module-layout.md now carries an
explicit Documentation Layout section anchoring WebApi docs at
modules/api_program.md; the components/06_web_api/ folder is
intentionally absent. .cursor/skills/new-task/SKILL.md Step 4 directs
future agents at the correct path. Cycle-1 + cycle-2 F1 findings in the
two batch-review files are marked RESOLVED with back-reference to
AZ-495. Cycle-2 retrospective decision-item list F1 updated.

AZ-496 (2 SP): bump Microsoft.AspNetCore.OpenApi and JwtBearer in
SatelliteProvider.Api.csproj from 8.0.21 to 8.0.25, closing CVE-
2026-26130 (SignalR DoS - not reachable in this app, but the runtime
patch is the recommended hardening per cycle-1 D1 + cycle-2 D3).
SatelliteProvider.Tests.csproj has no direct JwtBearer reference - it
consumes JwtBearer transitively via ProjectReference to Api, so no
edit needed there. Dockerfiles use floating mcr.microsoft.com/
dotnet/aspnet:8.0 / sdk:8.0 / runtime:8.0 tags which auto-resolve to
>= 8.0.25 on rebuild. Security artifacts (dependency_scan.md,
security_report.md) and current-state docs (module-layout.md,
architecture.md, modules/api_program.md, modules/tests_unit.md)
updated to reflect 8.0.25.

Batch report + code review report (verdict PASS_WITH_WARNINGS with 2
Low findings, neither blocking) written under _docs/03_implementation.

Test suite gate deferred to Step 16 (Final Test Run) per implement
skill convention. Patch-level bump within .NET 8 LTS; regression risk
very low.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 01:24:48 +03:00

104 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Module: Api/Program.cs
## Purpose
Application entry point. Configures DI container, sets up middleware, defines minimal API endpoints, runs database migrations on startup, and starts background services.
## Public Interface
### API Endpoints
| Method | Route | Handler | Description |
|--------|-------|---------|-------------|
| GET | `/tiles/{z}/{x}/{y}` | `ServeTile` | Slippy map tile server with in-memory caching |
| GET | `/api/satellite/tiles/latlon` | `GetTileByLatLon` | Download single tile by lat/lon/zoom |
| GET | `/api/satellite/tiles/mgrs` | `GetSatelliteTilesByMgrs` | MGRS stub (returns empty) |
| POST | `/api/satellite/upload` | `UploadUavTileBatch` | UAV tile batch upload (AZ-488) — multipart envelope, 5-rule quality gate, per-source UPSERT with `source='uav'`. Requires the `RequiresGpsPermission` policy. |
| POST | `/api/satellite/request` | `RequestRegion` | Queue region for async tile processing |
| GET | `/api/satellite/region/{id}` | `GetRegionStatus` | Get region processing status |
| POST | `/api/satellite/route` | `CreateRoute` | Create route with intermediate points |
| GET | `/api/satellite/route/{id}` | `GetRoute` | Get route with all points |
### Local Records (defined in Program.cs)
- `GetSatelliteTilesResponse`, `SatelliteTile` — MGRS response stubs
- `DownloadTileResponse` — tile download response
- `RequestRegionRequest` — region request body
- `ParameterDescriptionFilter` — Swagger operation filter
### Api/DTOs (AZ-488)
- `UavTileBatchUploadRequest` — multipart envelope with `metadata` (JSON string) and `files` (`IFormFileCollection`)
### Common/DTO (AZ-488)
- `UavTileMetadata`, `UavTileBatchMetadataPayload` — per-item metadata + envelope shape
- `UavTileBatchUploadResponse`, `UavTileUploadResultItem` — per-item response shape
- `UavTileUploadStatus`, `UavTileRejectReasons` — string-constant enumerations exposed in the v1.0.0 contract
## Internal Logic
### DI Registration
1. Serilog configured from `appsettings.json`
2. Connection string extracted from `ConnectionStrings:DefaultConnection`
3. Config bindings: `MapConfig`, `StorageConfig`, `ProcessingConfig`, `UavQualityConfig` (AZ-488)
4. **Request size limits (AZ-488)**: `KestrelServerOptions.Limits.MaxRequestBodySize` and `FormOptions.MultipartBodyLengthLimit` are set to `UavQualityConfig.MaxBatchSize × UavQualityConfig.MaxBytes` (default 100 × 5 MiB = 500 MiB) so an oversized UAV batch is rejected at the framework layer before reaching the handler.
5. Singletons: repositories (`TileRepository`, `RegionRepository`, `RouteRepository`), `GoogleMapsDownloaderV2`, `ITileService`, `IRegionService`, `IRouteService`, `IUavTileQualityGate`, `IUavTileUploadHandler` (AZ-488)
6. `IRegionRequestQueue` with configurable capacity
7. Hosted services: `RegionProcessingService`, `RouteProcessingService`
8. CORS policy: `TilesCors` — configured origins from `CorsConfig:AllowedOrigins`, falls back to allow-any
9. JSON options: camelCase, case-insensitive
10. **JWT authentication (AZ-487)**: `AddSatelliteJwt(builder.Configuration)` (extension in `SatelliteProvider.Api.Authentication`) registers `JwtBearer` with `TokenValidationParameters` set per the suite auth contract (signature + lifetime, no issuer/audience validation, 30 s clock skew, ≥ 32-byte HMAC key). Followed by `AddAuthorization` with the `RequiresGpsPermission` policy (AZ-488). The `PermissionsAuthorizationHandler` singleton supports both repeated-string and JSON-array shapes for the `permissions` claim.
### Startup
1. Database migration via `DatabaseMigrator.RunMigrations()` — throws on failure
2. Creates tiles and ready directories
3. Swagger enabled in Development mode
4. Middleware chain (order matters): `UseExceptionHandler``UseHttpsRedirection``UseCors("TilesCors")``UseAuthentication``UseAuthorization` → endpoint mapping.
5. Every `MapGet`/`MapPost` endpoint is decorated with `.RequireAuthorization()`; the framework returns 401 before the handler runs for any anonymous, expired, or invalid-signature request.
### ServeTile Handler
1. Checks `IMemoryCache` for tile bytes (1h absolute, 30min sliding expiration)
2. If cache miss: queries `ITileRepository.GetByTileCoordinatesAsync`
3. If no DB record: downloads tile via `GoogleMapsDownloaderV2.DownloadSingleTileAsync`, creates `TileEntity`, inserts
4. Returns image bytes with cache headers (`Cache-Control: public, max-age=86400`)
### GetTileByLatLon Handler
Downloads a tile, persists it, returns metadata as `DownloadTileResponse`.
### RequestRegion Handler
Validates size (10010000m), delegates to `IRegionService.RequestRegionAsync`.
### UploadUavTileBatch Handler (AZ-488)
Buffers each `IFormFile` into memory, packages them as `UavUploadFile` records (filename, content-type, bytes), and delegates to `IUavTileUploadHandler.HandleAsync`. Envelope-level errors (mismatched batch, oversized batch, malformed metadata) are surfaced as HTTP 400 ProblemDetails; per-item rejects are returned in the HTTP 200 response payload. The endpoint is protected by `.RequireAuthorization(SatellitePermissions.UavUploadPolicy)` so 401 (no token) and 403 (no `GPS` permission) are returned before the handler runs.
## Dependencies
All project references: Common, DataAccess, Services.
NuGet: `Serilog.AspNetCore`, `Swashbuckle.AspNetCore`, `Microsoft.AspNetCore.OpenApi` (8.0.25, bumped from 8.0.21 by AZ-496), `Microsoft.AspNetCore.Authentication.JwtBearer` (8.0.25, added at 8.0.21 by AZ-487, bumped by AZ-496), `SixLabors.ImageSharp`, `Newtonsoft.Json`.
## Consumers
- HTTP clients (external)
- Integration tests (via HTTP)
## Data Models
Defines several local request/response records that are not shared with other projects.
## Configuration
All configuration sections are consumed here:
- `ConnectionStrings:DefaultConnection`
- `MapConfig`, `StorageConfig`, `ProcessingConfig`
- `UavQuality` (AZ-488) — `MinBytes`, `MaxBytes`, `MaxAgeDays`, `CapturedAtFutureSkewSeconds`, `MinLuminanceVariance`, `MaxBatchSize`, `LuminanceSampleSize`. Drives the 5-rule quality gate AND the per-request body-size limits.
- `CorsConfig:AllowedOrigins`
- `Jwt:Secret` — HMAC-SHA256 signing key for JWT validation (AZ-487). Resolution: `JWT_SECRET` env var (preferred, opaque production secret) → `Jwt:Secret` configuration key (`appsettings.Development.json` placeholder only). Startup fails fast if the resolved value is unset, empty, or shorter than 32 bytes.
- `Serilog` section
## External Integrations
- Google Maps (indirectly via `GoogleMapsDownloaderV2`)
- PostgreSQL (via repositories and DatabaseMigrator)
- File system (`./tiles/`, `./ready/`)
## Security
- CORS configured (permissive by default when no origins specified)
- Swagger only in Development; Bearer token "Authorize" button registered via `AddSecurityDefinition`/`AddSecurityRequirement` (AZ-487)
- HTTPS redirection enabled
- JWT bearer authentication (AZ-487) — every endpoint requires a valid HS256-signed token. Anonymous, expired, or signature-tampered requests return 401 before the handler runs.
- Permission-claim policies (AZ-488) — `POST /api/satellite/upload` is wrapped in `.RequireAuthorization(SatellitePermissions.UavUploadPolicy)`. The `PermissionsAuthorizationHandler` reads the `permissions` claim (repeated-string OR JSON-array shape) and returns 403 if `GPS` is not present.
## Tests
Integration tests exercise all endpoints. Unit test project has only a dummy test.