Public Access
Restructure into src/tests and add build foundation (M0)
Moves the scaffold to src/DodoSSH.Api and establishes the repo conventions the rest
of the milestones build on.
Structure:
- src/{Contracts,Crypto,Domain,Infrastructure,Api}, tests/{Contracts,Crypto,Domain}.Tests
- DodoSSH.slnx rewritten with src/ and tests/ solution folders
Build:
- Directory.Build.props centralises TFM, nullable, deterministic builds and
TreatWarningsAsErrors; Directory.Packages.props pins every version centrally
- packages.lock.json committed so CI restores in locked mode
- NuGet.config clears machine-level sources, which both fixes NU1507 under central
package management and makes restore reproducible off this machine
- Microsoft.OpenApi pinned to 2.11.0: ASP.NET Core 10.0.10 resolves 2.0.0, which is
covered by GHSA-v5pm-xwqc-g5wc (high, patched in 2.7.5)
Analyzers:
- AnalysisLevel is Recommended, not All. With warnings-as-errors, All turns opinionated
naming rules into build breaks and trains people to blanket-suppress.
- BannedSymbols.txt bans DateTime.UtcNow (TimeProvider), Guid.NewGuid (CreateVersion7),
sync-over-async, MD5/SHA1, PBKDF2 and SecureString
- CA1711/CA1724 disabled: both are .NET Framework CAS-era naming rules
- PublicApiAnalyzers on Contracts only, since that assembly is the client's real contract
API:
- weather-forecast template removed
- UseHttpsRedirection removed; TLS terminates at the reverse proxy and redirecting
behind one causes loops
- /healthz/{live,ready,startup}. Liveness deliberately checks no dependencies so a
transient database outage cannot restart the container and kill live SSH sessions.
Notes:
- No coverage collector yet. Microsoft.Testing.Extensions.CodeCoverage pulls an MTP 1.x
MSBuild extension that throws TypeLoadException against the MTP 2.3.x xunit.v3 brings.
Coverage gates are an M3 concern; revisit with an MTP 2.x-aligned version then.
Verified: dotnet build (0 warnings), 17 tests pass, format check clean, API serves
health and OpenAPI endpoints.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
|
||||
namespace DodoSSH.Api.Setup;
|
||||
|
||||
/// <summary>
|
||||
/// Health check wiring.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The split between liveness and readiness is deliberate and load-bearing for the
|
||||
/// relay: <c>/healthz/live</c> checks the process and nothing else, so a transient
|
||||
/// PostgreSQL outage cannot cause the orchestrator to restart the container and
|
||||
/// guillotine every live SSH session. Dependency checks belong in
|
||||
/// <c>/healthz/ready</c>, which only removes the instance from load balancing.
|
||||
/// </remarks>
|
||||
internal static class HealthChecks
|
||||
{
|
||||
/// <summary>Tag for checks that gate readiness (dependencies).</summary>
|
||||
internal const string ReadyTag = "ready";
|
||||
|
||||
/// <summary>Tag for checks that gate startup completion.</summary>
|
||||
internal const string StartupTag = "startup";
|
||||
|
||||
internal static IServiceCollection AddDodoHealthChecks(this IServiceCollection services)
|
||||
{
|
||||
services.AddHealthChecks();
|
||||
|
||||
// Dependency checks are registered by the milestone that introduces the
|
||||
// dependency, each tagged ReadyTag:
|
||||
// M1 — PostgreSQL, OIDC discovery + JWKS reachability, pending migrations
|
||||
// M4 — Data Protection key ring readability
|
||||
return services;
|
||||
}
|
||||
|
||||
internal static WebApplication MapDodoHealthChecks(this WebApplication app)
|
||||
{
|
||||
// Liveness: process is running and the pipeline responds. No dependencies.
|
||||
app.MapHealthChecks("/healthz/live", new HealthCheckOptions
|
||||
{
|
||||
Predicate = _ => false,
|
||||
}).AllowAnonymous();
|
||||
|
||||
// Readiness: safe to route traffic here.
|
||||
app.MapHealthChecks("/healthz/ready", new HealthCheckOptions
|
||||
{
|
||||
Predicate = registration => registration.Tags.Contains(ReadyTag),
|
||||
}).AllowAnonymous();
|
||||
|
||||
// Startup: one-time initialisation finished (K8s startupProbe).
|
||||
app.MapHealthChecks("/healthz/startup", new HealthCheckOptions
|
||||
{
|
||||
Predicate = registration => registration.Tags.Contains(StartupTag),
|
||||
}).AllowAnonymous();
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace DodoSSH.Api.Setup;
|
||||
|
||||
/// <summary>
|
||||
/// OpenAPI document configuration.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The generated document exists for third parties and a future CLI. It is emitted at
|
||||
/// build time to <c>artifacts/openapi/v1.json</c> and diffed in CI so an unintended
|
||||
/// contract change fails the pull request. The desktop client's actual contract is the
|
||||
/// <c>DodoSSH.Contracts</c> assembly, guarded by PublicApiAnalyzers.
|
||||
/// </remarks>
|
||||
internal static class OpenApi
|
||||
{
|
||||
internal const string DocumentName = "v1";
|
||||
|
||||
internal static IServiceCollection AddDodoOpenApi(this IServiceCollection services)
|
||||
{
|
||||
services.AddOpenApi(DocumentName);
|
||||
|
||||
// Added in M1, once there are endpoints to describe:
|
||||
// - a document transformer contributing the OAuth2 authorizationCode + PKCE
|
||||
// security scheme, so the document is usable from a generated client
|
||||
// - a schema transformer mapping byte[] to {type: string, format: byte},
|
||||
// since every ciphertext field crosses the wire as base64
|
||||
return services;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user