Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f4512a807c | |||
| a89779a00c | |||
| 711ea2a00b | |||
| febc875834 | |||
| c27b1088ee | |||
| 2305e4b728 | |||
| 807d56d071 | |||
| b6dc66744f | |||
| c77cd7d524 | |||
| a01f80bb14 | |||
| 17c8e5e9ed | |||
| 9980d9217a | |||
| 9bcce06842 | |||
| 03b025c09c | |||
| 528eeb13a1 |
+6
-1
@@ -28,9 +28,14 @@ services:
|
|||||||
- DB_PROVIDER=PostgreSQL
|
- DB_PROVIDER=PostgreSQL
|
||||||
- ConnectionStrings__DefaultConnection=Host=db;Database=timetracker;Username=timetracker_user;Password=SecretPassword123;
|
- ConnectionStrings__DefaultConnection=Host=db;Database=timetracker;Username=timetracker_user;Password=SecretPassword123;
|
||||||
- EnableHttpsRedirect=false
|
- EnableHttpsRedirect=false
|
||||||
|
- TenantSettings__BaseDomain=timetracker.marc-wieland.de
|
||||||
|
volumes:
|
||||||
|
- dpkeys:/home/app/.aspnet/DataProtection-Keys
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
name: timetracker_pgdata
|
name: timetracker_pgdata
|
||||||
|
dpkeys:
|
||||||
|
name: timetracker_dpkeys
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
@@ -13,7 +13,6 @@ namespace timetracker.Data.Migrations
|
|||||||
migrationBuilder.AddColumn<bool>(
|
migrationBuilder.AddColumn<bool>(
|
||||||
name: "IsTenantAdmin",
|
name: "IsTenantAdmin",
|
||||||
table: "Users",
|
table: "Users",
|
||||||
type: "INTEGER",
|
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: false);
|
defaultValue: false);
|
||||||
|
|
||||||
|
|||||||
+265
@@ -0,0 +1,265 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using timetracker.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace timetracker.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(TimetrackerDbContext))]
|
||||||
|
[Migration("20260625184756_PendingChanges")]
|
||||||
|
partial class PendingChanges
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.9");
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.AppSettings", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<double>("DailyTargetHours")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<DateOnly?>("FlexTimeStartDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("FlexTimeStartingBalanceHours")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<string>("GermanState")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("MinimumBreakMinutes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("TenantId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("VacationDaysPerYear")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("WorkFriday")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("WorkMonday")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("WorkSaturday")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("WorkSunday")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("WorkThursday")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("WorkTuesday")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("WorkWednesday")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("AppSettings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.BreakEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<TimeOnly?>("EndTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<TimeOnly?>("StartTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("WorkDayId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("WorkDayId");
|
||||||
|
|
||||||
|
b.ToTable("BreakEntries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.PublicHoliday", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Counties")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PublicHolidays");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.Tenant", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsApproved")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PrimaryColor")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SecondaryColor")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Subdomain")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Subdomain")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Tenants");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("IsTenantAdmin")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordSalt")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("TenantId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.VacationDay", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Note")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("TenantId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("VacationDays");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.WorkDay", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<TimeOnly?>("EndTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<TimeOnly?>("StartTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("TenantId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("WorkDays");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.BreakEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("timetracker.Shared.WorkDay", "WorkDay")
|
||||||
|
.WithMany("Breaks")
|
||||||
|
.HasForeignKey("WorkDayId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("WorkDay");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.User", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("timetracker.Shared.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId");
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("timetracker.Shared.WorkDay", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Breaks");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace timetracker.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class PendingChanges : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ namespace timetracker.Data.Migrations
|
|||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.8");
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.9");
|
||||||
|
|
||||||
modelBuilder.Entity("timetracker.Shared.AppSettings", b =>
|
modelBuilder.Entity("timetracker.Shared.AppSettings", b =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class TenantProvider : ITenantProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resolve and cache in HttpContext.Items
|
// Resolve and cache in HttpContext.Items
|
||||||
ResolveTenantAsync().GetAwaiter().GetResult();
|
ResolveTenant();
|
||||||
|
|
||||||
if (httpContext.Items.TryGetValue("TenantId", out cachedId))
|
if (httpContext.Items.TryGetValue("TenantId", out cachedId))
|
||||||
{
|
{
|
||||||
@@ -48,27 +48,27 @@ public class TenantProvider : ITenantProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Tenant?> GetCurrentTenantAsync()
|
public Task<Tenant?> GetCurrentTenantAsync()
|
||||||
{
|
{
|
||||||
var httpContext = _httpContextAccessor.HttpContext;
|
var httpContext = _httpContextAccessor.HttpContext;
|
||||||
if (httpContext == null) return null;
|
if (httpContext == null) return Task.FromResult<Tenant?>(null);
|
||||||
|
|
||||||
if (httpContext.Items.TryGetValue("CurrentTenant", out var cachedTenant))
|
if (httpContext.Items.TryGetValue("CurrentTenant", out var cachedTenant))
|
||||||
{
|
{
|
||||||
return (Tenant?)cachedTenant;
|
return Task.FromResult((Tenant?)cachedTenant);
|
||||||
}
|
}
|
||||||
|
|
||||||
await ResolveTenantAsync();
|
ResolveTenant();
|
||||||
|
|
||||||
if (httpContext.Items.TryGetValue("CurrentTenant", out cachedTenant))
|
if (httpContext.Items.TryGetValue("CurrentTenant", out cachedTenant))
|
||||||
{
|
{
|
||||||
return (Tenant?)cachedTenant;
|
return Task.FromResult((Tenant?)cachedTenant);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return Task.FromResult<Tenant?>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ResolveTenantAsync()
|
private void ResolveTenant()
|
||||||
{
|
{
|
||||||
var httpContext = _httpContextAccessor.HttpContext;
|
var httpContext = _httpContextAccessor.HttpContext;
|
||||||
if (httpContext == null) return;
|
if (httpContext == null) return;
|
||||||
@@ -82,9 +82,9 @@ public class TenantProvider : ITenantProvider
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(subdomain))
|
if (!string.IsNullOrEmpty(subdomain))
|
||||||
{
|
{
|
||||||
await using var db = await _dbContextFactory.CreateDbContextAsync();
|
using var db = _dbContextFactory.CreateDbContext();
|
||||||
var tenant = await db.Tenants
|
var tenant = db.Tenants
|
||||||
.FirstOrDefaultAsync(t => t.Subdomain.ToLower() == subdomain.ToLower());
|
.FirstOrDefault(t => t.Subdomain.ToLower() == subdomain.ToLower());
|
||||||
|
|
||||||
if (tenant != null)
|
if (tenant != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -114,14 +114,16 @@ if (dbProvider.Equals("PostgreSQL", StringComparison.OrdinalIgnoreCase))
|
|||||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
|
||||||
?? builder.Configuration["ConnectionStrings:DefaultConnection"];
|
?? builder.Configuration["ConnectionStrings:DefaultConnection"];
|
||||||
builder.Services.AddDbContextFactory<TimetrackerDbContext>(options =>
|
builder.Services.AddDbContextFactory<TimetrackerDbContext>(options =>
|
||||||
options.UseNpgsql(connectionString));
|
options.UseNpgsql(connectionString)
|
||||||
|
.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var dbPath = Environment.GetEnvironmentVariable("TIMETRACKER_DB_PATH")
|
var dbPath = Environment.GetEnvironmentVariable("TIMETRACKER_DB_PATH")
|
||||||
?? Path.Combine(builder.Environment.ContentRootPath, "timetracker.db");
|
?? Path.Combine(builder.Environment.ContentRootPath, "timetracker.db");
|
||||||
builder.Services.AddDbContextFactory<TimetrackerDbContext>(options =>
|
builder.Services.AddDbContextFactory<TimetrackerDbContext>(options =>
|
||||||
options.UseSqlite($"Data Source={dbPath}"));
|
options.UseSqlite($"Data Source={dbPath}")
|
||||||
|
.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
@@ -131,13 +133,149 @@ using (var scope = app.Services.CreateScope())
|
|||||||
{
|
{
|
||||||
var factory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<TimetrackerDbContext>>();
|
var factory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<TimetrackerDbContext>>();
|
||||||
await using var db = await factory.CreateDbContextAsync();
|
await using var db = await factory.CreateDbContextAsync();
|
||||||
|
|
||||||
if (dbProvider.Equals("PostgreSQL", StringComparison.OrdinalIgnoreCase))
|
if (dbProvider.Equals("PostgreSQL", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
await db.Database.EnsureCreatedAsync();
|
try
|
||||||
|
{
|
||||||
|
var conn = db.Database.GetDbConnection();
|
||||||
|
if (conn.State != System.Data.ConnectionState.Open)
|
||||||
|
{
|
||||||
|
await conn.OpenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if AppSettings table already exists
|
||||||
|
using (var cmdCheck = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdCheck.CommandText = "SELECT EXISTS (SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'AppSettings');";
|
||||||
|
var appSettingsExists = (bool)(await cmdCheck.ExecuteScalarAsync() ?? false);
|
||||||
|
|
||||||
|
if (appSettingsExists)
|
||||||
|
{
|
||||||
|
// Ensure __EFMigrationsHistory table exists
|
||||||
|
using (var cmdCreateHistory = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdCreateHistory.CommandText = @"
|
||||||
|
CREATE TABLE IF NOT EXISTS ""__EFMigrationsHistory"" (
|
||||||
|
""MigrationId"" character varying(150) NOT NULL,
|
||||||
|
""ProductVersion"" character varying(32) NOT NULL,
|
||||||
|
CONSTRAINT ""PK___EFMigrationsHistory"" PRIMARY KEY (""MigrationId"")
|
||||||
|
);";
|
||||||
|
await cmdCreateHistory.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the initial migration is already recorded
|
||||||
|
using (var cmdCheckHistory = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdCheckHistory.CommandText = @"SELECT COUNT(*) FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260520133634_Initial';";
|
||||||
|
var historyCount = Convert.ToInt32(await cmdCheckHistory.ExecuteScalarAsync() ?? 0);
|
||||||
|
|
||||||
|
if (historyCount == 0)
|
||||||
|
{
|
||||||
|
// Seed the history table with the migrations that were already created by EnsureCreated in older versions
|
||||||
|
using (var cmdInsertHistory = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdInsertHistory.CommandText = @"
|
||||||
|
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"") VALUES
|
||||||
|
('20260520133634_Initial', '10.0.9'),
|
||||||
|
('20260520200000_AddPublicHolidays', '10.0.9'),
|
||||||
|
('20260522081459_AddMultiUser', '10.0.9'),
|
||||||
|
('20260607213215_AddFlexTimeAndHolidayState', '10.0.9');";
|
||||||
|
await cmdInsertHistory.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Fehler beim Vorbereiten der PostgreSQL Migrationshistorie: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
await db.Database.MigrateAsync();
|
||||||
|
|
||||||
|
if (dbProvider.Equals("PostgreSQL", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
await db.Database.MigrateAsync();
|
try
|
||||||
|
{
|
||||||
|
var conn = db.Database.GetDbConnection();
|
||||||
|
if (conn.State != System.Data.ConnectionState.Open)
|
||||||
|
{
|
||||||
|
await conn.OpenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if column IsTenantAdmin in Users table is of type integer
|
||||||
|
using (var cmdCheckType = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdCheckType.CommandText = @"
|
||||||
|
SELECT data_type
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_name = 'Users' AND column_name = 'IsTenantAdmin';";
|
||||||
|
var dataType = await cmdCheckType.ExecuteScalarAsync() as string;
|
||||||
|
if (dataType != null && dataType.Equals("integer", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// Alter the column type to boolean
|
||||||
|
using (var cmdAlterType = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdAlterType.CommandText = @"
|
||||||
|
ALTER TABLE ""Users""
|
||||||
|
ALTER COLUMN ""IsTenantAdmin"" TYPE boolean
|
||||||
|
USING (""IsTenantAdmin"" <> 0);";
|
||||||
|
await cmdAlterType.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if column IsApproved in Tenants table is of type integer
|
||||||
|
using (var cmdCheckApproved = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdCheckApproved.CommandText = @"
|
||||||
|
SELECT data_type
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_name = 'Tenants' AND column_name = 'IsApproved';";
|
||||||
|
var dataType = await cmdCheckApproved.ExecuteScalarAsync() as string;
|
||||||
|
if (dataType != null && dataType.Equals("integer", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// Alter the column type to boolean
|
||||||
|
using (var cmdAlterApproved = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdAlterApproved.CommandText = @"
|
||||||
|
ALTER TABLE ""Tenants""
|
||||||
|
ALTER COLUMN ""IsApproved"" TYPE boolean
|
||||||
|
USING (""IsApproved"" <> 0);";
|
||||||
|
await cmdAlterApproved.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if column CreatedAt in Tenants table is of type text or character varying
|
||||||
|
using (var cmdCheckCreatedAt = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdCheckCreatedAt.CommandText = @"
|
||||||
|
SELECT data_type
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_name = 'Tenants' AND column_name = 'CreatedAt';";
|
||||||
|
var dataType = await cmdCheckCreatedAt.ExecuteScalarAsync() as string;
|
||||||
|
if (dataType != null && (dataType.Equals("text", StringComparison.OrdinalIgnoreCase) || dataType.Equals("character varying", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
// Alter the column type to timestamp without time zone
|
||||||
|
using (var cmdAlterCreatedAt = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmdAlterCreatedAt.CommandText = @"
|
||||||
|
ALTER TABLE ""Tenants""
|
||||||
|
ALTER COLUMN ""CreatedAt"" TYPE timestamp without time zone
|
||||||
|
USING (""CreatedAt""::timestamp without time zone);";
|
||||||
|
await cmdAlterCreatedAt.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Fehler beim Konvertieren der PostgreSQL-Spaltentypen (IsTenantAdmin/IsApproved/CreatedAt): {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.8" />
|
||||||
<PackageReference Include="MudBlazor" Version="9.4.0" />
|
<PackageReference Include="MudBlazor" Version="9.4.0" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
|
||||||
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user