mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 10:21:10 +00:00
refactor: remove deploy.cmd and update Dockerfile for health checks
- Deleted the deploy.cmd script as it was no longer needed. - Updated Dockerfile to include curl for health checks and added a non-root user for improved security. - Modified health check command to use curl for better reliability. - Adjusted docker-compose.test.yml to reflect changes in health check configuration. - Cleaned up appsettings.json and removed unused configuration properties. - Removed Resource entity and related requests from the codebase as part of the architectural shift. - Updated documentation to reflect the removal of hardware binding and related endpoints. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+45
-31
@@ -6,6 +6,7 @@ using Azaion.Common.Entities;
|
||||
using Azaion.Common.Requests;
|
||||
using Azaion.Services;
|
||||
using FluentValidation;
|
||||
using LinqToDB.Data;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -33,6 +34,17 @@ if (jwtConfig == null || string.IsNullOrEmpty(jwtConfig.Secret))
|
||||
throw new Exception("Missing configuration section: JwtConfig");
|
||||
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtConfig.Secret));
|
||||
|
||||
// Fail-fast for DB connection strings — surfaces a missing env var at startup
|
||||
// instead of on the first request to a DB-backed endpoint.
|
||||
var connectionStrings = builder.Configuration.GetSection(nameof(ConnectionStrings)).Get<ConnectionStrings>();
|
||||
if (connectionStrings == null
|
||||
|| string.IsNullOrEmpty(connectionStrings.AzaionDb)
|
||||
|| string.IsNullOrEmpty(connectionStrings.AzaionDbAdmin))
|
||||
throw new Exception("Missing configuration section: ConnectionStrings (AzaionDb and AzaionDbAdmin are required)");
|
||||
|
||||
// Graceful shutdown: 30 s for in-flight requests; pair with `docker stop -t 40`.
|
||||
builder.Services.Configure<HostOptions>(o => o.ShutdownTimeout = TimeSpan.FromSeconds(30));
|
||||
|
||||
builder.Services.AddSerilog();
|
||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(o =>
|
||||
@@ -54,13 +66,9 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
var apiAdminPolicy = new AuthorizationPolicyBuilder()
|
||||
.RequireRole(RoleEnum.ApiAdmin.ToString()).Build();
|
||||
|
||||
var apiUploaderPolicy = new AuthorizationPolicyBuilder()
|
||||
.RequireRole(RoleEnum.ResourceUploader.ToString(), RoleEnum.ApiAdmin.ToString()).Build();
|
||||
|
||||
builder.Services.AddAuthorization(o =>
|
||||
{
|
||||
o.AddPolicy(nameof(apiAdminPolicy), apiAdminPolicy);
|
||||
o.AddPolicy(nameof(apiUploaderPolicy), apiUploaderPolicy);
|
||||
});
|
||||
|
||||
#endregion Policies
|
||||
@@ -98,7 +106,6 @@ builder.Services.AddScoped<IUserService, UserService>();
|
||||
builder.Services.AddScoped<IAuthService, AuthService>();
|
||||
builder.Services.AddScoped<IResourcesService, ResourcesService>();
|
||||
builder.Services.AddScoped<IDetectionClassService, DetectionClassService>();
|
||||
builder.Services.AddScoped<IResourceUpdateService, ResourceUpdateService>();
|
||||
builder.Services.AddSingleton<IDbFactory, DbFactory>();
|
||||
|
||||
builder.Services.AddLazyCache();
|
||||
@@ -134,6 +141,39 @@ app.UseAuthorization();
|
||||
|
||||
app.UseRewriter(new RewriteOptions().AddRedirect("^$", "/swagger"));
|
||||
|
||||
#region Health endpoints
|
||||
// Anonymous; expected to be exposed only on the management interface (not via the
|
||||
// public Nginx vhost). Surface contract documented in
|
||||
// _docs/04_deploy/deployment_procedures.md §2 and observability.md §7.
|
||||
|
||||
app.MapGet("/health/live", (HttpContext http) =>
|
||||
{
|
||||
http.Response.Headers.CacheControl = "no-store";
|
||||
return Results.Ok(new { status = "live" });
|
||||
}).AllowAnonymous().ExcludeFromDescription();
|
||||
|
||||
app.MapGet("/health/ready", async (IDbFactory dbFactory, HttpContext http, CancellationToken ct) =>
|
||||
{
|
||||
http.Response.Headers.CacheControl = "no-store";
|
||||
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
timeoutCts.CancelAfter(TimeSpan.FromSeconds(2));
|
||||
try
|
||||
{
|
||||
await dbFactory.Run(db => db.ExecuteAsync<int>("SELECT 1"));
|
||||
await dbFactory.RunAdmin(db => db.ExecuteAsync<int>("SELECT 1"));
|
||||
return Results.Ok(new { status = "ready" });
|
||||
}
|
||||
catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested && !ct.IsCancellationRequested)
|
||||
{
|
||||
return Results.Json(new { status = "not-ready", reason = "db-timeout" }, statusCode: 503);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Results.Json(new { status = "not-ready", reason = ex.GetType().Name }, statusCode: 503);
|
||||
}
|
||||
}).AllowAnonymous().ExcludeFromDescription();
|
||||
#endregion Health endpoints
|
||||
|
||||
app.MapPost("/login",
|
||||
async (LoginRequest request, IUserService userService, IAuthService authService, CancellationToken cancellationToken) =>
|
||||
{
|
||||
@@ -298,32 +338,6 @@ app.MapDelete("/classes/{id:int}",
|
||||
.RequireAuthorization(apiAdminPolicy)
|
||||
.WithSummary("Deletes a detection class");
|
||||
|
||||
app.MapPost("/get-update",
|
||||
async (GetUpdateRequest request, IValidator<GetUpdateRequest> validator,
|
||||
IResourceUpdateService resourceUpdateService, CancellationToken ct) =>
|
||||
{
|
||||
var validation = await validator.ValidateAsync(request, ct);
|
||||
if (!validation.IsValid)
|
||||
return Results.ValidationProblem(validation.ToDictionary());
|
||||
var updates = await resourceUpdateService.GetUpdate(request, ct);
|
||||
return Results.Ok(updates);
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithSummary("Returns resources newer than the device's reported current versions");
|
||||
|
||||
app.MapPost("/resources/publish",
|
||||
async (PublishResourceRequest request, IValidator<PublishResourceRequest> validator,
|
||||
IResourceUpdateService resourceUpdateService, CancellationToken ct) =>
|
||||
{
|
||||
var validation = await validator.ValidateAsync(request, ct);
|
||||
if (!validation.IsValid)
|
||||
return Results.ValidationProblem(validation.ToDictionary());
|
||||
await resourceUpdateService.Publish(request, ct);
|
||||
return Results.Ok();
|
||||
})
|
||||
.RequireAuthorization(apiUploaderPolicy)
|
||||
.WithSummary("CI/CD: publish a new resource version (encrypts encryption_key at rest, invalidates the per-(arch,stage) latest-versions cache)");
|
||||
|
||||
app.UseExceptionHandler(_ => {});
|
||||
|
||||
app.Run();
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
"ResourcesConfig": {
|
||||
"ResourcesFolder": "Content",
|
||||
"SuiteInstallerFolder": "suite",
|
||||
"SuiteStageInstallerFolder": "suite-stage",
|
||||
"EncryptionMasterKey": ""
|
||||
"SuiteStageInstallerFolder": "suite-stage"
|
||||
},
|
||||
"JwtConfig": {
|
||||
"Issuer": "AzaionApi",
|
||||
|
||||
Reference in New Issue
Block a user