Add Email and ClientIpAddress audit fields to cvSearch.JobSearchSessions and JobSearchResults
Captures client IP at job-search link-click time and threads it through to the session. Both Email and ClientIpAddress are copied from session to each result row during processing. Closes #47 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -81,11 +81,11 @@ public sealed class JobSearchController : ControllerBase
|
||||
[SwaggerResponse(StatusCodes.Status500InternalServerError, "Session creation failed", typeof(ErrorResponse))]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
|
||||
public async Task<ActionResult<StartJobSearchResponse>> Start(string tokenId, CancellationToken ct)
|
||||
public async Task<ActionResult<StartJobSearchResponse>> Start(string tokenId, [FromBody] StartJobSearchRequest? request, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var status = await _tokenService.TriggerStartAsync(tokenId, ct);
|
||||
var status = await _tokenService.TriggerStartAsync(tokenId, request?.ClientIpAddress, ct);
|
||||
return Ok(new StartJobSearchResponse { Status = status });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -25,10 +25,11 @@ public interface IJobTokenService
|
||||
/// Validates the token and, if valid, marks it as used and creates a <c>Pending</c> job search session.
|
||||
/// </summary>
|
||||
/// <param name="tokenId">The token ID from the one-click link.</param>
|
||||
/// <param name="clientIpAddress">Client IP address forwarded by the api layer. Null when not available.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>
|
||||
/// One of the <c>StartJobSearchStatus</c> string constants:
|
||||
/// <c>Started</c>, <c>AlreadyUsed</c>, <c>Expired</c>, or <c>NotFound</c>.
|
||||
/// </returns>
|
||||
Task<string> TriggerStartAsync(string tokenId, CancellationToken ct);
|
||||
Task<string> TriggerStartAsync(string tokenId, string? clientIpAddress, CancellationToken ct);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public sealed class JobTokenService : IJobTokenService
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> TriggerStartAsync(string tokenId, CancellationToken ct)
|
||||
public async Task<string> TriggerStartAsync(string tokenId, string? clientIpAddress, CancellationToken ct)
|
||||
{
|
||||
var token = await _db.JobSearchTokens.FirstOrDefaultAsync(x => x.Id == tokenId, ct);
|
||||
if (token is null) return StartJobSearchStatus.NotFound;
|
||||
@@ -94,6 +94,7 @@ public sealed class JobTokenService : IJobTokenService
|
||||
Status = JobSearchStatus.Pending,
|
||||
Keywords = keywords,
|
||||
Location = token.Location,
|
||||
ClientIpAddress = clientIpAddress,
|
||||
ProviderConfigJson = providerConfigJson,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user