chore: remove orphaned project directories left over from renames
Deleted stale directories and stray .csproj files that were never added to the solution after project renames: - Apis/cv-search-models/ (renamed → cv-search-data) - Apis/myai-models/ (renamed → myai-data) - Apis/shared-models/ (empty leftover) - Apis/cv-search-data/cv-search-models.csproj (stray old csproj) - Apis/myai-data/myai-models.csproj (stray old csproj) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<RootNamespace>CvSearch.Models</RootNamespace>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.7" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
-160
@@ -1,160 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using CvSearch.Models.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace CvSearch.Models.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(CvSearchDbContext))]
|
|
||||||
[Migration("20260522093356_AddJobSearchTables")]
|
|
||||||
partial class AddJobSearchTables
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("cvSearch")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
|
||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("CvSearch.Models.Data.Entities.JobSearchResultEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("JobText")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<string>("JobTitle")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(512)
|
|
||||||
.HasColumnType("nvarchar(512)");
|
|
||||||
|
|
||||||
b.Property<string>("JobUrl")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("nvarchar(2048)");
|
|
||||||
|
|
||||||
b.Property<string>("ProviderName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("ResultJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<int>("Score")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("SessionId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("SessionId");
|
|
||||||
|
|
||||||
b.ToTable("JobSearchResults", "cvSearch");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CvSearch.Models.Data.Entities.JobSearchSessionEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("CvDocumentId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Keywords")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("nvarchar(1000)");
|
|
||||||
|
|
||||||
b.Property<string>("ProviderConfigJson")
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<string>("Status")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("nvarchar(32)");
|
|
||||||
|
|
||||||
b.Property<string>("TokenId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Status");
|
|
||||||
|
|
||||||
b.ToTable("JobSearchSessions", "cvSearch");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CvSearch.Models.Data.Entities.JobSearchTokenEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("CvDocumentId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<bool>("Used")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bit")
|
|
||||||
.HasDefaultValue(false);
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("JobSearchTokens", "cvSearch");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace CvSearch.Models.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddJobSearchTables : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.EnsureSchema(
|
|
||||||
name: "cvSearch");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "JobSearchResults",
|
|
||||||
schema: "cvSearch",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
SessionId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
ProviderName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
|
||||||
JobUrl = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: false),
|
|
||||||
JobTitle = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
|
||||||
JobText = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
|
||||||
Score = table.Column<int>(type: "int", nullable: false),
|
|
||||||
ResultJson = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_JobSearchResults", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "JobSearchSessions",
|
|
||||||
schema: "cvSearch",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
TokenId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
CvDocumentId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
|
||||||
Status = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
|
|
||||||
Keywords = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
|
|
||||||
ProviderConfigJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_JobSearchSessions", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "JobSearchTokens",
|
|
||||||
schema: "cvSearch",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
CvDocumentId = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
|
||||||
Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
|
||||||
ExpiresAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
|
||||||
Used = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_JobSearchTokens", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_JobSearchResults_SessionId",
|
|
||||||
schema: "cvSearch",
|
|
||||||
table: "JobSearchResults",
|
|
||||||
column: "SessionId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_JobSearchSessions_Status",
|
|
||||||
schema: "cvSearch",
|
|
||||||
table: "JobSearchSessions",
|
|
||||||
column: "Status");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "JobSearchResults",
|
|
||||||
schema: "cvSearch");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "JobSearchSessions",
|
|
||||||
schema: "cvSearch");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "JobSearchTokens",
|
|
||||||
schema: "cvSearch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Generated
-174
@@ -1,174 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using CvSearch.Models.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace CvSearch.Models.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(CvSearchDbContext))]
|
|
||||||
[Migration("20260524145702_AddLanguageToJobSearchEntities")]
|
|
||||||
partial class AddLanguageToJobSearchEntities
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("cvSearch")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
|
||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("CvSearch.Models.Data.Entities.JobSearchResultEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("JobText")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<string>("JobTitle")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(512)
|
|
||||||
.HasColumnType("nvarchar(512)");
|
|
||||||
|
|
||||||
b.Property<string>("JobUrl")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("nvarchar(2048)");
|
|
||||||
|
|
||||||
b.Property<string>("ProviderName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("ResultJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<int>("Score")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("SessionId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("SessionId");
|
|
||||||
|
|
||||||
b.ToTable("JobSearchResults", "cvSearch");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CvSearch.Models.Data.Entities.JobSearchSessionEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("CvDocumentId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Keywords")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("nvarchar(1000)");
|
|
||||||
|
|
||||||
b.Property<string>("Language")
|
|
||||||
.IsRequired()
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasMaxLength(8)
|
|
||||||
.HasColumnType("nvarchar(8)")
|
|
||||||
.HasDefaultValue("en");
|
|
||||||
|
|
||||||
b.Property<string>("ProviderConfigJson")
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<string>("Status")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("nvarchar(32)");
|
|
||||||
|
|
||||||
b.Property<string>("TokenId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Status");
|
|
||||||
|
|
||||||
b.ToTable("JobSearchSessions", "cvSearch");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CvSearch.Models.Data.Entities.JobSearchTokenEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("CvDocumentId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("Language")
|
|
||||||
.IsRequired()
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasMaxLength(8)
|
|
||||||
.HasColumnType("nvarchar(8)")
|
|
||||||
.HasDefaultValue("en");
|
|
||||||
|
|
||||||
b.Property<bool>("Used")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bit")
|
|
||||||
.HasDefaultValue(false);
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("JobSearchTokens", "cvSearch");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace CvSearch.Models.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddLanguageToJobSearchEntities : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "Language",
|
|
||||||
schema: "cvSearch",
|
|
||||||
table: "JobSearchTokens",
|
|
||||||
type: "nvarchar(8)",
|
|
||||||
maxLength: 8,
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "en");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "Language",
|
|
||||||
schema: "cvSearch",
|
|
||||||
table: "JobSearchSessions",
|
|
||||||
type: "nvarchar(8)",
|
|
||||||
maxLength: 8,
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "en");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "Language",
|
|
||||||
schema: "cvSearch",
|
|
||||||
table: "JobSearchTokens");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "Language",
|
|
||||||
schema: "cvSearch",
|
|
||||||
table: "JobSearchSessions");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using CvSearch.Models.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace CvSearch.Models.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(CvSearchDbContext))]
|
|
||||||
partial class CvSearchDbContextModelSnapshot : ModelSnapshot
|
|
||||||
{
|
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("cvSearch")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
|
||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("CvSearch.Models.Data.Entities.JobSearchResultEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("JobText")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<string>("JobTitle")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(512)
|
|
||||||
.HasColumnType("nvarchar(512)");
|
|
||||||
|
|
||||||
b.Property<string>("JobUrl")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("nvarchar(2048)");
|
|
||||||
|
|
||||||
b.Property<string>("ProviderName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("ResultJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<int>("Score")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("SessionId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("SessionId");
|
|
||||||
|
|
||||||
b.ToTable("JobSearchResults", "cvSearch");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CvSearch.Models.Data.Entities.JobSearchSessionEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("CvDocumentId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Keywords")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("nvarchar(1000)");
|
|
||||||
|
|
||||||
b.Property<string>("Language")
|
|
||||||
.IsRequired()
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasMaxLength(8)
|
|
||||||
.HasColumnType("nvarchar(8)")
|
|
||||||
.HasDefaultValue("en");
|
|
||||||
|
|
||||||
b.Property<string>("ProviderConfigJson")
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.Property<string>("Status")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("nvarchar(32)");
|
|
||||||
|
|
||||||
b.Property<string>("TokenId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Status");
|
|
||||||
|
|
||||||
b.ToTable("JobSearchSessions", "cvSearch");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("CvSearch.Models.Data.Entities.JobSearchTokenEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("CvDocumentId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("datetime2");
|
|
||||||
|
|
||||||
b.Property<string>("Language")
|
|
||||||
.IsRequired()
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasMaxLength(8)
|
|
||||||
.HasColumnType("nvarchar(8)")
|
|
||||||
.HasDefaultValue("en");
|
|
||||||
|
|
||||||
b.Property<bool>("Used")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bit")
|
|
||||||
.HasDefaultValue(false);
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("JobSearchTokens", "cvSearch");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
namespace CvSearch.Models.Settings;
|
|
||||||
|
|
||||||
public sealed class JobSearchSettings
|
|
||||||
{
|
|
||||||
public bool Enabled { get; set; } = true;
|
|
||||||
public string JobSearchLinkBaseUrl { get; set; } = string.Empty;
|
|
||||||
public int TokenExpiryDays { get; set; } = 7;
|
|
||||||
public int MinMatchScore { get; set; } = 15;
|
|
||||||
public int MaxJobsToMatch { get; set; } = 15;
|
|
||||||
public List<JobProviderConfig> Providers { get; set; } = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class JobProviderConfig
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public bool Enabled { get; set; } = true;
|
|
||||||
public string SearchUrlTemplate { get; set; } = string.Empty;
|
|
||||||
public string JobLinkContains { get; set; } = string.Empty;
|
|
||||||
public List<string> InitialKeywords { get; set; } = [];
|
|
||||||
public int MaxResults { get; set; } = 20;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<RootNamespace>CvSearch.Models</RootNamespace>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.7" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<RootNamespace>MyAi.Models</RootNamespace>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.7" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using MyAi.Models.Data;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace MyAi.Models.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(MyAiDbContext))]
|
|
||||||
[Migration("20260524145351_AddTemplates")]
|
|
||||||
partial class AddTemplates
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("myAi")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
|
||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("MyAi.Models.Data.Entities.TemplateEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("Language")
|
|
||||||
.HasMaxLength(8)
|
|
||||||
.HasColumnType("nvarchar(8)");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("nvarchar(500)")
|
|
||||||
.HasDefaultValue("");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.HasKey("Key", "Language");
|
|
||||||
|
|
||||||
b.ToTable("Templates", "myAi");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace MyAi.Models.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddTemplates : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.EnsureSchema(
|
|
||||||
name: "myAi");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Templates",
|
|
||||||
schema: "myAi",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Key = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
|
||||||
Language = table.Column<string>(type: "nvarchar(8)", maxLength: 8, nullable: false),
|
|
||||||
Value = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
|
||||||
Description = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: false, defaultValue: ""),
|
|
||||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "SYSUTCDATETIME()")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Templates", x => new { x.Key, x.Language });
|
|
||||||
});
|
|
||||||
|
|
||||||
Seed(migrationBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Seed(MigrationBuilder m)
|
|
||||||
{
|
|
||||||
void Row(string key, string lang, string value, string description = "")
|
|
||||||
=> m.InsertData("Templates", ["Key", "Language", "Value", "Description"], [key, lang, value, description], "myAi");
|
|
||||||
|
|
||||||
// Match result email — subject
|
|
||||||
Row("email.match.subject", "en", "MyAi.ro CV Match: {{score}}% - {{jobLabel}}", "Subject for the CV match result email");
|
|
||||||
Row("email.match.subject", "ro", "MyAi.ro Potrivire CV: {{score}}% - {{jobLabel}}", "Subiect email rezultat potrivire CV");
|
|
||||||
|
|
||||||
// Match result email — body
|
|
||||||
Row("email.match.body", "en",
|
|
||||||
"CV Matcher result\n\nCV Document ID: {{cvDocumentId}}\nJob: {{jobLabel}}\nJob URL: {{jobUrl}}\nScore: {{score}}%\n\nSummary:\n{{summary}}\n\nStrengths:\n{{strengths}}\n\nGaps:\n{{gaps}}\n\nRecommendations:\n{{recommendations}}",
|
|
||||||
"Body for the CV match result email");
|
|
||||||
Row("email.match.body", "ro",
|
|
||||||
"Rezultat potrivire CV\n\nID document CV: {{cvDocumentId}}\nJob: {{jobLabel}}\nURL job: {{jobUrl}}\nScor: {{score}}%\n\nRezumat:\n{{summary}}\n\nPuncte forte:\n{{strengths}}\n\nLipsuri:\n{{gaps}}\n\nRecomandări:\n{{recommendations}}",
|
|
||||||
"Corpul emailului pentru rezultatul potrivirii CV");
|
|
||||||
|
|
||||||
// Match result email — job search CTA footer
|
|
||||||
Row("email.match.job-search-footer", "en",
|
|
||||||
"\n\n---\nWant to find more jobs matching your CV?\nClick: {{jobSearchLink}}\n(link valid for {{expiryDays}} days)",
|
|
||||||
"Job search CTA appended to match result email");
|
|
||||||
Row("email.match.job-search-footer", "ro",
|
|
||||||
"\n\n---\nVrei sa gasesti mai multe joburi potrivite CV-ului tau?\nClick: {{jobSearchLink}}\n(link valabil {{expiryDays}} zile)",
|
|
||||||
"CTA cautare joburi adaugat la emailul de potrivire CV");
|
|
||||||
|
|
||||||
// Job search results email — subject
|
|
||||||
Row("email.search-results.subject", "en", "MyAi.ro: {{count}} jobs matching your CV", "Subject for job search results email");
|
|
||||||
Row("email.search-results.subject", "ro", "MyAi.ro: {{count}} joburi potrivite CV-ului tau", "Subiect email rezultate cautare joburi");
|
|
||||||
|
|
||||||
// Job search results email — body preamble (items appended in code)
|
|
||||||
Row("email.search-results.body", "en", "MyAi.ro found {{count}} jobs matching your CV:\n\n{{items}}", "Body preamble for job search results email");
|
|
||||||
Row("email.search-results.body", "ro", "MyAi.ro a gasit {{count}} joburi potrivite CV-ului tau:\n\n{{items}}", "Corpul emailului de rezultate cautare joburi");
|
|
||||||
|
|
||||||
// Job search results email — no results found
|
|
||||||
Row("email.search-results.empty", "en", "MyAi.ro found no jobs matching your CV. Try again later or update your CV.", "No results message for job search results email");
|
|
||||||
Row("email.search-results.empty", "ro", "MyAi.ro nu a gasit joburi care sa corespunda CV-ului tau. Incercati mai tarziu sau ajustati CV-ul.", "Mesaj fara rezultate pentru emailul de cautare joburi");
|
|
||||||
|
|
||||||
// HTML job-search start page messages
|
|
||||||
Row("html.job-search.started.title", "en", "Job search started", "Title for job search started page");
|
|
||||||
Row("html.job-search.started.message", "en", "Your job search has started. Results will be sent to your email shortly.", "Message for job search started page");
|
|
||||||
Row("html.job-search.started.title", "ro", "Căutare joburi pornită", "Titlu pagina cautare joburi pornita");
|
|
||||||
Row("html.job-search.started.message", "ro", "Căutarea joburilor a început. Rezultatele vor fi trimise pe email în scurt timp.", "Mesaj pagina cautare joburi pornita");
|
|
||||||
|
|
||||||
Row("html.job-search.already-used.title", "en", "Link already used", "Title for already-used page");
|
|
||||||
Row("html.job-search.already-used.message", "en", "This job search link has already been used.", "Message for already-used page");
|
|
||||||
Row("html.job-search.already-used.title", "ro", "Link deja folosit", "Titlu pagina link deja folosit");
|
|
||||||
Row("html.job-search.already-used.message", "ro", "Acest link de cautare joburi a fost deja folosit.", "Mesaj pagina link deja folosit");
|
|
||||||
|
|
||||||
Row("html.job-search.expired.title", "en", "Link expired", "Title for expired link page");
|
|
||||||
Row("html.job-search.expired.message", "en", "This job search link has expired. Please request a new CV match to get a fresh link.", "Message for expired link page");
|
|
||||||
Row("html.job-search.expired.title", "ro", "Link expirat", "Titlu pagina link expirat");
|
|
||||||
Row("html.job-search.expired.message", "ro", "Acest link de cautare joburi a expirat. Solicita o noua potrivire CV pentru a primi un link nou.", "Mesaj pagina link expirat");
|
|
||||||
|
|
||||||
Row("html.job-search.invalid.title", "en", "Invalid link", "Title for invalid link page");
|
|
||||||
Row("html.job-search.invalid.message", "en", "This job search link is not valid.", "Message for invalid link page");
|
|
||||||
Row("html.job-search.invalid.title", "ro", "Link invalid", "Titlu pagina link invalid");
|
|
||||||
Row("html.job-search.invalid.message", "ro", "Acest link de cautare joburi nu este valid.", "Mesaj pagina link invalid");
|
|
||||||
|
|
||||||
Row("html.job-search.error.title", "en", "Error", "Title for error page");
|
|
||||||
Row("html.job-search.error.message", "en", "An error occurred. Please try again later.", "Message for error page");
|
|
||||||
Row("html.job-search.error.title", "ro", "Eroare", "Titlu pagina eroare");
|
|
||||||
Row("html.job-search.error.message", "ro", "A apărut o eroare. Te rugăm să încerci din nou mai târziu.", "Mesaj pagina eroare");
|
|
||||||
|
|
||||||
// AI system prompt for CV matching (language is a {{languageName}} variable inside it)
|
|
||||||
Row("ai.cv-match.system-prompt", "*",
|
|
||||||
"You are a strict CV-to-job matching engine. Return JSON only. Score realistically from 0 to 100.\nPenalize missing required skills. Do not invent experience. Use concise business language.\nRespond entirely in {{languageName}} — all text fields in the JSON must be in {{languageName}}.\nJSON shape: {\"score\":number,\"summary\":\"...\",\"strengths\":[\"...\"],\"gaps\":[\"...\"],\"recommendations\":[\"...\"],\"evidence\":[\"...\"]}",
|
|
||||||
"System prompt template for the CV-to-job LLM matching call. {{languageName}} is substituted at runtime.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Templates",
|
|
||||||
schema: "myAi");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using MyAi.Models.Data;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace MyAi.Models.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(MyAiDbContext))]
|
|
||||||
partial class MyAiDbContextModelSnapshot : ModelSnapshot
|
|
||||||
{
|
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("myAi")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
|
||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("MyAi.Models.Data.Entities.TemplateEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)");
|
|
||||||
|
|
||||||
b.Property<string>("Language")
|
|
||||||
.HasMaxLength(8)
|
|
||||||
.HasColumnType("nvarchar(8)");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("nvarchar(500)")
|
|
||||||
.HasDefaultValue("");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasDefaultValueSql("SYSUTCDATETIME()");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("nvarchar(max)");
|
|
||||||
|
|
||||||
b.HasKey("Key", "Language");
|
|
||||||
|
|
||||||
b.ToTable("Templates", "myAi");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using MyAi.Models.Data;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace MyAi.Models.Services;
|
|
||||||
|
|
||||||
public sealed class DbTemplateService : ITemplateService
|
|
||||||
{
|
|
||||||
private readonly IServiceScopeFactory _scopeFactory;
|
|
||||||
private readonly ILogger<DbTemplateService> _logger;
|
|
||||||
private ConcurrentDictionary<string, string> _cache = new(StringComparer.OrdinalIgnoreCase);
|
|
||||||
private DateTime _loadedAt = DateTime.MinValue;
|
|
||||||
private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(10);
|
|
||||||
|
|
||||||
public DbTemplateService(IServiceScopeFactory scopeFactory, ILogger<DbTemplateService> logger)
|
|
||||||
{
|
|
||||||
_scopeFactory = scopeFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Get(string key, string language = "en")
|
|
||||||
{
|
|
||||||
EnsureCacheLoaded();
|
|
||||||
|
|
||||||
if (_cache.TryGetValue(CacheKey(key, language), out var value))
|
|
||||||
return value;
|
|
||||||
|
|
||||||
if (!string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)
|
|
||||||
&& _cache.TryGetValue(CacheKey(key, "en"), out var fallback))
|
|
||||||
return fallback;
|
|
||||||
|
|
||||||
_logger.LogWarning("Template not found: key={Key}, language={Language}", key, language);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Render(string key, string language, params (string Key, string Value)[] placeholders)
|
|
||||||
{
|
|
||||||
var template = Get(key, language);
|
|
||||||
foreach (var (k, v) in placeholders)
|
|
||||||
template = template.Replace($"{{{{{k}}}}}", v, StringComparison.OrdinalIgnoreCase);
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureCacheLoaded()
|
|
||||||
{
|
|
||||||
if (DateTime.UtcNow - _loadedAt < CacheTtl) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var scope = _scopeFactory.CreateScope();
|
|
||||||
var db = scope.ServiceProvider.GetRequiredService<MyAiDbContext>();
|
|
||||||
var rows = db.Templates.AsNoTracking().ToList();
|
|
||||||
var fresh = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var row in rows)
|
|
||||||
fresh[CacheKey(row.Key, row.Language)] = row.Value;
|
|
||||||
|
|
||||||
_cache = fresh;
|
|
||||||
_loadedAt = DateTime.UtcNow;
|
|
||||||
_logger.LogDebug("Template cache refreshed. {Count} templates loaded.", rows.Count);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to refresh template cache. Serving stale cache.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string CacheKey(string key, string language) => $"{key}::{language}";
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace MyAi.Models.Services;
|
|
||||||
|
|
||||||
public interface ITemplateService
|
|
||||||
{
|
|
||||||
string Get(string key, string language = "en");
|
|
||||||
string Render(string key, string language, params (string Key, string Value)[] placeholders);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<RootNamespace>MyAi.Models</RootNamespace>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.7" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
Reference in New Issue
Block a user