using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using SVSim.Database; using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.Account; namespace SVSim.EmulatedEntrypoint.Controllers; /// /// /account/* — viewer profile mutations that aren't tied to a specific subsystem. /// public class AccountController : SVSimController { /// /// Conservative server-side cap on viewer display names. The client's UserNameInput /// enforces its own limit at the keyboard; this is the backstop against direct API /// abuse (10-MB names ballooning every subsequent /load/index, etc.). Names are /// typically <=20 chars in prod traffic. /// private const int MaxDisplayNameLength = 24; private readonly SVSimDbContext _db; public AccountController(SVSimDbContext db) { _db = db; } [HttpPost("update_name")] public async Task UpdateName([FromBody] AccountUpdateNameRequest request) { if (!TryGetViewerId(out long viewerId)) return Unauthorized(); // Defensive null check: the DTO defaults to string.Empty but a JSON body with // an explicit `"name": null` deserialises through msgpack→JSON→STJ to null, and // assigning null to viewer.DisplayName (non-nullable in the entity) would NRE. if (string.IsNullOrWhiteSpace(request.Name)) return BadRequest(new { error = "name_empty" }); if (request.Name.Length > MaxDisplayNameLength) return BadRequest(new { error = "name_too_long" }); var viewer = await _db.Viewers.FirstAsync(v => v.Id == viewerId); viewer.DisplayName = request.Name; await _db.SaveChangesAsync(); // Prod returns `data: []` — empty array, not empty object. Use an empty array literal // so the translation middleware emits the right msgpack shape. return Ok(Array.Empty()); } }