From a0cdacc7be2e840b8fbd1b662c1c37f5d5fc47ea Mon Sep 17 00:00:00 2001 From: "cubecraft-agents[bot]" <3458173+cubecraft-agents[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 13:16:13 +0000 Subject: [PATCH 1/5] CUB-29: Create filament inventory database migration --- .../Configurations/BaseEntityConfiguration.cs | 15 +- .../Data/Configurations/SpoolConfiguration.cs | 8 + .../20260426131419_InitialCreate.Designer.cs | 958 ++++++++++++++++++ .../20260426131419_InitialCreate.cs | 416 ++++++++ .../ExtrudexDbContextModelSnapshot.cs | 955 +++++++++++++++++ 5 files changed, 2350 insertions(+), 2 deletions(-) create mode 100644 backend/Infrastructure/Data/Migrations/20260426131419_InitialCreate.Designer.cs create mode 100644 backend/Infrastructure/Data/Migrations/20260426131419_InitialCreate.cs create mode 100644 backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs diff --git a/backend/Infrastructure/Data/Configurations/BaseEntityConfiguration.cs b/backend/Infrastructure/Data/Configurations/BaseEntityConfiguration.cs index 818c301..9e37ac0 100644 --- a/backend/Infrastructure/Data/Configurations/BaseEntityConfiguration.cs +++ b/backend/Infrastructure/Data/Configurations/BaseEntityConfiguration.cs @@ -49,15 +49,26 @@ public abstract class BaseEntityConfiguration : IEntityTypeConfiguratio } /// - /// Converts PascalCase or camelCase to snake_case. + /// Converts PascalCase or camelCase entity name to plural snake_case table name. + /// e.g. MaterialBase → material_bases, AmsSlot → ams_slots /// protected static string ToSnakeCase(string name) { - return string.Concat( + var snake = string.Concat( name.Select((ch, i) => i > 0 && char.IsUpper(ch) && (char.IsLower(name[i - 1]) || (i + 1 < name.Length && char.IsLower(name[i + 1]))) ? "_" + ch : ch.ToString())) .ToLowerInvariant(); + + // Pluralize: add 's' (handles most cases; irregular plurals handled explicitly if needed) + // Special cases: already_plural stays, 'y' → 'ies', 's'/'x'/'ch'/'sh' → 'es' + if (snake.EndsWith("s")) + return snake; // Already plural or ambiguous — leave as-is + if (snake.EndsWith("y") && !snake.EndsWith("ay") && !snake.EndsWith("ey") && !snake.EndsWith("oy") && !snake.EndsWith("uy")) + return snake[..^1] + "ies"; + if (snake.EndsWith("x") || snake.EndsWith("ch") || snake.EndsWith("sh")) + return snake + "es"; + return snake + "s"; } } \ No newline at end of file diff --git a/backend/Infrastructure/Data/Configurations/SpoolConfiguration.cs b/backend/Infrastructure/Data/Configurations/SpoolConfiguration.cs index fbc46f8..a426906 100644 --- a/backend/Infrastructure/Data/Configurations/SpoolConfiguration.cs +++ b/backend/Infrastructure/Data/Configurations/SpoolConfiguration.cs @@ -77,6 +77,14 @@ public class SpoolConfiguration : BaseEntityConfiguration builder.HasIndex(e => e.MaterialBaseId) .HasDatabaseName("ix_spools_material_base_id"); + // Index on material_finish_id for spool filtering + builder.HasIndex(e => e.MaterialFinishId) + .HasDatabaseName("ix_spools_material_finish_id"); + + // Index on material_modifier_id for spool filtering + builder.HasIndex(e => e.MaterialModifierId) + .HasDatabaseName("ix_spools_material_modifier_id"); + // Index on is_active for active spool queries builder.HasIndex(e => e.IsActive) .HasDatabaseName("ix_spools_is_active"); diff --git a/backend/Infrastructure/Data/Migrations/20260426131419_InitialCreate.Designer.cs b/backend/Infrastructure/Data/Migrations/20260426131419_InitialCreate.Designer.cs new file mode 100644 index 0000000..0e1cb4a --- /dev/null +++ b/backend/Infrastructure/Data/Migrations/20260426131419_InitialCreate.Designer.cs @@ -0,0 +1,958 @@ +// +using System; +using Extrudex.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Extrudex.Infrastructure.Data.Migrations +{ + [DbContext(typeof(ExtrudexDbContext))] + [Migration("20260426131419_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsSlot", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AmsUnitId") + .HasColumnType("uuid") + .HasColumnName("ams_unit_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("RemainingWeightG") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("remaining_weight_g"); + + b.Property("SpoolId") + .HasColumnType("uuid") + .HasColumnName("spool_id"); + + b.Property("TrayIndex") + .HasColumnType("integer") + .HasColumnName("tray_index"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("SpoolId") + .HasDatabaseName("ix_ams_slots_spool_id"); + + b.HasIndex("AmsUnitId", "TrayIndex") + .IsUnique() + .HasDatabaseName("ix_ams_slots_ams_unit_id_tray_index"); + + b.ToTable("ams_slots", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("PrinterId") + .HasColumnType("uuid") + .HasColumnName("printer_id"); + + b.Property("UnitIndex") + .HasColumnType("integer") + .HasColumnName("unit_index"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("PrinterId", "UnitIndex") + .IsUnique() + .HasDatabaseName("ix_ams_units_printer_id_unit_index"); + + b.ToTable("ams_units", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("DensityGperCm3") + .HasPrecision(10, 4) + .HasColumnType("numeric(10,4)") + .HasColumnName("density_g_per_cm3"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_material_bases_name"); + + b.ToTable("material_bases", (string)null); + + b.HasData( + new + { + Id = new Guid("10000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096), + DensityGperCm3 = 1.24m, + Name = "PLA", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620), + DensityGperCm3 = 1.27m, + Name = "PETG", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630), + DensityGperCm3 = 1.04m, + Name = "ABS", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638), + DensityGperCm3 = 1.07m, + Name = "ASA", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645), + DensityGperCm3 = 1.21m, + Name = "TPU", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1651), + DensityGperCm3 = 1.14m, + Name = "Nylon", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1652) + }); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("MaterialBaseId") + .HasColumnType("uuid") + .HasColumnName("material_base_id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("MaterialBaseId", "Name") + .IsUnique() + .HasDatabaseName("ix_material_finishes_material_base_id_name"); + + b.ToTable("material_finishes", (string)null); + + b.HasData( + new + { + Id = new Guid("20000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Silk", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2055), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Glitter", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2056) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Marble", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Sparkle", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000007"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000008"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Silk", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000010"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000011"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000012"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000013"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000014"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2132), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000005"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2133) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000015"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139) + }); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("MaterialBaseId") + .HasColumnType("uuid") + .HasColumnName("material_base_id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("MaterialBaseId", "Name") + .IsUnique() + .HasDatabaseName("ix_material_modifiers_material_base_id_name"); + + b.ToTable("material_modifiers", (string)null); + + b.HasData( + new + { + Id = new Guid("30000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Wood Fill", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2477), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Glow-in-the-Dark", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2478) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2490), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2491) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000007"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000008"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000010"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000011"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2522), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2523) + }); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("completed_at"); + + b.Property("CostPerPrint") + .HasPrecision(10, 4) + .HasColumnType("numeric(10,4)") + .HasColumnName("cost_per_print"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("DataSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("data_source"); + + b.Property("FilamentDiameterAtPrintMm") + .HasPrecision(6, 3) + .HasColumnType("numeric(6,3)") + .HasColumnName("filament_diameter_at_print_mm"); + + b.Property("GcodeFilePath") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("gcode_file_path"); + + b.Property("GramsDerived") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("grams_derived"); + + b.Property("MaterialDensityAtPrint") + .HasPrecision(10, 4) + .HasColumnType("numeric(10,4)") + .HasColumnName("material_density_at_print"); + + b.Property("MmExtruded") + .HasPrecision(12, 2) + .HasColumnType("numeric(12,2)") + .HasColumnName("mm_extruded"); + + b.Property("Notes") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("notes"); + + b.Property("PrintName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("print_name"); + + b.Property("PrinterId") + .HasColumnType("uuid") + .HasColumnName("printer_id"); + + b.Property("SpoolId") + .HasColumnType("uuid") + .HasColumnName("spool_id"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("started_at"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasDefaultValue("Queued") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DataSource") + .HasDatabaseName("ix_print_jobs_data_source"); + + b.HasIndex("PrinterId") + .HasDatabaseName("ix_print_jobs_printer_id"); + + b.HasIndex("SpoolId") + .HasDatabaseName("ix_print_jobs_spool_id"); + + b.HasIndex("Status") + .HasDatabaseName("ix_print_jobs_status"); + + b.ToTable("print_jobs", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("api_key"); + + b.Property("ConnectionType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("connection_type"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("HostnameOrIp") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("hostname_or_ip"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("is_active"); + + b.Property("LastSeenAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_at"); + + b.Property("Manufacturer") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("manufacturer"); + + b.Property("Model") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("model"); + + b.Property("MqttPassword") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("mqtt_password"); + + b.Property("MqttUseTls") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("mqtt_use_tls"); + + b.Property("MqttUsername") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mqtt_username"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("Port") + .HasColumnType("integer") + .HasColumnName("port"); + + b.Property("PrinterType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("printer_type"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasDefaultValue("Offline") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("ConnectionType") + .HasDatabaseName("ix_printers_connection_type"); + + b.HasIndex("IsActive") + .HasDatabaseName("ix_printers_is_active"); + + b.HasIndex("PrinterType") + .HasDatabaseName("ix_printers_printer_type"); + + b.HasIndex("Status") + .HasDatabaseName("ix_printers_status"); + + b.ToTable("printers", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Brand") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("brand"); + + b.Property("ColorHex") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("character varying(7)") + .HasColumnName("color_hex"); + + b.Property("ColorName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("color_name"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("FilamentDiameterMm") + .HasPrecision(6, 3) + .HasColumnType("numeric(6,3)") + .HasColumnName("filament_diameter_mm"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("is_active"); + + b.Property("MaterialBaseId") + .HasColumnType("uuid") + .HasColumnName("material_base_id"); + + b.Property("MaterialFinishId") + .HasColumnType("uuid") + .HasColumnName("material_finish_id"); + + b.Property("MaterialModifierId") + .HasColumnType("uuid") + .HasColumnName("material_modifier_id"); + + b.Property("PurchaseDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("purchase_date"); + + b.Property("PurchasePrice") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("purchase_price"); + + b.Property("SpoolSerial") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("spool_serial"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("WeightRemainingGrams") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("weight_remaining_grams"); + + b.Property("WeightTotalGrams") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("weight_total_grams"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("ix_spools_is_active"); + + b.HasIndex("MaterialBaseId") + .HasDatabaseName("ix_spools_material_base_id"); + + b.HasIndex("MaterialFinishId") + .HasDatabaseName("ix_spools_material_finish_id"); + + b.HasIndex("MaterialModifierId") + .HasDatabaseName("ix_spools_material_modifier_id"); + + b.HasIndex("SpoolSerial") + .IsUnique() + .HasDatabaseName("ix_spools_spool_serial"); + + b.ToTable("spools", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsSlot", b => + { + b.HasOne("Extrudex.Domain.Entities.AmsUnit", "AmsUnit") + .WithMany("Slots") + .HasForeignKey("AmsUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_ams_slots_ams_unit"); + + b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") + .WithMany("AmsSlots") + .HasForeignKey("SpoolId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("fk_ams_slots_spool"); + + b.Navigation("AmsUnit"); + + b.Navigation("Spool"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => + { + b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") + .WithMany("AmsUnits") + .HasForeignKey("PrinterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_ams_units_printer"); + + b.Navigation("Printer"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => + { + b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") + .WithMany("Finishes") + .HasForeignKey("MaterialBaseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_material_finishes_material_base"); + + b.Navigation("MaterialBase"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => + { + b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") + .WithMany("Modifiers") + .HasForeignKey("MaterialBaseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_material_modifiers_material_base"); + + b.Navigation("MaterialBase"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => + { + b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") + .WithMany("PrintJobs") + .HasForeignKey("PrinterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_print_jobs_printer"); + + b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") + .WithMany("PrintJobs") + .HasForeignKey("SpoolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_print_jobs_spool"); + + b.Navigation("Printer"); + + b.Navigation("Spool"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => + { + b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") + .WithMany("Spools") + .HasForeignKey("MaterialBaseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_spools_material_base"); + + b.HasOne("Extrudex.Domain.Entities.MaterialFinish", "MaterialFinish") + .WithMany("Spools") + .HasForeignKey("MaterialFinishId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_spools_material_finish"); + + b.HasOne("Extrudex.Domain.Entities.MaterialModifier", "MaterialModifier") + .WithMany("Spools") + .HasForeignKey("MaterialModifierId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("fk_spools_material_modifier"); + + b.Navigation("MaterialBase"); + + b.Navigation("MaterialFinish"); + + b.Navigation("MaterialModifier"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => + { + b.Navigation("Slots"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => + { + b.Navigation("Finishes"); + + b.Navigation("Modifiers"); + + b.Navigation("Spools"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => + { + b.Navigation("Spools"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => + { + b.Navigation("Spools"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => + { + b.Navigation("AmsUnits"); + + b.Navigation("PrintJobs"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => + { + b.Navigation("AmsSlots"); + + b.Navigation("PrintJobs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/Infrastructure/Data/Migrations/20260426131419_InitialCreate.cs b/backend/Infrastructure/Data/Migrations/20260426131419_InitialCreate.cs new file mode 100644 index 0000000..7c3450e --- /dev/null +++ b/backend/Infrastructure/Data/Migrations/20260426131419_InitialCreate.cs @@ -0,0 +1,416 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Extrudex.Infrastructure.Data.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "material_bases", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + density_g_per_cm3 = table.Column(type: "numeric(10,4)", precision: 10, scale: 4, nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") + }, + constraints: table => + { + table.PrimaryKey("PK_material_bases", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "printers", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + status = table.Column(type: "character varying(50)", maxLength: 50, nullable: false, defaultValue: "Offline"), + name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + manufacturer = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + model = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + printer_type = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + connection_type = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + hostname_or_ip = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + port = table.Column(type: "integer", nullable: false), + mqtt_username = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + mqtt_password = table.Column(type: "character varying(500)", maxLength: 500, nullable: false), + mqtt_use_tls = table.Column(type: "boolean", nullable: false, defaultValue: false), + api_key = table.Column(type: "character varying(500)", maxLength: 500, nullable: false), + is_active = table.Column(type: "boolean", nullable: false, defaultValue: true), + last_seen_at = table.Column(type: "timestamp with time zone", nullable: true), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") + }, + constraints: table => + { + table.PrimaryKey("PK_printers", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "material_finishes", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + material_base_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") + }, + constraints: table => + { + table.PrimaryKey("PK_material_finishes", x => x.id); + table.ForeignKey( + name: "fk_material_finishes_material_base", + column: x => x.material_base_id, + principalTable: "material_bases", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "material_modifiers", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + name = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + material_base_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") + }, + constraints: table => + { + table.PrimaryKey("PK_material_modifiers", x => x.id); + table.ForeignKey( + name: "fk_material_modifiers_material_base", + column: x => x.material_base_id, + principalTable: "material_bases", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "ams_units", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + unit_index = table.Column(type: "integer", nullable: false), + printer_id = table.Column(type: "uuid", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") + }, + constraints: table => + { + table.PrimaryKey("PK_ams_units", x => x.id); + table.ForeignKey( + name: "fk_ams_units_printer", + column: x => x.printer_id, + principalTable: "printers", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "spools", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + material_base_id = table.Column(type: "uuid", nullable: false), + material_finish_id = table.Column(type: "uuid", nullable: false), + material_modifier_id = table.Column(type: "uuid", nullable: true), + brand = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + color_name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + color_hex = table.Column(type: "character varying(7)", maxLength: 7, nullable: false), + weight_total_grams = table.Column(type: "numeric(10,2)", precision: 10, scale: 2, nullable: false), + weight_remaining_grams = table.Column(type: "numeric(10,2)", precision: 10, scale: 2, nullable: false), + filament_diameter_mm = table.Column(type: "numeric(6,3)", precision: 6, scale: 3, nullable: false), + spool_serial = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + purchase_price = table.Column(type: "numeric(10,2)", precision: 10, scale: 2, nullable: true), + purchase_date = table.Column(type: "timestamp with time zone", nullable: true), + is_active = table.Column(type: "boolean", nullable: false, defaultValue: true), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") + }, + constraints: table => + { + table.PrimaryKey("PK_spools", x => x.id); + table.ForeignKey( + name: "fk_spools_material_base", + column: x => x.material_base_id, + principalTable: "material_bases", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_spools_material_finish", + column: x => x.material_finish_id, + principalTable: "material_finishes", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_spools_material_modifier", + column: x => x.material_modifier_id, + principalTable: "material_modifiers", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "ams_slots", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + tray_index = table.Column(type: "integer", nullable: false), + ams_unit_id = table.Column(type: "uuid", nullable: false), + spool_id = table.Column(type: "uuid", nullable: true), + remaining_weight_g = table.Column(type: "numeric(10,2)", precision: 10, scale: 2, nullable: true), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") + }, + constraints: table => + { + table.PrimaryKey("PK_ams_slots", x => x.id); + table.ForeignKey( + name: "fk_ams_slots_ams_unit", + column: x => x.ams_unit_id, + principalTable: "ams_units", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "fk_ams_slots_spool", + column: x => x.spool_id, + principalTable: "spools", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "print_jobs", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + printer_id = table.Column(type: "uuid", nullable: false), + spool_id = table.Column(type: "uuid", nullable: false), + print_name = table.Column(type: "character varying(500)", maxLength: 500, nullable: false), + gcode_file_path = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: true), + mm_extruded = table.Column(type: "numeric(12,2)", precision: 12, scale: 2, nullable: false), + grams_derived = table.Column(type: "numeric(10,2)", precision: 10, scale: 2, nullable: false), + cost_per_print = table.Column(type: "numeric(10,4)", precision: 10, scale: 4, nullable: true), + started_at = table.Column(type: "timestamp with time zone", nullable: true), + completed_at = table.Column(type: "timestamp with time zone", nullable: true), + status = table.Column(type: "character varying(50)", maxLength: 50, nullable: false, defaultValue: "Queued"), + data_source = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + filament_diameter_at_print_mm = table.Column(type: "numeric(6,3)", precision: 6, scale: 3, nullable: false), + material_density_at_print = table.Column(type: "numeric(10,4)", precision: 10, scale: 4, nullable: false), + notes = table.Column(type: "character varying(2000)", maxLength: 2000, nullable: true), + created_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'"), + updated_at = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now() at time zone 'utc'") + }, + constraints: table => + { + table.PrimaryKey("PK_print_jobs", x => x.id); + table.ForeignKey( + name: "fk_print_jobs_printer", + column: x => x.printer_id, + principalTable: "printers", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "fk_print_jobs_spool", + column: x => x.spool_id, + principalTable: "spools", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.InsertData( + table: "material_bases", + columns: new[] { "id", "created_at", "density_g_per_cm3", "name", "updated_at" }, + values: new object[,] + { + { new Guid("10000000-0000-0000-0000-000000000001"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096), 1.24m, "PLA", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096) }, + { new Guid("10000000-0000-0000-0000-000000000002"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620), 1.27m, "PETG", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620) }, + { new Guid("10000000-0000-0000-0000-000000000003"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630), 1.04m, "ABS", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630) }, + { new Guid("10000000-0000-0000-0000-000000000004"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638), 1.07m, "ASA", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638) }, + { new Guid("10000000-0000-0000-0000-000000000005"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645), 1.21m, "TPU", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645) }, + { new Guid("10000000-0000-0000-0000-000000000006"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1651), 1.14m, "Nylon", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1652) } + }); + + migrationBuilder.InsertData( + table: "material_finishes", + columns: new[] { "id", "created_at", "material_base_id", "name", "updated_at" }, + values: new object[,] + { + { new Guid("20000000-0000-0000-0000-000000000001"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850), new Guid("10000000-0000-0000-0000-000000000001"), "Basic", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850) }, + { new Guid("20000000-0000-0000-0000-000000000002"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041), new Guid("10000000-0000-0000-0000-000000000001"), "Matte", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041) }, + { new Guid("20000000-0000-0000-0000-000000000003"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049), new Guid("10000000-0000-0000-0000-000000000001"), "Silk", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049) }, + { new Guid("20000000-0000-0000-0000-000000000004"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2055), new Guid("10000000-0000-0000-0000-000000000001"), "Glitter", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2056) }, + { new Guid("20000000-0000-0000-0000-000000000005"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062), new Guid("10000000-0000-0000-0000-000000000001"), "Marble", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062) }, + { new Guid("20000000-0000-0000-0000-000000000006"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068), new Guid("10000000-0000-0000-0000-000000000001"), "Sparkle", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068) }, + { new Guid("20000000-0000-0000-0000-000000000007"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075), new Guid("10000000-0000-0000-0000-000000000002"), "Basic", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075) }, + { new Guid("20000000-0000-0000-0000-000000000008"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081), new Guid("10000000-0000-0000-0000-000000000002"), "Matte", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081) }, + { new Guid("20000000-0000-0000-0000-000000000009"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100), new Guid("10000000-0000-0000-0000-000000000002"), "Silk", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100) }, + { new Guid("20000000-0000-0000-0000-000000000010"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107), new Guid("10000000-0000-0000-0000-000000000003"), "Basic", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107) }, + { new Guid("20000000-0000-0000-0000-000000000011"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113), new Guid("10000000-0000-0000-0000-000000000003"), "Matte", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113) }, + { new Guid("20000000-0000-0000-0000-000000000012"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120), new Guid("10000000-0000-0000-0000-000000000004"), "Basic", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120) }, + { new Guid("20000000-0000-0000-0000-000000000013"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126), new Guid("10000000-0000-0000-0000-000000000004"), "Matte", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126) }, + { new Guid("20000000-0000-0000-0000-000000000014"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2132), new Guid("10000000-0000-0000-0000-000000000005"), "Basic", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2133) }, + { new Guid("20000000-0000-0000-0000-000000000015"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139), new Guid("10000000-0000-0000-0000-000000000006"), "Basic", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139) } + }); + + migrationBuilder.InsertData( + table: "material_modifiers", + columns: new[] { "id", "created_at", "material_base_id", "name", "updated_at" }, + values: new object[,] + { + { new Guid("30000000-0000-0000-0000-000000000001"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304), new Guid("10000000-0000-0000-0000-000000000001"), "Carbon Fiber", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304) }, + { new Guid("30000000-0000-0000-0000-000000000002"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463), new Guid("10000000-0000-0000-0000-000000000001"), "Glass Fiber", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463) }, + { new Guid("30000000-0000-0000-0000-000000000003"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471), new Guid("10000000-0000-0000-0000-000000000001"), "Wood Fill", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471) }, + { new Guid("30000000-0000-0000-0000-000000000004"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2477), new Guid("10000000-0000-0000-0000-000000000001"), "Glow-in-the-Dark", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2478) }, + { new Guid("30000000-0000-0000-0000-000000000005"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484), new Guid("10000000-0000-0000-0000-000000000002"), "Carbon Fiber", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484) }, + { new Guid("30000000-0000-0000-0000-000000000006"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2490), new Guid("10000000-0000-0000-0000-000000000002"), "Glass Fiber", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2491) }, + { new Guid("30000000-0000-0000-0000-000000000007"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497), new Guid("10000000-0000-0000-0000-000000000003"), "Carbon Fiber", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497) }, + { new Guid("30000000-0000-0000-0000-000000000008"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503), new Guid("10000000-0000-0000-0000-000000000003"), "Glass Fiber", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503) }, + { new Guid("30000000-0000-0000-0000-000000000009"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510), new Guid("10000000-0000-0000-0000-000000000004"), "Carbon Fiber", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510) }, + { new Guid("30000000-0000-0000-0000-000000000010"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516), new Guid("10000000-0000-0000-0000-000000000006"), "Carbon Fiber", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516) }, + { new Guid("30000000-0000-0000-0000-000000000011"), new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2522), new Guid("10000000-0000-0000-0000-000000000006"), "Glass Fiber", new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2523) } + }); + + migrationBuilder.CreateIndex( + name: "ix_ams_slots_ams_unit_id_tray_index", + table: "ams_slots", + columns: new[] { "ams_unit_id", "tray_index" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_ams_slots_spool_id", + table: "ams_slots", + column: "spool_id"); + + migrationBuilder.CreateIndex( + name: "ix_ams_units_printer_id_unit_index", + table: "ams_units", + columns: new[] { "printer_id", "unit_index" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_material_bases_name", + table: "material_bases", + column: "name", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_material_finishes_material_base_id_name", + table: "material_finishes", + columns: new[] { "material_base_id", "name" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_material_modifiers_material_base_id_name", + table: "material_modifiers", + columns: new[] { "material_base_id", "name" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_print_jobs_data_source", + table: "print_jobs", + column: "data_source"); + + migrationBuilder.CreateIndex( + name: "ix_print_jobs_printer_id", + table: "print_jobs", + column: "printer_id"); + + migrationBuilder.CreateIndex( + name: "ix_print_jobs_spool_id", + table: "print_jobs", + column: "spool_id"); + + migrationBuilder.CreateIndex( + name: "ix_print_jobs_status", + table: "print_jobs", + column: "status"); + + migrationBuilder.CreateIndex( + name: "ix_printers_connection_type", + table: "printers", + column: "connection_type"); + + migrationBuilder.CreateIndex( + name: "ix_printers_is_active", + table: "printers", + column: "is_active"); + + migrationBuilder.CreateIndex( + name: "ix_printers_printer_type", + table: "printers", + column: "printer_type"); + + migrationBuilder.CreateIndex( + name: "ix_printers_status", + table: "printers", + column: "status"); + + migrationBuilder.CreateIndex( + name: "ix_spools_is_active", + table: "spools", + column: "is_active"); + + migrationBuilder.CreateIndex( + name: "ix_spools_material_base_id", + table: "spools", + column: "material_base_id"); + + migrationBuilder.CreateIndex( + name: "ix_spools_material_finish_id", + table: "spools", + column: "material_finish_id"); + + migrationBuilder.CreateIndex( + name: "ix_spools_material_modifier_id", + table: "spools", + column: "material_modifier_id"); + + migrationBuilder.CreateIndex( + name: "ix_spools_spool_serial", + table: "spools", + column: "spool_serial", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ams_slots"); + + migrationBuilder.DropTable( + name: "print_jobs"); + + migrationBuilder.DropTable( + name: "ams_units"); + + migrationBuilder.DropTable( + name: "spools"); + + migrationBuilder.DropTable( + name: "printers"); + + migrationBuilder.DropTable( + name: "material_finishes"); + + migrationBuilder.DropTable( + name: "material_modifiers"); + + migrationBuilder.DropTable( + name: "material_bases"); + } + } +} diff --git a/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs b/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs new file mode 100644 index 0000000..64647c5 --- /dev/null +++ b/backend/Infrastructure/Data/Migrations/ExtrudexDbContextModelSnapshot.cs @@ -0,0 +1,955 @@ +// +using System; +using Extrudex.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Extrudex.Infrastructure.Data.Migrations +{ + [DbContext(typeof(ExtrudexDbContext))] + partial class ExtrudexDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsSlot", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AmsUnitId") + .HasColumnType("uuid") + .HasColumnName("ams_unit_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("RemainingWeightG") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("remaining_weight_g"); + + b.Property("SpoolId") + .HasColumnType("uuid") + .HasColumnName("spool_id"); + + b.Property("TrayIndex") + .HasColumnType("integer") + .HasColumnName("tray_index"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("SpoolId") + .HasDatabaseName("ix_ams_slots_spool_id"); + + b.HasIndex("AmsUnitId", "TrayIndex") + .IsUnique() + .HasDatabaseName("ix_ams_slots_ams_unit_id_tray_index"); + + b.ToTable("ams_slots", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("PrinterId") + .HasColumnType("uuid") + .HasColumnName("printer_id"); + + b.Property("UnitIndex") + .HasColumnType("integer") + .HasColumnName("unit_index"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("PrinterId", "UnitIndex") + .IsUnique() + .HasDatabaseName("ix_ams_units_printer_id_unit_index"); + + b.ToTable("ams_units", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("DensityGperCm3") + .HasPrecision(10, 4) + .HasColumnType("numeric(10,4)") + .HasColumnName("density_g_per_cm3"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_material_bases_name"); + + b.ToTable("material_bases", (string)null); + + b.HasData( + new + { + Id = new Guid("10000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096), + DensityGperCm3 = 1.24m, + Name = "PLA", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1096) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620), + DensityGperCm3 = 1.27m, + Name = "PETG", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1620) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630), + DensityGperCm3 = 1.04m, + Name = "ABS", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1630) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638), + DensityGperCm3 = 1.07m, + Name = "ASA", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1638) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645), + DensityGperCm3 = 1.21m, + Name = "TPU", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1645) + }, + new + { + Id = new Guid("10000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1651), + DensityGperCm3 = 1.14m, + Name = "Nylon", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1652) + }); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("MaterialBaseId") + .HasColumnType("uuid") + .HasColumnName("material_base_id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("MaterialBaseId", "Name") + .IsUnique() + .HasDatabaseName("ix_material_finishes_material_base_id_name"); + + b.ToTable("material_finishes", (string)null); + + b.HasData( + new + { + Id = new Guid("20000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(1850) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2041) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Silk", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2049) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2055), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Glitter", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2056) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Marble", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2062) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Sparkle", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2068) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000007"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2075) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000008"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2081) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Silk", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2100) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000010"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2107) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000011"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2113) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000012"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2120) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000013"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), + Name = "Matte", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2126) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000014"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2132), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000005"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2133) + }, + new + { + Id = new Guid("20000000-0000-0000-0000-000000000015"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), + Name = "Basic", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2139) + }); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("MaterialBaseId") + .HasColumnType("uuid") + .HasColumnName("material_base_id"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("MaterialBaseId", "Name") + .IsUnique() + .HasDatabaseName("ix_material_modifiers_material_base_id_name"); + + b.ToTable("material_modifiers", (string)null); + + b.HasData( + new + { + Id = new Guid("30000000-0000-0000-0000-000000000001"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2304) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000002"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2463) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000003"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Wood Fill", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2471) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000004"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2477), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000001"), + Name = "Glow-in-the-Dark", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2478) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000005"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2484) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000006"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2490), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000002"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2491) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000007"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2497) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000008"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000003"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2503) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000009"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000004"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2510) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000010"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), + Name = "Carbon Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2516) + }, + new + { + Id = new Guid("30000000-0000-0000-0000-000000000011"), + CreatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2522), + MaterialBaseId = new Guid("10000000-0000-0000-0000-000000000006"), + Name = "Glass Fiber", + UpdatedAt = new DateTime(2026, 4, 26, 13, 14, 18, 745, DateTimeKind.Utc).AddTicks(2523) + }); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("completed_at"); + + b.Property("CostPerPrint") + .HasPrecision(10, 4) + .HasColumnType("numeric(10,4)") + .HasColumnName("cost_per_print"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("DataSource") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("data_source"); + + b.Property("FilamentDiameterAtPrintMm") + .HasPrecision(6, 3) + .HasColumnType("numeric(6,3)") + .HasColumnName("filament_diameter_at_print_mm"); + + b.Property("GcodeFilePath") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)") + .HasColumnName("gcode_file_path"); + + b.Property("GramsDerived") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("grams_derived"); + + b.Property("MaterialDensityAtPrint") + .HasPrecision(10, 4) + .HasColumnType("numeric(10,4)") + .HasColumnName("material_density_at_print"); + + b.Property("MmExtruded") + .HasPrecision(12, 2) + .HasColumnType("numeric(12,2)") + .HasColumnName("mm_extruded"); + + b.Property("Notes") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("notes"); + + b.Property("PrintName") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("print_name"); + + b.Property("PrinterId") + .HasColumnType("uuid") + .HasColumnName("printer_id"); + + b.Property("SpoolId") + .HasColumnType("uuid") + .HasColumnName("spool_id"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("started_at"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasDefaultValue("Queued") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("DataSource") + .HasDatabaseName("ix_print_jobs_data_source"); + + b.HasIndex("PrinterId") + .HasDatabaseName("ix_print_jobs_printer_id"); + + b.HasIndex("SpoolId") + .HasDatabaseName("ix_print_jobs_spool_id"); + + b.HasIndex("Status") + .HasDatabaseName("ix_print_jobs_status"); + + b.ToTable("print_jobs", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("api_key"); + + b.Property("ConnectionType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("connection_type"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("HostnameOrIp") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("hostname_or_ip"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("is_active"); + + b.Property("LastSeenAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_at"); + + b.Property("Manufacturer") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("manufacturer"); + + b.Property("Model") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("model"); + + b.Property("MqttPassword") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)") + .HasColumnName("mqtt_password"); + + b.Property("MqttUseTls") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("mqtt_use_tls"); + + b.Property("MqttUsername") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("mqtt_username"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("name"); + + b.Property("Port") + .HasColumnType("integer") + .HasColumnName("port"); + + b.Property("PrinterType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasColumnName("printer_type"); + + b.Property("Status") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasDefaultValue("Offline") + .HasColumnName("status"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.HasKey("Id"); + + b.HasIndex("ConnectionType") + .HasDatabaseName("ix_printers_connection_type"); + + b.HasIndex("IsActive") + .HasDatabaseName("ix_printers_is_active"); + + b.HasIndex("PrinterType") + .HasDatabaseName("ix_printers_printer_type"); + + b.HasIndex("Status") + .HasDatabaseName("ix_printers_status"); + + b.ToTable("printers", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Brand") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("brand"); + + b.Property("ColorHex") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("character varying(7)") + .HasColumnName("color_hex"); + + b.Property("ColorName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("color_name"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("FilamentDiameterMm") + .HasPrecision(6, 3) + .HasColumnType("numeric(6,3)") + .HasColumnName("filament_diameter_mm"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true) + .HasColumnName("is_active"); + + b.Property("MaterialBaseId") + .HasColumnType("uuid") + .HasColumnName("material_base_id"); + + b.Property("MaterialFinishId") + .HasColumnType("uuid") + .HasColumnName("material_finish_id"); + + b.Property("MaterialModifierId") + .HasColumnType("uuid") + .HasColumnName("material_modifier_id"); + + b.Property("PurchaseDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("purchase_date"); + + b.Property("PurchasePrice") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("purchase_price"); + + b.Property("SpoolSerial") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("spool_serial"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at") + .HasDefaultValueSql("now() at time zone 'utc'"); + + b.Property("WeightRemainingGrams") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("weight_remaining_grams"); + + b.Property("WeightTotalGrams") + .HasPrecision(10, 2) + .HasColumnType("numeric(10,2)") + .HasColumnName("weight_total_grams"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("ix_spools_is_active"); + + b.HasIndex("MaterialBaseId") + .HasDatabaseName("ix_spools_material_base_id"); + + b.HasIndex("MaterialFinishId") + .HasDatabaseName("ix_spools_material_finish_id"); + + b.HasIndex("MaterialModifierId") + .HasDatabaseName("ix_spools_material_modifier_id"); + + b.HasIndex("SpoolSerial") + .IsUnique() + .HasDatabaseName("ix_spools_spool_serial"); + + b.ToTable("spools", (string)null); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsSlot", b => + { + b.HasOne("Extrudex.Domain.Entities.AmsUnit", "AmsUnit") + .WithMany("Slots") + .HasForeignKey("AmsUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_ams_slots_ams_unit"); + + b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") + .WithMany("AmsSlots") + .HasForeignKey("SpoolId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("fk_ams_slots_spool"); + + b.Navigation("AmsUnit"); + + b.Navigation("Spool"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => + { + b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") + .WithMany("AmsUnits") + .HasForeignKey("PrinterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_ams_units_printer"); + + b.Navigation("Printer"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => + { + b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") + .WithMany("Finishes") + .HasForeignKey("MaterialBaseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_material_finishes_material_base"); + + b.Navigation("MaterialBase"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => + { + b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") + .WithMany("Modifiers") + .HasForeignKey("MaterialBaseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_material_modifiers_material_base"); + + b.Navigation("MaterialBase"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.PrintJob", b => + { + b.HasOne("Extrudex.Domain.Entities.Printer", "Printer") + .WithMany("PrintJobs") + .HasForeignKey("PrinterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_print_jobs_printer"); + + b.HasOne("Extrudex.Domain.Entities.Spool", "Spool") + .WithMany("PrintJobs") + .HasForeignKey("SpoolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_print_jobs_spool"); + + b.Navigation("Printer"); + + b.Navigation("Spool"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => + { + b.HasOne("Extrudex.Domain.Entities.MaterialBase", "MaterialBase") + .WithMany("Spools") + .HasForeignKey("MaterialBaseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_spools_material_base"); + + b.HasOne("Extrudex.Domain.Entities.MaterialFinish", "MaterialFinish") + .WithMany("Spools") + .HasForeignKey("MaterialFinishId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_spools_material_finish"); + + b.HasOne("Extrudex.Domain.Entities.MaterialModifier", "MaterialModifier") + .WithMany("Spools") + .HasForeignKey("MaterialModifierId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("fk_spools_material_modifier"); + + b.Navigation("MaterialBase"); + + b.Navigation("MaterialFinish"); + + b.Navigation("MaterialModifier"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.AmsUnit", b => + { + b.Navigation("Slots"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialBase", b => + { + b.Navigation("Finishes"); + + b.Navigation("Modifiers"); + + b.Navigation("Spools"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialFinish", b => + { + b.Navigation("Spools"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.MaterialModifier", b => + { + b.Navigation("Spools"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Printer", b => + { + b.Navigation("AmsUnits"); + + b.Navigation("PrintJobs"); + }); + + modelBuilder.Entity("Extrudex.Domain.Entities.Spool", b => + { + b.Navigation("AmsSlots"); + + b.Navigation("PrintJobs"); + }); +#pragma warning restore 612, 618 + } + } +} From 458fc9a4e1e24911e1df3ef75065cb2b33a37a09 Mon Sep 17 00:00:00 2001 From: Joshua King Date: Sun, 26 Apr 2026 11:51:31 -0400 Subject: [PATCH 2/5] add dev workflow for building and deploying backend and frontend --- .gitea/workflows/dev.yml | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .gitea/workflows/dev.yml diff --git a/.gitea/workflows/dev.yml b/.gitea/workflows/dev.yml new file mode 100644 index 0000000..393cce2 --- /dev/null +++ b/.gitea/workflows/dev.yml @@ -0,0 +1,50 @@ +name: Dev Build + +on: + pull_request: + branches: [dev] + push: + branches: [dev] + +jobs: + build-test: + runs-on: ubuntu-dotnet + + steps: + - uses: actions/checkout@v4 + + - name: Restore backend + run: dotnet restore + + - name: Build backend + run: dotnet build --no-restore --configuration Release + + - name: Test backend + run: dotnet test --no-build --configuration Release + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "24" + + - name: Install frontend deps + run: npm ci + working-directory: ./frontend + + - name: Build frontend + run: npm run build + working-directory: ./frontend + + deploy-dev: + needs: build-test + if: gitea.event_name == 'push' + runs-on: ubuntu-latest + + steps: + - name: Deploy dev + run: | + echo "${{ secrets.DEV_DEPLOY_SSH_KEY }}" > /tmp/dev_key + chmod 600 /tmp/dev_key + ssh -i /tmp/dev_key -o StrictHostKeyChecking=no \ + ${{ secrets.DEV_DEPLOY_USER }}@${{ secrets.DEV_DEPLOY_HOST }} \ + "${{ secrets.DEV_DEPLOY_PATH }}/deploy.sh" \ No newline at end of file From c3def212208bf4140016ad2df6ab73828380f97d Mon Sep 17 00:00:00 2001 From: Joshua King Date: Sun, 26 Apr 2026 12:52:22 -0400 Subject: [PATCH 3/5] add notifications for Slack on success and failure of dev deployment --- .gitea/workflows/dev.yml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/dev.yml b/.gitea/workflows/dev.yml index 393cce2..65ad3d7 100644 --- a/.gitea/workflows/dev.yml +++ b/.gitea/workflows/dev.yml @@ -47,4 +47,26 @@ jobs: chmod 600 /tmp/dev_key ssh -i /tmp/dev_key -o StrictHostKeyChecking=no \ ${{ secrets.DEV_DEPLOY_USER }}@${{ secrets.DEV_DEPLOY_HOST }} \ - "${{ secrets.DEV_DEPLOY_PATH }}/deploy.sh" \ No newline at end of file + "${{ secrets.DEV_DEPLOY_PATH }}/deploy.sh" + + notify-success: + needs: [build-test, deploy-dev] + if: success() && gitea.event_name == 'push' + runs-on: ubuntu-latest + steps: + - name: Notify Slack success + run: | + curl -X POST -H 'Content-type: application/json' \ + --data "{\"text\":\"✅ Extrudex dev deployed successfully from dev branch.\"}" \ + "${{ secrets.SLACK_WEBHOOK_URL }}" + + notify-failure: + needs: [build-test, deploy-dev] + if: failure() + runs-on: ubuntu-latest + steps: + - name: Notify Slack failure + run: | + curl -X POST -H 'Content-type: application/json' \ + --data "{\"text\":\"🚨 Extrudex dev pipeline failed. Check Gitea Actions for details.\"}" \ + "${{ secrets.SLACK_WEBHOOK_URL }}" \ No newline at end of file From 4172e21fd15e039e61b24bfa7c72f545b87caff3 Mon Sep 17 00:00:00 2001 From: Joshua King Date: Sun, 26 Apr 2026 12:56:06 -0400 Subject: [PATCH 4/5] update dev workflow to use ubuntu-latest for build-test job Co-authored-by: Copilot --- .gitea/workflows/dev.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/dev.yml b/.gitea/workflows/dev.yml index 65ad3d7..3adde84 100644 --- a/.gitea/workflows/dev.yml +++ b/.gitea/workflows/dev.yml @@ -8,11 +8,16 @@ on: jobs: build-test: - runs-on: ubuntu-dotnet + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + - name: Restore backend run: dotnet restore From 7d0369b8e97406367928c313355bd3ae1bcbf46d Mon Sep 17 00:00:00 2001 From: rex-bot Date: Sun, 26 Apr 2026 17:28:06 +0000 Subject: [PATCH 5/5] chore: add Docker deployment setup and health check wiring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add multi-stage Dockerfile for backend (SDK build → ASP.NET runtime, non-root user, /health HEALTHCHECK) - Add docker-compose.dev.yml orchestrating extrudex-api + control-center-web - Add deploy.sh convenience script wrapping docker compose up --build - Wire ASP.NET health checks: AddHealthChecks().AddNpgSql() + MapHealthChecks("/health") - Add backend .dockerignore (comprehensive pattern list) - Exclude frontend/dist, frontend/node_modules, frontend/.angular from git Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 7 ++++++- backend/.dockerignore | 27 +++++++++++++++++++++++++++ backend/Dockerfile | 34 ++++++++++++++++++++++++++++++++++ backend/Extrudex.csproj | 1 + backend/Program.cs | 7 +++++++ deploy.sh | 33 +++++++++++++++++++++++++++++++++ docker-compose.dev.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 backend/.dockerignore create mode 100644 backend/Dockerfile create mode 100755 deploy.sh create mode 100644 docker-compose.dev.yml diff --git a/.gitignore b/.gitignore index 29ec7d0..27cb172 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,9 @@ bin/ obj/ *.user *.suo -.vs/ \ No newline at end of file +.vs/ + +# Frontend build artifacts +frontend/dist/ +frontend/node_modules/ +frontend/.angular/ \ No newline at end of file diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..c5a3aee --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,27 @@ +# Build artifacts +bin/ +obj/ + +# IDE / editor +.vs/ +.vscode/ +*.user +*.suo +.idea/ + +# Environment & secrets +appsettings.Development.json +.env +.env.* + +# Docker +Dockerfile +.dockerignore + +# OS +.DS_Store +Thumbs.db + +# Misc +*.md +*.log \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..b604978 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,34 @@ +# ── Stage 1: Build ────────────────────────────────────────── +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copy csproj first for layer caching — restores before copying source +COPY Extrudex.csproj . +RUN dotnet restore + +# Copy the rest of the source +COPY . . +RUN dotnet publish Extrudex.csproj \ + -c Release \ + -o /app/publish \ + --no-restore + +# ── Stage 2: Runtime ──────────────────────────────────────── +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime +WORKDIR /app + +# Non-root user for security +RUN adduser --disabled-password --gecos "" appuser +USER appuser + +# Copy published output from build stage +COPY --from=build /app/publish . + +# ASP.NET Core listens on 8080 by default in .NET 8+ +EXPOSE 8080 + +# Health check against /health endpoint +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD curl --fail http://localhost:8080/health || exit 1 + +ENTRYPOINT ["dotnet", "Extrudex.dll"] \ No newline at end of file diff --git a/backend/Extrudex.csproj b/backend/Extrudex.csproj index f4f38a8..8e664c3 100644 --- a/backend/Extrudex.csproj +++ b/backend/Extrudex.csproj @@ -9,6 +9,7 @@ + diff --git a/backend/Program.cs b/backend/Program.cs index 86ff1c4..c77d0eb 100644 --- a/backend/Program.cs +++ b/backend/Program.cs @@ -69,6 +69,10 @@ builder.Services.AddCors(options => // ── SignalR (real-time printer updates) ──────────────────── builder.Services.AddSignalR(); +// ── Health Checks ─────────────────────────────────────────── +builder.Services.AddHealthChecks() + .AddNpgSql(connectionString); + var app = builder.Build(); // ── Middleware ────────────────────────────────────────────── @@ -85,6 +89,9 @@ app.MapControllers(); // ── Hub Endpoints ─────────────────────────────────────────── app.MapHub("/hubs/printer"); +// ── Health Check Endpoint ────────────────────────────────── +app.MapHealthChecks("/health"); + app.Run(); // Helper: builds a connection string from individual env vars. diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..d17960c --- /dev/null +++ b/deploy.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -e + +echo "🔧 Deploying Extrudex Docker runtime..." + +# Check if Docker Compose is available +if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then + echo "❌ Docker Compose is not installed" + exit 1 +fi + +COMPOSE_CMD="docker compose" +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" +fi + +echo "📦 Building and starting services..." +$COMPOSE_CMD -f docker-compose.dev.yml up -d --build + +echo "⏳ Waiting for services to become healthy..." +sleep 10 + +echo "✅ Deployment complete!" +echo "" +echo "Services running:" +echo " • Extrudex API: http://localhost:5080" +echo " • Control Center Web: http://localhost:5081" +echo "" +echo "To view logs:" +echo " $COMPOSE_CMD -f docker-compose.dev.yml logs -f" +echo "" +echo "To stop:" +echo " $COMPOSE_CMD -f docker-compose.dev.yml down" \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..2859dff --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,40 @@ +version: '3.8' + +services: + extrudex-api: + build: + context: ./backend + dockerfile: Dockerfile + container_name: extrudex-api + ports: + - "5080:8080" + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://+:8080 + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + networks: + - extrudex-network + + control-center-web: + build: + context: ../Control-Center/frontend + dockerfile: Dockerfile + container_name: control-center-web + ports: + - "5081:80" + depends_on: + extrudex-api: + condition: service_healthy + restart: unless-stopped + networks: + - extrudex-network + +networks: + extrudex-network: + driver: bridge \ No newline at end of file