feat(tool/signup): anonymous viewer creation keyed on UDID
POST /tool/signup upserts a Viewer keyed on the resolved request UDID (via the existing SID->UDID dict). Stashes the viewer on HttpContext so the translation middleware emits viewer_id/short_udid/udid in data_headers. Empty data payload -- all signup outputs flow in data_headers per spec. Idempotent: repeat signups for the same UDID return the existing viewer. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
51
SVSim.EmulatedEntrypoint/Controllers/ToolController.cs
Normal file
51
SVSim.EmulatedEntrypoint/Controllers/ToolController.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SVSim.Database.Repositories.Viewer;
|
||||
using SVSim.EmulatedEntrypoint.Extensions;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Responses;
|
||||
|
||||
namespace SVSim.EmulatedEntrypoint.Controllers;
|
||||
|
||||
public class ToolController : SVSimController
|
||||
{
|
||||
private readonly ILogger<ToolController> _logger;
|
||||
private readonly IViewerRepository _viewerRepository;
|
||||
|
||||
public ToolController(ILogger<ToolController> logger, IViewerRepository viewerRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
_viewerRepository = viewerRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>POST /tool/signup</c> — the client's first request on a fresh boot. Creates (or returns
|
||||
/// the existing) Viewer keyed on the request's UDID. The interesting outputs (viewer_id,
|
||||
/// short_udid, udid) all flow back via <c>data_headers</c>, populated by the translation
|
||||
/// middleware after this action returns — we just need to stash the viewer on HttpContext so
|
||||
/// the middleware picks it up the same way the auth handler does for logged-in endpoints.
|
||||
///
|
||||
/// Spec: <c>docs/api-spec/endpoints/pre-login/tool-signup.md</c>.
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("signup")]
|
||||
public async Task<SignupResponse> Signup([FromBody] SignupRequest request)
|
||||
{
|
||||
Guid? maybeUdid = HttpContext.GetUdid();
|
||||
if (maybeUdid is not Guid udid || udid == Guid.Empty)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Cannot register viewer: request has no resolvable UDID (missing UDID/SID headers, or " +
|
||||
"SessionidMappingMiddleware couldn't decode the UDID header).");
|
||||
}
|
||||
|
||||
var viewer = await _viewerRepository.GetViewerByUdid(udid)
|
||||
?? await _viewerRepository.RegisterAnonymousViewer(udid);
|
||||
|
||||
HttpContext.SetViewer(viewer);
|
||||
_logger.LogInformation("Signup resolved for udid={Udid} → viewer_id={ViewerId}, short_udid={ShortUdid}.",
|
||||
udid, viewer.Id, viewer.ShortUdid);
|
||||
|
||||
return new SignupResponse();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user