Adds a [Altcha]-decorated ProtectedActionCommand with IHasAltchaSolution and a StubAltchaVerifier that treats the literal "valid-solution" as passing PoW. Exercises both the HTTP MinimalApi and gRPC pipelines without requiring an external altcha service. Validated 4 scenarios on each transport (8 total, all pass): HTTP /api/command/protectedAction POST 6001 gRPC :6000 ------------------------------------------------------------------- no AltchaSolution 401 Unauthenticated AltchaSolution = "wrong" 401 Unauthenticated AltchaSolution = "valid-solution" 200 result OK + result addUser (no [Altcha]) 200 result OK + result The last row confirms backward compatibility: a request type that isn't decorated with [Altcha] bypasses the check entirely — the AltchaAuthorizationCheck self-applies and no-ops, and any consumer that doesn't call AddSvrntyAltcha() sees zero behavior change. Generated CommandServiceImpl.g.cs verified to include the ICommandAuthorizationCheck loop after validation, before handler invocation, with the materialized command instance in ctx.Command. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
73 lines
2.6 KiB
C#
73 lines
2.6 KiB
C#
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
|
using Svrnty.CQRS;
|
|
using Svrnty.CQRS.Abstractions;
|
|
using Svrnty.CQRS.Altcha;
|
|
using Svrnty.CQRS.Altcha.Abstractions;
|
|
using Svrnty.CQRS.DynamicQuery;
|
|
using Svrnty.CQRS.FluentValidation;
|
|
using Svrnty.CQRS.Grpc;
|
|
using Svrnty.CQRS.MinimalApi;
|
|
using Svrnty.Sample;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Configure Kestrel to support both HTTP/1.1 (for REST APIs) and HTTP/2 (for gRPC)
|
|
builder.WebHost.ConfigureKestrel(options =>
|
|
{
|
|
// Port 6000: HTTP/2 for gRPC
|
|
options.ListenLocalhost(6000, o => o.Protocols = HttpProtocols.Http2);
|
|
// Port 6001: HTTP/1.1 for HTTP API
|
|
options.ListenLocalhost(6001, o => o.Protocols = HttpProtocols.Http1);
|
|
});
|
|
|
|
// IMPORTANT: Register dynamic query dependencies FIRST
|
|
// (before AddSvrntyCqrs, so gRPC services can find the handlers)
|
|
builder.Services.AddTransient<PoweredSoft.Data.Core.IAsyncQueryableService, SimpleAsyncQueryableService>();
|
|
builder.Services.AddTransient<PoweredSoft.DynamicQuery.Core.IQueryHandlerAsync, PoweredSoft.DynamicQuery.QueryHandlerAsync>();
|
|
builder.Services.AddDynamicQueryWithProvider<User, UserQueryableProvider>();
|
|
|
|
// Register commands and queries with validators
|
|
builder.Services.AddCommand<AddUserCommand, int, AddUserCommandHandler, AddUserCommandValidator>();
|
|
builder.Services.AddCommand<RemoveUserCommand, RemoveUserCommandHandler>();
|
|
builder.Services.AddCommand<ProtectedActionCommand, string, ProtectedActionCommandHandler>();
|
|
builder.Services.AddQuery<FetchUserQuery, User, FetchUserQueryHandler>();
|
|
|
|
// Wire the Altcha check + a stub verifier so [Altcha] commands run
|
|
// through the ICommandAuthorizationCheck pipeline.
|
|
builder.Services.AddSvrntyAltcha();
|
|
builder.Services.AddSingleton<IAltchaVerifier, StubAltchaVerifier>();
|
|
|
|
// Configure CQRS with fluent API
|
|
builder.Services.AddSvrntyCqrs(cqrs =>
|
|
{
|
|
// Enable gRPC endpoints with reflection
|
|
cqrs.AddGrpc(grpc =>
|
|
{
|
|
grpc.EnableReflection();
|
|
});
|
|
|
|
// Enable MinimalApi endpoints
|
|
cqrs.AddMinimalApi(configure =>
|
|
{
|
|
});
|
|
});
|
|
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
builder.Services.AddSwaggerGen();
|
|
|
|
var app = builder.Build();
|
|
|
|
// Map all configured CQRS endpoints (gRPC, MinimalApi, and Dynamic Queries)
|
|
app.UseSvrntyCqrs();
|
|
|
|
app.UseSwagger();
|
|
app.UseSwaggerUI();
|
|
|
|
|
|
Console.WriteLine("Auto-Generated gRPC Server with Reflection, Validation, MinimalApi and Swagger");
|
|
Console.WriteLine("gRPC (HTTP/2): http://localhost:6000");
|
|
Console.WriteLine("HTTP API (HTTP/1.1): http://localhost:6001/api/command/* and http://localhost:6001/api/query/*");
|
|
Console.WriteLine("Swagger UI: http://localhost:6001/swagger");
|
|
|
|
app.Run();
|