diff --git a/Apis/cv-matcher-data/Migrations/20260601133028_InitialSchema.cs b/Apis/cv-matcher-data/Migrations/20260601133028_InitialSchema.cs index bb11b99..13e650c 100644 --- a/Apis/cv-matcher-data/Migrations/20260601133028_InitialSchema.cs +++ b/Apis/cv-matcher-data/Migrations/20260601133028_InitialSchema.cs @@ -1,4 +1,5 @@ using System; +using CvMatcher.Data; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -12,11 +13,11 @@ namespace CvMatcher.Data.Migrations protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.EnsureSchema( - name: "cvMatcher"); + name: MigrationConstants.SchemaName); migrationBuilder.CreateTable( name: "AiPrompts", - schema: "cvMatcher", + schema: MigrationConstants.SchemaName, columns: table => new { Key = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), @@ -32,7 +33,7 @@ namespace CvMatcher.Data.Migrations migrationBuilder.CreateTable( name: "ChatCache", - schema: "cvMatcher", + schema: MigrationConstants.SchemaName, columns: table => new { CacheKey = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), @@ -48,7 +49,7 @@ namespace CvMatcher.Data.Migrations migrationBuilder.CreateTable( name: "Results", - schema: "cvMatcher", + schema: MigrationConstants.SchemaName, columns: table => new { Id = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), @@ -66,19 +67,28 @@ namespace CvMatcher.Data.Migrations migrationBuilder.CreateIndex( name: "IX_Results_CvDocumentId_JobDocumentId_Language", - schema: "cvMatcher", + schema: MigrationConstants.SchemaName, table: "Results", columns: new[] { "CvDocumentId", "JobDocumentId", "Language" }, unique: true); - // Seed AI prompts for CV matching (language-specific) - migrationBuilder.Sql(@" -INSERT INTO [cvMatcher].AiPrompts ([Key], [Language], [Value], [Description]) VALUES -('ai.cv-match.system-prompt', 'en', 'You are a strict CV-to-job matching engine. Return JSON only. Score realistically from 0 to 100. Penalize missing required skills. Do not invent experience. Use concise business language. All text fields in the JSON response must be in English. -JSON shape: {""score"":number,""summary"":""one-line summary in English"",""strengths"":[""strength 1 in English"",""strength 2 in English""],""gaps"":[""gap 1 in English""],""recommendations"":[""recommendation 1 in English""],""evidence"":[""evidence 1 in English""]}', 'System prompt for CV-to-job matching in English. Instructs LLM to return JSON with CV strengths, gaps, and recommendations relative to the job.'), -('ai.cv-match.system-prompt', 'ro', 'Ești un motor strict de potrivire CV-job. Returnează doar JSON. Punctează realist între 0 și 100. Penalizează abilitățile lipsă necesare. Nu inventa experiență. Folosește limbaj profesional concis. Toate câmpurile text din răspunsul JSON trebuie să fie în limba română. -JSON shape: {""score"":number,""summary"":""rezumat pe o linie în română"",""strengths"":[""punct forte 1 în română"",""punct forte 2 în română""],""gaps"":[""lipsă 1 în română""],""recommendations"":[""recomandare 1 în română""],""evidence"":[""dovadă 1 în română""]}', 'System prompt pentru potrivire CV-job în limba română. Instruiește LLM-ul să returneze JSON cu punctele forte ale CV-ului, lacunele și recomandări relative la job.'); - "); + Seed(migrationBuilder); + } + + private static void Seed(MigrationBuilder m) + { + void Row(string key, string lang, string value, string description = "") + => m.InsertData("AiPrompts", ["Key", "Language", "Value", "Description"], [key, lang, value, description], MigrationConstants.SchemaName); + + // AI system prompt for CV matching — English + Row("ai.cv-match.system-prompt", "en", + "You are a strict CV-to-job matching engine. Return JSON only. Score realistically from 0 to 100. Penalize missing required skills. Do not invent experience. Use concise business language. All text fields in the JSON response must be in English.\nJSON shape: {\"score\":number,\"summary\":\"one-line summary in English\",\"strengths\":[\"strength 1 in English\",\"strength 2 in English\"],\"gaps\":[\"gap 1 in English\"],\"recommendations\":[\"recommendation 1 in English\"],\"evidence\":[\"evidence 1 in English\"]}", + "System prompt for CV-to-job matching in English. Instructs LLM to return JSON with CV strengths, gaps, and recommendations relative to the job."); + + // AI system prompt for CV matching — Romanian + Row("ai.cv-match.system-prompt", "ro", + "Ești un motor strict de potrivire CV-job. Returnează doar JSON. Punctează realist între 0 și 100. Penalizează abilitățile lipsă necesare. Nu inventa experiență. Folosește limbaj profesional concis. Toate câmpurile text din răspunsul JSON trebuie să fie în limba română.\nJSON shape: {\"score\":number,\"summary\":\"rezumat pe o linie în română\",\"strengths\":[\"punct forte 1 în română\",\"punct forte 2 în română\"],\"gaps\":[\"lipsă 1 în română\"],\"recommendations\":[\"recomandare 1 în română\"],\"evidence\":[\"dovadă 1 în română\"]}", + "System prompt pentru potrivire CV-job în limba română. Instruiește LLM-ul să returneze JSON cu punctele forte ale CV-ului, lacunele și recomandări relative la job."); } /// @@ -86,15 +96,15 @@ JSON shape: {""score"":number,""summary"":""rezumat pe o linie în română"","" { migrationBuilder.DropTable( name: "AiPrompts", - schema: "cvMatcher"); + schema: MigrationConstants.SchemaName); migrationBuilder.DropTable( name: "ChatCache", - schema: "cvMatcher"); + schema: MigrationConstants.SchemaName); migrationBuilder.DropTable( name: "Results", - schema: "cvMatcher"); + schema: MigrationConstants.SchemaName); } } } diff --git a/Apis/email-data/Migrations/20260601133043_InitialSchema.cs b/Apis/email-data/Migrations/20260601133043_InitialSchema.cs index 4b5a0c8..0568ed2 100644 --- a/Apis/email-data/Migrations/20260601133043_InitialSchema.cs +++ b/Apis/email-data/Migrations/20260601133043_InitialSchema.cs @@ -97,45 +97,6 @@ namespace Email.Data.Migrations 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"); - - // HTML email shell — opening tags (blue header, white card container) - Row("email.html-shell.start", "*", - $$""" - - - - - - - - - - - - """, - "Closing HTML wrapper for branded emails (footer and closing tags)"); } /// diff --git a/Apis/email-data/Migrations/20260601145256_AddHtmlShellTemplates.Designer.cs b/Apis/email-data/Migrations/20260601145256_AddHtmlShellTemplates.Designer.cs new file mode 100644 index 0000000..ef4508b --- /dev/null +++ b/Apis/email-data/Migrations/20260601145256_AddHtmlShellTemplates.Designer.cs @@ -0,0 +1,69 @@ +// +using System; +using Email.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Email.Data.Migrations +{ + [DbContext(typeof(EmailDbContext))] + [Migration("20260601145256_AddHtmlShellTemplates")] + partial class AddHtmlShellTemplates + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("email") + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Email.Data.Entities.EmailTemplateEntity", b => + { + b.Property("Key") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Language") + .HasMaxLength(8) + .HasColumnType("nvarchar(8)"); + + b.Property("Description") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)") + .HasDefaultValue(""); + + b.Property("OperatorCopy") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)") + .HasDefaultValue(""); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("SYSUTCDATETIME()"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Key", "Language"); + + b.ToTable("Templates", "email"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Apis/email-data/Migrations/20260601145256_AddHtmlShellTemplates.cs b/Apis/email-data/Migrations/20260601145256_AddHtmlShellTemplates.cs new file mode 100644 index 0000000..6376b45 --- /dev/null +++ b/Apis/email-data/Migrations/20260601145256_AddHtmlShellTemplates.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Email.Data; + +#nullable disable + +namespace Email.Data.Migrations +{ + /// + public partial class AddHtmlShellTemplates : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + // HTML email shell — opening tags (blue header, white card container) + migrationBuilder.InsertData( + table: "Templates", + columns: new[] { "Key", "Language", "Value", "Description" }, + values: new object[] { "email.html-shell.start", "*", "\n\n\n \n \n \n\n\n
\n
\n

MyAi.ro

\n
\n
\n", "Opening HTML wrapper for branded emails (blue header, white content area)" }, + schema: MigrationConstants.SchemaName); + + // HTML email shell — closing tags (footer) + migrationBuilder.InsertData( + table: "Templates", + columns: new[] { "Key", "Language", "Value", "Description" }, + values: new object[] { "email.html-shell.end", "*", "
\n
\n

© 2026 MyAi.ro. All rights reserved.

\n
\n
\n\n\n", "Closing HTML wrapper for branded emails (footer and closing tags)" }, + schema: MigrationConstants.SchemaName); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "Templates", + keyColumns: new[] { "Key", "Language" }, + keyValues: new object[] { "email.html-shell.start", "*" }, + schema: MigrationConstants.SchemaName); + + migrationBuilder.DeleteData( + table: "Templates", + keyColumns: new[] { "Key", "Language" }, + keyValues: new object[] { "email.html-shell.end", "*" }, + schema: MigrationConstants.SchemaName); + } + } +}