diff --git a/.gitignore b/.gitignore index 3bb1dc7..a67d0b9 100644 --- a/.gitignore +++ b/.gitignore @@ -297,6 +297,7 @@ __pycache__/ # Environment Secrets **/environment +**/appsettings.Development.json # local sqlite files **/deepdrft.db* diff --git a/DeepDrftCli/DeepDrftCli.csproj b/DeepDrftCli/DeepDrftCli.csproj index 1ca1e16..d7dfa06 100644 --- a/DeepDrftCli/DeepDrftCli.csproj +++ b/DeepDrftCli/DeepDrftCli.csproj @@ -8,14 +8,15 @@ - - - - - - - - + + + + + + + + + diff --git a/DeepDrftCli/Program.cs b/DeepDrftCli/Program.cs index 117b010..a265998 100644 --- a/DeepDrftCli/Program.cs +++ b/DeepDrftCli/Program.cs @@ -22,7 +22,7 @@ builder.Services.AddLogging(configure => configure.AddConsole()); // Add database context builder.Services.AddDbContext(options => - options.UseSqlite(cliSettings.ConnectionString)); + options.UseNpgsql(cliSettings.ConnectionString)); // Add FileDatabase builder.Services.AddSingleton(provider => diff --git a/DeepDrftModels/DeepDrftModels.csproj b/DeepDrftModels/DeepDrftModels.csproj index cb70a35..f7f66b9 100644 --- a/DeepDrftModels/DeepDrftModels.csproj +++ b/DeepDrftModels/DeepDrftModels.csproj @@ -7,7 +7,7 @@ - + diff --git a/DeepDrftWeb.Services/Data/DeepDrftContextFactory.cs b/DeepDrftWeb.Services/Data/DeepDrftContextFactory.cs index f2f8bfd..97084ab 100644 --- a/DeepDrftWeb.Services/Data/DeepDrftContextFactory.cs +++ b/DeepDrftWeb.Services/Data/DeepDrftContextFactory.cs @@ -7,9 +7,16 @@ public class DeepDrftContextFactory : IDesignTimeDbContextFactory(); - optionsBuilder.UseSqlite("Data Source=../Database/deepdrft.db"); - + optionsBuilder.UseNpgsql(connectionString); + return new DeepDrftContext(optionsBuilder.Options); } } \ No newline at end of file diff --git a/DeepDrftWeb.Services/DeepDrftWeb.Services.csproj b/DeepDrftWeb.Services/DeepDrftWeb.Services.csproj index c923130..26da6f1 100644 --- a/DeepDrftWeb.Services/DeepDrftWeb.Services.csproj +++ b/DeepDrftWeb.Services/DeepDrftWeb.Services.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -7,12 +7,17 @@ - - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/DeepDrftWeb.Services/Migrations/20250904233927_Initial.cs b/DeepDrftWeb.Services/Migrations/20250904233927_Initial.cs deleted file mode 100644 index 0d937b3..0000000 --- a/DeepDrftWeb.Services/Migrations/20250904233927_Initial.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace DeepDrftWeb.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "track", - columns: table => new - { - id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - entry_key = table.Column(type: "TEXT", maxLength: 100, nullable: false), - track_name = table.Column(type: "TEXT", maxLength: 200, nullable: false), - artist = table.Column(type: "TEXT", maxLength: 200, nullable: false), - album = table.Column(type: "TEXT", maxLength: 200, nullable: true), - genre = table.Column(type: "TEXT", maxLength: 100, nullable: true), - release_date = table.Column(type: "TEXT", nullable: true), - image_path = table.Column(type: "TEXT", maxLength: 500, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_track", x => x.id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "track"); - } - } -} diff --git a/DeepDrftWeb.Services/Migrations/20250904233927_Initial.Designer.cs b/DeepDrftWeb.Services/Migrations/20260518025102_Initial.Designer.cs similarity index 68% rename from DeepDrftWeb.Services/Migrations/20250904233927_Initial.Designer.cs rename to DeepDrftWeb.Services/Migrations/20260518025102_Initial.Designer.cs index b652a07..1be11d7 100644 --- a/DeepDrftWeb.Services/Migrations/20250904233927_Initial.Designer.cs +++ b/DeepDrftWeb.Services/Migrations/20260518025102_Initial.Designer.cs @@ -5,63 +5,70 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable namespace DeepDrftWeb.Migrations { [DbContext(typeof(DeepDrftContext))] - [Migration("20250904233927_Initial")] + [Migration("20260518025102_Initial")] partial class Initial { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.8"); + modelBuilder + .HasAnnotation("ProductVersion", "10.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("DeepDrftModels.Entities.TrackEntity", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") + .HasColumnType("bigint") .HasColumnName("id"); + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Album") .HasMaxLength(200) - .HasColumnType("TEXT") + .HasColumnType("character varying(200)") .HasColumnName("album"); b.Property("Artist") .IsRequired() .HasMaxLength(200) - .HasColumnType("TEXT") + .HasColumnType("character varying(200)") .HasColumnName("artist"); b.Property("EntryKey") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT") + .HasColumnType("character varying(100)") .HasColumnName("entry_key"); b.Property("Genre") .HasMaxLength(100) - .HasColumnType("TEXT") + .HasColumnType("character varying(100)") .HasColumnName("genre"); b.Property("ImagePath") .HasMaxLength(500) - .HasColumnType("TEXT") + .HasColumnType("character varying(500)") .HasColumnName("image_path"); b.Property("ReleaseDate") - .HasColumnType("TEXT") + .HasColumnType("date") .HasColumnName("release_date"); b.Property("TrackName") .IsRequired() .HasMaxLength(200) - .HasColumnType("TEXT") + .HasColumnType("character varying(200)") .HasColumnName("track_name"); b.HasKey("Id"); diff --git a/DeepDrftWeb.Services/Migrations/20260518025102_Initial.cs b/DeepDrftWeb.Services/Migrations/20260518025102_Initial.cs new file mode 100644 index 0000000..2662487 --- /dev/null +++ b/DeepDrftWeb.Services/Migrations/20260518025102_Initial.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace DeepDrftWeb.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "track", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + entry_key = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + track_name = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + artist = table.Column(type: "character varying(200)", maxLength: 200, nullable: false), + album = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + genre = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + release_date = table.Column(type: "date", nullable: true), + image_path = table.Column(type: "character varying(500)", maxLength: 500, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_track", x => x.id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "track"); + } + } +} diff --git a/DeepDrftWeb.Services/Migrations/DeepDrftContextModelSnapshot.cs b/DeepDrftWeb.Services/Migrations/DeepDrftContextModelSnapshot.cs index 36cb1f3..e795600 100644 --- a/DeepDrftWeb.Services/Migrations/DeepDrftContextModelSnapshot.cs +++ b/DeepDrftWeb.Services/Migrations/DeepDrftContextModelSnapshot.cs @@ -4,6 +4,7 @@ using DeepDrftWeb.Services.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -15,50 +16,56 @@ namespace DeepDrftWeb.Migrations protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.8"); + modelBuilder + .HasAnnotation("ProductVersion", "10.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("DeepDrftModels.Entities.TrackEntity", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") + .HasColumnType("bigint") .HasColumnName("id"); + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Album") .HasMaxLength(200) - .HasColumnType("TEXT") + .HasColumnType("character varying(200)") .HasColumnName("album"); b.Property("Artist") .IsRequired() .HasMaxLength(200) - .HasColumnType("TEXT") + .HasColumnType("character varying(200)") .HasColumnName("artist"); b.Property("EntryKey") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT") + .HasColumnType("character varying(100)") .HasColumnName("entry_key"); b.Property("Genre") .HasMaxLength(100) - .HasColumnType("TEXT") + .HasColumnType("character varying(100)") .HasColumnName("genre"); b.Property("ImagePath") .HasMaxLength(500) - .HasColumnType("TEXT") + .HasColumnType("character varying(500)") .HasColumnName("image_path"); b.Property("ReleaseDate") - .HasColumnType("TEXT") + .HasColumnType("date") .HasColumnName("release_date"); b.Property("TrackName") .IsRequired() .HasMaxLength(200) - .HasColumnType("TEXT") + .HasColumnType("character varying(200)") .HasColumnName("track_name"); b.HasKey("Id"); diff --git a/DeepDrftWeb.Services/Repositories/TrackRepository.cs b/DeepDrftWeb.Services/Repositories/TrackRepository.cs index 69972a5..d4cb123 100644 --- a/DeepDrftWeb.Services/Repositories/TrackRepository.cs +++ b/DeepDrftWeb.Services/Repositories/TrackRepository.cs @@ -27,7 +27,7 @@ public class TrackRepository public async Task> GetPage(PagingParameters pageParameters) { // Two separate queries with no transaction: count and page can be momentarily inconsistent - // under concurrent writes. Acceptable — SQLite concurrency is low and the UI is read-only. + // under concurrent writes. Acceptable — write volume is low and the UI is read-only. // If filtering is added, the count query must be updated to apply the same filter. var count = await _db.Tracks.CountAsync(); diff --git a/DeepDrftWeb/DeepDrftWeb.csproj b/DeepDrftWeb/DeepDrftWeb.csproj index 8db9af4..f50f99b 100644 --- a/DeepDrftWeb/DeepDrftWeb.csproj +++ b/DeepDrftWeb/DeepDrftWeb.csproj @@ -8,17 +8,18 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/DeepDrftWeb/Startup.cs b/DeepDrftWeb/Startup.cs index f762135..c72c28c 100644 --- a/DeepDrftWeb/Startup.cs +++ b/DeepDrftWeb/Startup.cs @@ -11,7 +11,7 @@ public static class Startup { // Add Entity Framework services builder.Services.AddDbContext(options => - options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection"))); + options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))); // Add Server Prerendering Theming Support // DarkModeSettings is registered in DeepDrftWeb.Client.Startup.ConfigureDomainServices diff --git a/DeepDrftWeb/appsettings.Development.json b/DeepDrftWeb/appsettings.Development.json deleted file mode 100644 index 88066b5..0000000 --- a/DeepDrftWeb/appsettings.Development.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "DeepDrftWeb.Client.Services.StreamingAudioPlayerService": "Debug" - }, - "Console": { - "FormatterName": "simple", - "LogLevel": { - "Default": "Information" - } - } - }, - "DetailedErrors": true, - "ApiUrls": { - "ContentApi": "http://localhost:54494/" - } -} diff --git a/DeepDrftWeb/appsettings.json b/DeepDrftWeb/appsettings.json index cb978c1..7b8bb3f 100644 --- a/DeepDrftWeb/appsettings.json +++ b/DeepDrftWeb/appsettings.json @@ -7,7 +7,7 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "DefaultConnection": "Data Source=../Database/deepdrft.db" + "DefaultConnection": "Host=localhost;Database=deepdrft_dev;Username=postgres;Password=REPLACE_IN_ENV" }, "ApiUrls": { "ContentApi": "http://localhost:12777/" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9f7d2ac --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +services: + postgres: + image: postgres:17 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: deepdrft_dev + ports: + - "5432:5432" + volumes: + - deepdrft_postgres:/var/lib/postgresql/data + +volumes: + deepdrft_postgres: