ca0c05db10
Build backend images / build content-svc (push) Failing after 1m0s
Build backend images / build file-svc (push) Failing after 47s
Build backend images / build gateway (push) Failing after 57s
Build backend images / build identity-svc (push) Failing after 1m2s
Build backend images / build notification-svc (push) Failing after 1m0s
Build backend images / build render-svc (push) Failing after 1m1s
Build backend images / build studio-svc (push) Failing after 58s
The nested positional record ChargeReq(Guid UserId) failed System.Text.Json binding under the snake_case policy (400). Use a plain class with a settable property. Verified: consume decrements + blocks at 0, refund restores, bad service token → 401. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
44 lines
1.6 KiB
C#
44 lines
1.6 KiB
C#
using FlatRender.IdentitySvc.Application.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace FlatRender.IdentitySvc.Controllers;
|
|
|
|
/// <summary>Service-to-service endpoints (render-svc → identity). Auth via shared service token,
|
|
/// reachable only on the internal network (the gateway does not route /v1/internal).</summary>
|
|
[ApiController]
|
|
[AllowAnonymous]
|
|
[Route("v1/internal")]
|
|
public class InternalController(AdminService svc, IConfiguration config) : ControllerBase
|
|
{
|
|
public class ChargeReq { public Guid UserId { get; set; } }
|
|
|
|
private bool ServiceTokenValid()
|
|
{
|
|
var expected = config["ServiceToken"] ?? Environment.GetEnvironmentVariable("SERVICE_TOKEN") ?? "internal-service-secret";
|
|
var got = Request.Headers["X-Service-Token"].ToString();
|
|
if (string.IsNullOrEmpty(got))
|
|
{
|
|
var auth = Request.Headers["Authorization"].ToString();
|
|
if (auth.StartsWith("Bearer ", StringComparison.Ordinal)) got = auth[7..];
|
|
}
|
|
return !string.IsNullOrEmpty(got) && got == expected;
|
|
}
|
|
|
|
[HttpPost("render-charge/consume")]
|
|
public async Task<IActionResult> Consume([FromBody] ChargeReq req)
|
|
{
|
|
if (!ServiceTokenValid()) return Unauthorized();
|
|
var (allowed, remaining) = await svc.ConsumeRenderChargeAsync(req.UserId);
|
|
return Ok(new { allowed, remaining });
|
|
}
|
|
|
|
[HttpPost("render-charge/refund")]
|
|
public async Task<IActionResult> Refund([FromBody] ChargeReq req)
|
|
{
|
|
if (!ServiceTokenValid()) return Unauthorized();
|
|
await svc.RefundRenderChargeAsync(req.UserId);
|
|
return Ok(new { ok = true });
|
|
}
|
|
}
|