diff --git a/Apis/page-fetcher-api-models/FetchPageRequest.cs b/Apis/page-fetcher-api-models/FetchPageRequest.cs
index 7a24faa..80f4e73 100644
--- a/Apis/page-fetcher-api-models/FetchPageRequest.cs
+++ b/Apis/page-fetcher-api-models/FetchPageRequest.cs
@@ -17,4 +17,10 @@ public sealed class FetchPageRequest
/// Identifies the calling service for audit purposes (e.g. cv-matcher-api, cv-search-job).
///
public string CallerService { get; set; } = string.Empty;
+
+ ///
+ /// Optional reference to the job search session that triggered this fetch.
+ /// Stored on pageFetcher.PageFetches for cross-schema audit queries.
+ ///
+ public string? JobSearchSessionId { get; set; }
}
diff --git a/Apis/page-fetcher-api/Services/PageFetcherService.cs b/Apis/page-fetcher-api/Services/PageFetcherService.cs
index bc7772e..df61770 100644
--- a/Apis/page-fetcher-api/Services/PageFetcherService.cs
+++ b/Apis/page-fetcher-api/Services/PageFetcherService.cs
@@ -101,6 +101,7 @@ public sealed class PageFetcherService
Id = Guid.NewGuid().ToString("N"),
Url = request.Url,
CallerService = request.CallerService ?? string.Empty,
+ JobSearchSessionId = request.JobSearchSessionId,
HttpStatusCode = statusCode,
Html = html,
Text = text,
diff --git a/Apis/page-fetcher-data/Data/Entities/PageFetchEntity.cs b/Apis/page-fetcher-data/Data/Entities/PageFetchEntity.cs
index 96ef3ef..16c08d1 100644
--- a/Apis/page-fetcher-data/Data/Entities/PageFetchEntity.cs
+++ b/Apis/page-fetcher-data/Data/Entities/PageFetchEntity.cs
@@ -31,4 +31,10 @@ public sealed class PageFetchEntity : BaseEntity
/// Exception message when is false.
public string? ErrorMessage { get; set; }
+
+ ///
+ /// Optional reference to the cvSearch.JobSearchSessions row that triggered this fetch.
+ /// Null for fetches not originating from a job search session (e.g. direct CV-to-job matches).
+ ///
+ public string? JobSearchSessionId { get; set; }
}
diff --git a/Apis/page-fetcher-data/Migrations/20260608165542_AddJobSearchSessionId.Designer.cs b/Apis/page-fetcher-data/Migrations/20260608165542_AddJobSearchSessionId.Designer.cs
new file mode 100644
index 0000000..a39a3db
--- /dev/null
+++ b/Apis/page-fetcher-data/Migrations/20260608165542_AddJobSearchSessionId.Designer.cs
@@ -0,0 +1,88 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using PageFetcher.Data;
+
+#nullable disable
+
+namespace PageFetcher.Data.Migrations
+{
+ [DbContext(typeof(PageFetchDbContext))]
+ [Migration("20260608165542_AddJobSearchSessionId")]
+ partial class AddJobSearchSessionId
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("pageFetcher")
+ .HasAnnotation("ProductVersion", "10.0.7")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("PageFetcher.Data.Entities.PageFetchEntity", b =>
+ {
+ b.Property("Id")
+ .HasMaxLength(64)
+ .HasColumnType("nvarchar(64)");
+
+ b.Property("CallerService")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("nvarchar(64)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("datetime2")
+ .HasDefaultValueSql("SYSUTCDATETIME()");
+
+ b.Property("DurationMs")
+ .HasColumnType("bigint");
+
+ b.Property("ErrorMessage")
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.Property("Html")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("HttpStatusCode")
+ .HasColumnType("int");
+
+ b.Property("JobSearchSessionId")
+ .HasMaxLength(64)
+ .HasColumnType("nvarchar(64)");
+
+ b.Property("Success")
+ .HasColumnType("bit");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasMaxLength(2000)
+ .HasColumnType("nvarchar(2000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("JobSearchSessionId");
+
+ b.HasIndex("Url");
+
+ b.ToTable("PageFetches", "pageFetcher");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Apis/page-fetcher-data/Migrations/20260608165542_AddJobSearchSessionId.cs b/Apis/page-fetcher-data/Migrations/20260608165542_AddJobSearchSessionId.cs
new file mode 100644
index 0000000..aa1ea36
--- /dev/null
+++ b/Apis/page-fetcher-data/Migrations/20260608165542_AddJobSearchSessionId.cs
@@ -0,0 +1,43 @@
+using PageFetcher.Data;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace PageFetcher.Data.Migrations
+{
+ ///
+ public partial class AddJobSearchSessionId : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "JobSearchSessionId",
+ schema: MigrationConstants.SchemaName,
+ table: "PageFetches",
+ type: "nvarchar(64)",
+ maxLength: 64,
+ nullable: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_PageFetches_JobSearchSessionId",
+ schema: MigrationConstants.SchemaName,
+ table: "PageFetches",
+ column: "JobSearchSessionId");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropIndex(
+ name: "IX_PageFetches_JobSearchSessionId",
+ schema: MigrationConstants.SchemaName,
+ table: "PageFetches");
+
+ migrationBuilder.DropColumn(
+ name: "JobSearchSessionId",
+ schema: MigrationConstants.SchemaName,
+ table: "PageFetches");
+ }
+ }
+}
diff --git a/Apis/page-fetcher-data/Migrations/PageFetchDbContextModelSnapshot.cs b/Apis/page-fetcher-data/Migrations/PageFetchDbContextModelSnapshot.cs
index dd72094..af3a679 100644
--- a/Apis/page-fetcher-data/Migrations/PageFetchDbContextModelSnapshot.cs
+++ b/Apis/page-fetcher-data/Migrations/PageFetchDbContextModelSnapshot.cs
@@ -53,6 +53,10 @@ namespace PageFetcher.Data.Migrations
b.Property("HttpStatusCode")
.HasColumnType("int");
+ b.Property("JobSearchSessionId")
+ .HasMaxLength(64)
+ .HasColumnType("nvarchar(64)");
+
b.Property("Success")
.HasColumnType("bit");
@@ -69,6 +73,8 @@ namespace PageFetcher.Data.Migrations
b.HasIndex("CreatedAt");
+ b.HasIndex("JobSearchSessionId");
+
b.HasIndex("Url");
b.ToTable("PageFetches", "pageFetcher");
diff --git a/Apis/page-fetcher-data/PageFetchDbContext.cs b/Apis/page-fetcher-data/PageFetchDbContext.cs
index 5f9538f..fbd9e22 100644
--- a/Apis/page-fetcher-data/PageFetchDbContext.cs
+++ b/Apis/page-fetcher-data/PageFetchDbContext.cs
@@ -36,8 +36,11 @@ public sealed class PageFetchDbContext : DbContext
entity.Property(x => x.Html).IsRequired();
entity.Property(x => x.Text).IsRequired();
entity.Property(x => x.ErrorMessage).HasMaxLength(2000);
+ entity.Property(x => x.JobSearchSessionId).HasMaxLength(64);
entity.Property(x => x.CreatedAt).HasDefaultValueSql("SYSUTCDATETIME()");
+ entity.HasIndex(x => x.JobSearchSessionId);
+
entity.HasIndex(x => x.Url);
entity.HasIndex(x => x.CreatedAt);
});
diff --git a/Jobs/cv-search-job/Tasks/CvSearchJobTask.cs b/Jobs/cv-search-job/Tasks/CvSearchJobTask.cs
index 2fc034e..43337d5 100644
--- a/Jobs/cv-search-job/Tasks/CvSearchJobTask.cs
+++ b/Jobs/cv-search-job/Tasks/CvSearchJobTask.cs
@@ -169,7 +169,8 @@ public sealed class CvSearchJobTask : IJobTask
{
Url = url,
WaitFor = "domcontentloaded",
- CallerService = "cv-search-job"
+ CallerService = "cv-search-job",
+ JobSearchSessionId = session.Id
}, ct);
if (!fetchResponse.Success || string.IsNullOrWhiteSpace(fetchResponse.Text))