fix(cv): bind letter proposals to current sources and preserve document choices

This commit is contained in:
cesnimda
2026-10-26 03:44:37 +01:00
parent 8948c988e2
commit 8979244576
21 changed files with 816 additions and 32 deletions
@@ -50,6 +50,11 @@ public sealed class AiWorkspaceController : ControllerBase
{
var user = await _users.GetUserAsync(User);
if (user is null) return Unauthorized();
if (_db is not null && string.Equals(request?.Module?.Trim(), "cover-letter", StringComparison.OrdinalIgnoreCase))
{
user = await _db.Users.AsNoTracking().FirstOrDefaultAsync(item => item.Id == user.Id, ct);
if (user is null) return Unauthorized();
}
if (string.IsNullOrWhiteSpace(request?.Module)) return BadRequest("Choose an AI module.");
AiUsageReservation? reservation = null;
@@ -93,6 +98,13 @@ public sealed class AiWorkspaceController : ControllerBase
reservation.Record.Id, interaction.InputCharacterCount, interaction.OutputCharacterCount, ct);
return Ok(ToDto(interaction));
}
catch (CoverLetterSourceChangedException ex)
{
if (reservation is not null)
await (_usage ?? new AiUsageMeter(_db!, TimeProvider.System)).FinalizeAsync(
reservation.Record.Id, ex.InputCharacterCount, ex.OutputCharacterCount, ct);
return Conflict(new { code = "cover_letter_source_changed" });
}
catch (ArgumentException ex)
{
if (reservation is not null) await (_usage ?? new AiUsageMeter(_db!, TimeProvider.System)).ReleaseAsync(reservation.Record.Id, ct);
@@ -113,6 +125,17 @@ public sealed class AiWorkspaceController : ControllerBase
return Ok(history.Select(ToDto));
}
[HttpPost("history/{id:int}/validate-source")]
[Authorize(Policy = ProEntitlement.Policy)]
public async Task<IActionResult> ValidateSource(int jobId, int id, CancellationToken ct)
{
var user = await _users.GetUserAsync(User);
if (user is null) return Unauthorized();
var current = await _workspace.ValidateCoverLetterSourceAsync(user.Id, jobId, id, ct);
return current is null ? NotFound() : current.Value ? NoContent()
: Conflict(new { code = "cover_letter_source_changed" });
}
[HttpDelete("history/{id:int}")]
public async Task<IActionResult> Delete(int jobId, int id, CancellationToken ct)
{