diff --git a/docker-compose.yml b/docker-compose.yml index a467396..752b3c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,6 +36,14 @@ services: depends_on: - db + logshell: + image: alpine:3.22 + container_name: timetracker-logshell + restart: unless-stopped + command: ["sh", "-c", "tail -f /dev/null"] + volumes: + - applogs:/logs:ro + volumes: pgdata: name: timetracker_pgdata diff --git a/timetracker.Server/Program.cs b/timetracker.Server/Program.cs index f973181..3e304e4 100644 --- a/timetracker.Server/Program.cs +++ b/timetracker.Server/Program.cs @@ -231,6 +231,11 @@ using (var scope = app.Services.CreateScope()) await conn.OpenAsync(); } + foreach (var tableName in new[] { "Users", "Tenants", "AppSettings", "WorkDays", "BreakEntries", "VacationDays", "PublicHolidays" }) + { + await EnsurePostgresIntegerPrimaryKeyDefaultAsync(conn, tableName); + } + // Check if column IsTenantAdmin in Users table is of type integer using (var cmdCheckType = conn.CreateCommand()) { @@ -902,6 +907,31 @@ static string ComputePasswordHash(string password, string salt) return Convert.ToBase64String(hash); } +static async Task EnsurePostgresIntegerPrimaryKeyDefaultAsync(System.Data.Common.DbConnection conn, string tableName) +{ + using var cmd = conn.CreateCommand(); + cmd.CommandText = $@" +DO $$ +DECLARE + sequence_name text := '" + @"" + @"' || '{tableName}_Id_seq'; +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = '{tableName}' + AND column_name = 'Id' + AND column_default IS NULL + ) THEN + EXECUTE 'CREATE SEQUENCE IF NOT EXISTS public.""{tableName}_Id_seq""'; + EXECUTE 'ALTER SEQUENCE public.""{tableName}_Id_seq"" OWNED BY public.""{tableName}"".""Id""'; + EXECUTE 'ALTER TABLE public.""{tableName}"" ALTER COLUMN ""Id"" SET DEFAULT nextval(''''public.""{tableName}_Id_seq""'''')'; + EXECUTE 'SELECT setval(''''public.""{tableName}_Id_seq""'''', COALESCE((SELECT MAX(""Id"") FROM public.""{tableName}""), 0) + 1, false)'; + END IF; +END $$;"; + await cmd.ExecuteNonQueryAsync(); +} + // ── Models for Request Bodies ────────────────────────────────────────────────── public record LoginRequest(string Username, string Password); public record RegisterRequest(string Username, string Password, string? Honeypot = null);