Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
- ASP.NET Core API POST /api/v1/findings with EF Core (Npgsql, snake_case) and a generated initial migration - The hub is the authority on licenses: verifies the signature itself, rejects invalid (401), community/expired past grace (403), revoked (403, re-checked under the lock) and over the instance limit (403) - Full-state reconciliation per instance: new findings inserted, existing updated, missing ones resolved, rule states upserted; idempotent by submission id; late older submissions are recorded but not applied - Serialized per customer with a PostgreSQL advisory lock (found by a concurrency test: parallel first submissions raced on the license row) - Size and schema validation; tests on real PostgreSQL, including the full path agent → queue → hub → PostgreSQL with evidence redaction Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
485 lines
18 KiB
C#
485 lines
18 KiB
C#
// <auto-generated />
|
|
using System;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
using SQLInsight.Hub.Data;
|
|
|
|
#nullable disable
|
|
|
|
namespace SQLInsight.Hub.Data.Migrations
|
|
{
|
|
[DbContext(typeof(HubDbContext))]
|
|
partial class HubDbContextModelSnapshot : ModelSnapshot
|
|
{
|
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
{
|
|
#pragma warning disable 612, 618
|
|
modelBuilder
|
|
.HasAnnotation("ProductVersion", "10.0.12")
|
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
|
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.Customer", b =>
|
|
{
|
|
b.Property<int>("Id")
|
|
.ValueGeneratedOnAdd()
|
|
.HasColumnType("integer")
|
|
.HasColumnName("id");
|
|
|
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
|
|
b.Property<DateTime>("CreatedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("created_at");
|
|
|
|
b.Property<string>("Name")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("name");
|
|
|
|
b.HasKey("Id")
|
|
.HasName("pk_customers");
|
|
|
|
b.HasIndex("Name")
|
|
.IsUnique()
|
|
.HasDatabaseName("ix_customers_name");
|
|
|
|
b.ToTable("customers", (string)null);
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.HubFinding", b =>
|
|
{
|
|
b.Property<long>("Id")
|
|
.ValueGeneratedOnAdd()
|
|
.HasColumnType("bigint")
|
|
.HasColumnName("id");
|
|
|
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
|
|
b.Property<string>("EvidenceJson")
|
|
.IsRequired()
|
|
.HasColumnType("jsonb")
|
|
.HasColumnName("evidence_json");
|
|
|
|
b.Property<string>("Explanation")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("explanation");
|
|
|
|
b.Property<DateTime>("FirstSeenAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("first_seen_at");
|
|
|
|
b.Property<int>("InstanceId")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("instance_id");
|
|
|
|
b.Property<DateTime>("LastSeenAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("last_seen_at");
|
|
|
|
b.Property<string>("Recommendation")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("recommendation");
|
|
|
|
b.Property<DateTime?>("ResolvedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("resolved_at");
|
|
|
|
b.Property<string>("RuleId")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("rule_id");
|
|
|
|
b.Property<int>("Severity")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("severity");
|
|
|
|
b.Property<string>("Subject")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("subject");
|
|
|
|
b.Property<int?>("SupportCaseId")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("support_case_id");
|
|
|
|
b.Property<string>("Title")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("title");
|
|
|
|
b.HasKey("Id")
|
|
.HasName("pk_findings");
|
|
|
|
b.HasIndex("SupportCaseId")
|
|
.HasDatabaseName("ix_findings_support_case_id");
|
|
|
|
b.HasIndex("Severity", "ResolvedAt")
|
|
.HasDatabaseName("ix_findings_severity_resolved_at");
|
|
|
|
b.HasIndex("InstanceId", "RuleId", "Subject")
|
|
.IsUnique()
|
|
.HasDatabaseName("ix_findings_instance_id_rule_id_subject")
|
|
.HasFilter("resolved_at IS NULL");
|
|
|
|
b.ToTable("findings", (string)null);
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.Instance", b =>
|
|
{
|
|
b.Property<int>("Id")
|
|
.ValueGeneratedOnAdd()
|
|
.HasColumnType("integer")
|
|
.HasColumnName("id");
|
|
|
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
|
|
b.Property<string>("AgentVersion")
|
|
.HasColumnType("text")
|
|
.HasColumnName("agent_version");
|
|
|
|
b.Property<int>("CustomerId")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("customer_id");
|
|
|
|
b.Property<string>("Edition")
|
|
.HasColumnType("text")
|
|
.HasColumnName("edition");
|
|
|
|
b.Property<string>("InstanceKey")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("instance_key");
|
|
|
|
b.Property<DateTime?>("LastCollectedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("last_collected_at");
|
|
|
|
b.Property<DateTime?>("LastSubmissionAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("last_submission_at");
|
|
|
|
b.Property<string>("ProductLevel")
|
|
.HasColumnType("text")
|
|
.HasColumnName("product_level");
|
|
|
|
b.Property<string>("ProductUpdateLevel")
|
|
.HasColumnType("text")
|
|
.HasColumnName("product_update_level");
|
|
|
|
b.Property<string>("ProductVersion")
|
|
.HasColumnType("text")
|
|
.HasColumnName("product_version");
|
|
|
|
b.Property<string>("Release")
|
|
.HasColumnType("text")
|
|
.HasColumnName("release");
|
|
|
|
b.Property<string>("ServerName")
|
|
.HasColumnType("text")
|
|
.HasColumnName("server_name");
|
|
|
|
b.HasKey("Id")
|
|
.HasName("pk_instances");
|
|
|
|
b.HasIndex("CustomerId", "InstanceKey")
|
|
.IsUnique()
|
|
.HasDatabaseName("ix_instances_customer_id_instance_key");
|
|
|
|
b.ToTable("instances", (string)null);
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.LicenseRecord", b =>
|
|
{
|
|
b.Property<string>("LicenseId")
|
|
.HasColumnType("text")
|
|
.HasColumnName("license_id");
|
|
|
|
b.Property<int>("CustomerId")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("customer_id");
|
|
|
|
b.Property<DateTime>("ExpiresAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("expires_at");
|
|
|
|
b.Property<DateTime>("FirstSeenAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("first_seen_at");
|
|
|
|
b.Property<DateTime>("IssuedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("issued_at");
|
|
|
|
b.Property<DateTime>("LastSeenAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("last_seen_at");
|
|
|
|
b.Property<int>("MaxInstances")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("max_instances");
|
|
|
|
b.Property<string>("Plan")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("plan");
|
|
|
|
b.Property<DateTime?>("RevokedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("revoked_at");
|
|
|
|
b.Property<string>("RevokedReason")
|
|
.HasColumnType("text")
|
|
.HasColumnName("revoked_reason");
|
|
|
|
b.HasKey("LicenseId")
|
|
.HasName("pk_licenses");
|
|
|
|
b.HasIndex("CustomerId")
|
|
.HasDatabaseName("ix_licenses_customer_id");
|
|
|
|
b.ToTable("licenses", (string)null);
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.RuleState", b =>
|
|
{
|
|
b.Property<int>("InstanceId")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("instance_id");
|
|
|
|
b.Property<string>("RuleId")
|
|
.HasColumnType("text")
|
|
.HasColumnName("rule_id");
|
|
|
|
b.Property<string>("Outcome")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("outcome");
|
|
|
|
b.Property<string>("Reason")
|
|
.HasColumnType("text")
|
|
.HasColumnName("reason");
|
|
|
|
b.Property<DateTime>("UpdatedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("updated_at");
|
|
|
|
b.HasKey("InstanceId", "RuleId")
|
|
.HasName("pk_rule_states");
|
|
|
|
b.ToTable("rule_states", (string)null);
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.StaffUser", b =>
|
|
{
|
|
b.Property<int>("Id")
|
|
.ValueGeneratedOnAdd()
|
|
.HasColumnType("integer")
|
|
.HasColumnName("id");
|
|
|
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
|
|
b.Property<DateTime>("CreatedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("created_at");
|
|
|
|
b.Property<string>("DisplayName")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("display_name");
|
|
|
|
b.Property<bool>("IsActive")
|
|
.HasColumnType("boolean")
|
|
.HasColumnName("is_active");
|
|
|
|
b.Property<DateTime?>("LastLoginAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("last_login_at");
|
|
|
|
b.Property<string>("PasswordHash")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("password_hash");
|
|
|
|
b.Property<string>("UserName")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("user_name");
|
|
|
|
b.HasKey("Id")
|
|
.HasName("pk_staff_users");
|
|
|
|
b.HasIndex("UserName")
|
|
.IsUnique()
|
|
.HasDatabaseName("ix_staff_users_user_name");
|
|
|
|
b.ToTable("staff_users", (string)null);
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.Submission", b =>
|
|
{
|
|
b.Property<Guid>("SubmissionId")
|
|
.ValueGeneratedOnAdd()
|
|
.HasColumnType("uuid")
|
|
.HasColumnName("submission_id");
|
|
|
|
b.Property<bool>("Applied")
|
|
.HasColumnType("boolean")
|
|
.HasColumnName("applied");
|
|
|
|
b.Property<DateTime>("CollectedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("collected_at");
|
|
|
|
b.Property<int>("FindingCount")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("finding_count");
|
|
|
|
b.Property<int>("InstanceId")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("instance_id");
|
|
|
|
b.Property<DateTime>("ReceivedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("received_at");
|
|
|
|
b.HasKey("SubmissionId")
|
|
.HasName("pk_submissions");
|
|
|
|
b.HasIndex("ReceivedAt")
|
|
.HasDatabaseName("ix_submissions_received_at");
|
|
|
|
b.ToTable("submissions", (string)null);
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.SupportCase", b =>
|
|
{
|
|
b.Property<int>("Id")
|
|
.ValueGeneratedOnAdd()
|
|
.HasColumnType("integer")
|
|
.HasColumnName("id");
|
|
|
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
|
|
b.Property<DateTime?>("ClosedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("closed_at");
|
|
|
|
b.Property<DateTime>("CreatedAt")
|
|
.HasColumnType("timestamp with time zone")
|
|
.HasColumnName("created_at");
|
|
|
|
b.Property<int>("CustomerId")
|
|
.HasColumnType("integer")
|
|
.HasColumnName("customer_id");
|
|
|
|
b.Property<bool>("IsOpen")
|
|
.HasColumnType("boolean")
|
|
.HasColumnName("is_open");
|
|
|
|
b.Property<string>("Note")
|
|
.HasColumnType("text")
|
|
.HasColumnName("note");
|
|
|
|
b.Property<string>("Reference")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("reference");
|
|
|
|
b.Property<string>("Title")
|
|
.IsRequired()
|
|
.HasColumnType("text")
|
|
.HasColumnName("title");
|
|
|
|
b.HasKey("Id")
|
|
.HasName("pk_support_cases");
|
|
|
|
b.HasIndex("CustomerId")
|
|
.HasDatabaseName("ix_support_cases_customer_id");
|
|
|
|
b.ToTable("support_cases", (string)null);
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.HubFinding", b =>
|
|
{
|
|
b.HasOne("SQLInsight.Hub.Data.Instance", "Instance")
|
|
.WithMany("Findings")
|
|
.HasForeignKey("InstanceId")
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
.IsRequired()
|
|
.HasConstraintName("fk_findings_instances_instance_id");
|
|
|
|
b.HasOne("SQLInsight.Hub.Data.SupportCase", "SupportCase")
|
|
.WithMany("Findings")
|
|
.HasForeignKey("SupportCaseId")
|
|
.OnDelete(DeleteBehavior.SetNull)
|
|
.HasConstraintName("fk_findings_support_cases_support_case_id");
|
|
|
|
b.Navigation("Instance");
|
|
|
|
b.Navigation("SupportCase");
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.Instance", b =>
|
|
{
|
|
b.HasOne("SQLInsight.Hub.Data.Customer", "Customer")
|
|
.WithMany("Instances")
|
|
.HasForeignKey("CustomerId")
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
.IsRequired()
|
|
.HasConstraintName("fk_instances_customers_customer_id");
|
|
|
|
b.Navigation("Customer");
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.LicenseRecord", b =>
|
|
{
|
|
b.HasOne("SQLInsight.Hub.Data.Customer", "Customer")
|
|
.WithMany("Licenses")
|
|
.HasForeignKey("CustomerId")
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
.IsRequired()
|
|
.HasConstraintName("fk_licenses_customers_customer_id");
|
|
|
|
b.Navigation("Customer");
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.SupportCase", b =>
|
|
{
|
|
b.HasOne("SQLInsight.Hub.Data.Customer", "Customer")
|
|
.WithMany("SupportCases")
|
|
.HasForeignKey("CustomerId")
|
|
.OnDelete(DeleteBehavior.Cascade)
|
|
.IsRequired()
|
|
.HasConstraintName("fk_support_cases_customers_customer_id");
|
|
|
|
b.Navigation("Customer");
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.Customer", b =>
|
|
{
|
|
b.Navigation("Instances");
|
|
|
|
b.Navigation("Licenses");
|
|
|
|
b.Navigation("SupportCases");
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.Instance", b =>
|
|
{
|
|
b.Navigation("Findings");
|
|
});
|
|
|
|
modelBuilder.Entity("SQLInsight.Hub.Data.SupportCase", b =>
|
|
{
|
|
b.Navigation("Findings");
|
|
});
|
|
#pragma warning restore 612, 618
|
|
}
|
|
}
|
|
}
|