using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using QuestionsApi.Models; using QuestionsApi.Services; using System.Security.Claims; using System.Security.Cryptography; namespace QuestionsApi.Controllers; [ApiController] [Route("api/profile")] public sealed class ProfileController(UserProfileStore profiles, IConfiguration configuration) : ControllerBase { [HttpGet("me/preferences")] [Authorize(Roles = "USER,SUPER")] public async Task GetPreferences() { var profile = await profiles.GetAsync(UserId()); return Ok(profile ?? new UserStudyProfile { UserId = UserId() }); } [HttpPut("me/preferences")] [Authorize(Roles = "USER,SUPER")] public async Task UpdatePreferences(UpdateUserStudyProfileRequest request) { if (UserStudyProfileValidator.Validate(request) is { } error) return BadRequest(error); return Ok(await profiles.UpsertAsync(UserId(), request)); } [HttpPut("me/text-scale")] [Authorize(Roles = "USER,SUPER")] public async Task UpdateTextScale(UpdateTextScaleRequest request) { if (UpdateTextScaleRequestValidator.Validate(request) is { } error) return BadRequest(error); return Ok(await profiles.UpdateTextScaleAsync(UserId(), request)); } [HttpGet("me/export")] [Authorize(Roles = "USER,SUPER")] public async Task Export() => Ok(new { schemaVersion = 1, exportedUtc = DateTime.UtcNow, userId = UserId(), records = await profiles.ExportAsync(UserId()) }); [HttpDelete("internal/users/{userId}")] [AllowAnonymous] public async Task Purge(string userId) { var expected = configuration["InternalPurge:Secret"]; var supplied = Request.Headers["X-MedMind-Internal-Key"].FirstOrDefault(); if (string.IsNullOrWhiteSpace(expected) || !CryptographicOperations.FixedTimeEquals( System.Text.Encoding.UTF8.GetBytes(expected), System.Text.Encoding.UTF8.GetBytes(supplied ?? ""))) return Unauthorized(); await profiles.PurgeAsync(userId); return NoContent(); } private string UserId() => User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue(ClaimTypes.Name) ?? User.Identity?.Name ?? throw new UnauthorizedAccessException(); }