mirror of
https://github.com/azaion/missions.git
synced 2026-06-22 09:41:07 +00:00
chore: update configuration and Docker setup for JWT and test results
ci/woodpecker/push/build-arm Pipeline was successful
ci/woodpecker/push/build-arm Pipeline was successful
Enhanced the .gitignore to exclude test results and updated the Dockerfile to include a new entrypoint script for improved container initialization. Refactored JWT configuration to support additional parameters for automatic refresh intervals, ensuring better control over token management. Updated the ConfigurationResolver to enforce required environment variables without hardcoded fallbacks, enhancing security and flexibility.
This commit is contained in:
@@ -8,9 +8,10 @@
|
||||
|
||||
## Preconditions
|
||||
|
||||
- `DATABASE_URL` resolves (env or hardcoded dev fallback).
|
||||
- `DATABASE_URL`, `JWT_ISSUER`, `JWT_AUDIENCE`, `JWT_JWKS_URL` all resolve via `ConfigurationResolver.ResolveRequiredOrThrow`. Any missing value aborts startup before the host is built — there are no hardcoded fallbacks.
|
||||
- `postgres-local` is reachable.
|
||||
- The `azaion` database itself exists in PostgreSQL (created at provisioning time, NOT by this migrator).
|
||||
- `admin` does NOT need to be reachable at process start. The JWKS fetch is lazy — it happens on the first protected request, not during the startup sequence diagrammed below.
|
||||
|
||||
## Sequence Diagram
|
||||
|
||||
@@ -26,9 +27,12 @@ sequenceDiagram
|
||||
participant DB as postgres-local
|
||||
|
||||
Docker->>Host: ENTRYPOINT dotnet Azaion.Missions.dll
|
||||
Host->>Cfg: read DATABASE_URL → ConvertPostgresUrl → Npgsql connection string
|
||||
Host->>Cfg: read JWT_SECRET (env or hardcoded fallback)
|
||||
Host->>Identity: AddJwtAuth(jwtSecret) — DI registration only, no network
|
||||
Host->>Cfg: ResolveRequiredOrThrow DATABASE_URL → ConvertPostgresUrl → Npgsql connection string
|
||||
Host->>Cfg: ResolveRequiredOrThrow JWT_ISSUER, JWT_AUDIENCE, JWT_JWKS_URL — throw on any miss
|
||||
Host->>Identity: AddJwtAuth(builder.Configuration) — DI registration + ConfigurationManager<JsonWebKeySet> wiring (no network yet)
|
||||
Host->>Cfg: read CorsConfig:AllowedOrigins, CorsConfig:AllowAnyOrigin
|
||||
Host->>Cfg: CorsConfigurationValidator.EnsureSafeForEnvironment — throws in Production when origins=[] AND allowAnyOrigin=false
|
||||
Host->>DI: register CORS policy (permissive OR WithOrigins(...))
|
||||
Host->>DI: register controllers + middleware + scoped AppDataConnection + scoped services
|
||||
Host->>DI: build Host
|
||||
Host->>Migrator: scope.Resolve<AppDataConnection>(); Migrate(db)
|
||||
@@ -41,8 +45,9 @@ sequenceDiagram
|
||||
Migrator->>DB: DROP TABLE IF EXISTS gps_corrections
|
||||
note right of Migrator: B9 one-shot. Idempotent on devices that already cleaned up.
|
||||
Migrator-->>Host: void
|
||||
Host->>Host: emit PermissiveDefaultWarning if implicit-permissive CORS applies (non-Production with empty origins)
|
||||
Host->>Host: register ErrorHandlingMiddleware FIRST in pipeline
|
||||
Host->>Host: UseAuthentication / UseAuthorization
|
||||
Host->>Host: UseCors / UseAuthentication / UseAuthorization
|
||||
Host->>Host: MapControllers + MapGet("/health") + UseSwagger
|
||||
Host->>Docker: app.Run() — listening on 0.0.0.0:8080
|
||||
```
|
||||
@@ -51,15 +56,24 @@ sequenceDiagram
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([Container start]) --> ReadCfg[Read DATABASE_URL + JWT_SECRET]
|
||||
ReadCfg --> RegDI[DI registrations: controllers, middleware, scoped DB + services]
|
||||
Start([Container start]) --> ResolveDB[ResolveRequiredOrThrow DATABASE_URL]
|
||||
ResolveDB --> ResolveJwt["ResolveRequiredOrThrow JWT_ISSUER, JWT_AUDIENCE, JWT_JWKS_URL"]
|
||||
ResolveJwt --> CorsCfg[Read CorsConfig:AllowedOrigins + CorsConfig:AllowAnyOrigin]
|
||||
CorsCfg --> CorsGate{Production AND origins=[] AND allowAnyOrigin=false?}
|
||||
CorsGate -->|yes| FailFast([InvalidOperationException — Watchtower restarts])
|
||||
CorsGate -->|no| RegDI[DI registrations: JWT bearer + JWKS manager, CORS, controllers, scoped DB + services]
|
||||
RegDI --> Build[Build Host]
|
||||
Build --> OpenScope[Open startup scope]
|
||||
Build --> CorsWarn{Implicit-permissive CORS in this env?}
|
||||
CorsWarn -->|yes| LogWarn[Log PermissiveDefaultWarning]
|
||||
CorsWarn -->|no| OpenScope[Open startup scope]
|
||||
LogWarn --> OpenScope
|
||||
OpenScope --> Migrate[Run DatabaseMigrator.Migrate]
|
||||
Migrate --> Create["CREATE TABLE IF NOT EXISTS x4 + indexes"]
|
||||
Create --> Drop["DROP TABLE IF EXISTS orthophotos, gps_corrections (B9)"]
|
||||
Drop --> Pipeline[Wire pipeline: error MW first, auth, controllers, /health, Swagger]
|
||||
Pipeline --> Run([app.Run on :8080])
|
||||
ResolveDB -. on missing value .-> FailFast
|
||||
ResolveJwt -. on missing value .-> FailFast
|
||||
Migrate -. on failure .-> Crash([Process exits non-zero — Watchtower restarts])
|
||||
```
|
||||
|
||||
@@ -67,15 +81,18 @@ flowchart TD
|
||||
|
||||
| Step | From | To | Data | Format |
|
||||
|------|------|----|------|--------|
|
||||
| 1 | Environment / appsettings | `Program.cs` | `DATABASE_URL`, `JWT_SECRET` | string |
|
||||
| 2 | `Program.cs` | DI container | service registrations | C# code |
|
||||
| 3 | `DatabaseMigrator` | `postgres-local` | DDL statements (CREATE / INDEX / DROP) | SQL |
|
||||
| 4 | `Program.cs` | OS / Docker | bind to `0.0.0.0:8080` | TCP listener |
|
||||
| 1 | Environment / IConfiguration | `Program.cs` (via `ConfigurationResolver`) | `DATABASE_URL`, `JWT_ISSUER`, `JWT_AUDIENCE`, `JWT_JWKS_URL` | string (required) |
|
||||
| 2 | Environment / IConfiguration | `Program.cs` | `CorsConfig:AllowedOrigins` (string[]), `CorsConfig:AllowAnyOrigin` (bool) | optional |
|
||||
| 3 | `Program.cs` | DI container | service registrations + JWT bearer + JWKS `ConfigurationManager` | C# code |
|
||||
| 4 | `DatabaseMigrator` | `postgres-local` | DDL statements (CREATE / INDEX / DROP) | SQL |
|
||||
| 5 | `Program.cs` | OS / Docker | bind to `0.0.0.0:8080` | TCP listener |
|
||||
|
||||
## Error Scenarios
|
||||
|
||||
| Error | Where | Detection | Recovery |
|
||||
|-------|-------|-----------|----------|
|
||||
| Missing `DATABASE_URL` / `JWT_ISSUER` / `JWT_AUDIENCE` / `JWT_JWKS_URL` | `ConfigurationResolver.ResolveRequiredOrThrow` | env var + config key both empty/whitespace | Process exits non-zero with `InvalidOperationException` whose message names the missing env var and config key. Watchtower restarts but the new container hits the same failure. **Fix**: provide the value via env or `appsettings.json` |
|
||||
| CORS misconfigured in Production (`CorsConfig:AllowedOrigins=[]` AND `CorsConfig:AllowAnyOrigin!=true`) | `CorsConfigurationValidator.EnsureSafeForEnvironment` at startup | hard-fail guard | Process exits with `InvalidOperationException("CORS is misconfigured: ...")`. **Fix**: set `CorsConfig:AllowedOrigins` to the production UI origins, or set `CorsConfig:AllowAnyOrigin=true` to opt in explicitly |
|
||||
| `postgres-local` unreachable | Migrate step | Npgsql `IOException` / `SocketException` | Process exits non-zero. Watchtower restarts; `flight-gate` prevents restart mid-mission. **Fix**: ensure `postgres-local` healthcheck passes before `missions` starts (compose `depends_on` with `condition: service_healthy`) |
|
||||
| `azaion` database missing | Migrate step | Npgsql `3D000` (`invalid_catalog_name`) | Process exits. Operator must create the database — provisioning concern, not this service. Documented in `../../suite/_docs/00_top_level_architecture.md` |
|
||||
| `DROP TABLE IF EXISTS orthophotos` fails because table is locked by `gps-denied` | B9 one-shot | Lock timeout or `55006` | Process exits, Watchtower restarts in a few seconds. **Out-of-band ordering**: deploy `gps-denied` FIRST so it has its own copy of the schema before `missions` drops the legacy tables. Documented in B9 ticket |
|
||||
|
||||
Reference in New Issue
Block a user