diff --git a/api/Sozsoft.Platform.HttpApi.Host.Dockerfile b/api/Sozsoft.Platform.HttpApi.Host.Dockerfile index c5b4e45..bf9a2b1 100644 --- a/api/Sozsoft.Platform.HttpApi.Host.Dockerfile +++ b/api/Sozsoft.Platform.HttpApi.Host.Dockerfile @@ -44,6 +44,7 @@ COPY "src/Sozsoft.Platform.EntityFrameworkCore/Sozsoft.Platform.EntityFrameworkC COPY "src/Sozsoft.Platform.HttpApi/Sozsoft.Platform.HttpApi.csproj" "src/Sozsoft.Platform.HttpApi/" COPY "src/Sozsoft.Platform.HttpApi.Client/Sozsoft.Platform.HttpApi.Client.csproj" "src/Sozsoft.Platform.HttpApi.Client/" COPY "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj" "src/Sozsoft.Platform.HttpApi.Host/" +COPY "src/Sozsoft.Platform.DbMigrator/Sozsoft.Platform.DbMigrator.csproj" "src/Sozsoft.Platform.DbMigrator/" COPY "test/Sozsoft.Platform.EntityFrameworkCore.Tests/Sozsoft.Platform.EntityFrameworkCore.Tests.csproj" "test/Sozsoft.Platform.EntityFrameworkCore.Tests/" COPY "test/Sozsoft.Platform.TestBase/Sozsoft.Platform.TestBase.csproj" "test/Sozsoft.Platform.TestBase/" RUN dotnet restore "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj" @@ -51,6 +52,7 @@ RUN dotnet restore "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.H COPY . . RUN mkdir -p publish RUN dotnet publish "src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj" -c Release -o /app/publish --no-restore +RUN dotnet publish "src/Sozsoft.Platform.DbMigrator/Sozsoft.Platform.DbMigrator.csproj" -c Release -o /app/migrator FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS final @@ -97,4 +99,8 @@ EXPOSE 443 WORKDIR /srv/app COPY --from=build /app/publish . + +# Migrator publish çıktısını Setup modunun çağırabilmesi için kopyala +COPY --from=build /app/migrator /srv/Sozsoft.Platform.DbMigrator + ENTRYPOINT ["./Sozsoft.Platform.HttpApi.Host"] diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/DbStartup/SetupAppRunner.cs b/api/src/Sozsoft.Platform.HttpApi.Host/DbStartup/SetupAppRunner.cs index 261c4f8..266174b 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/DbStartup/SetupAppRunner.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/DbStartup/SetupAppRunner.cs @@ -162,15 +162,47 @@ internal static class SetupAppRunner await Send("info", "Veritabanı migration ve seed başlatılıyor..."); await Send("info", $"Migrator yolu: {migratorPath}"); - var fileName = "dotnet"; - var arguments = migratorPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) - ? $"\"{migratorPath}\"" - : $"run --project \"{migratorPath}\""; + var extraArgs = cfg["Setup:MigratorArgs"] ?? "--Seed=true"; - // seed=true her zaman geçirilir — başlangıç kurulumu için zorunlu. - // appsettings Setup:MigratorArgs ile override edilebilir. - var extraArgs = cfg["Setup:MigratorArgs"] ?? "seed=true"; - arguments += $" -- {extraArgs}"; + string fileName; + string arguments; + string workingDirectory; + + if (migratorPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && File.Exists(migratorPath)) + { + // Doğrudan DLL yolu verilmiş — "--" separator YOK, doğrudan argüman + fileName = "dotnet"; + arguments = $"\"{migratorPath}\" {extraArgs}"; + workingDirectory = Path.GetDirectoryName(migratorPath)!; + } + else if (Directory.Exists(migratorPath)) + { + // Klasör verilmiş — içinde publish edilmiş DLL var mı? + var dllFiles = Directory.GetFiles(migratorPath, "*.DbMigrator.dll", SearchOption.TopDirectoryOnly); + if (dllFiles.Length == 0) + dllFiles = Directory.GetFiles(migratorPath, "*Migrator*.dll", SearchOption.TopDirectoryOnly); + + if (dllFiles.Length > 0) + { + // Publish çıktısı — SDK gerekmez, "--" separator YOK + fileName = "dotnet"; + arguments = $"\"{dllFiles[0]}\" {extraArgs}"; + workingDirectory = migratorPath; + } + else + { + // Kaynak proje klasörü — geliştirme ortamı, "--" gerekli + fileName = "dotnet"; + arguments = $"run --project \"{migratorPath}\" -- {extraArgs}"; + workingDirectory = migratorPath; + } + } + else + { + await Send("error", $"Migrator yolu bulunamadı veya geçersiz: {migratorPath}"); + await Send("done", "Hata ile sonlandı."); + return; + } await Send("info", $"Çalıştırılıyor: {fileName} {arguments}"); @@ -187,7 +219,7 @@ internal static class SetupAppRunner RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, - WorkingDirectory = migratorPath, + WorkingDirectory = workingDirectory, } }; diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json index 04ca671..95d2470 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json +++ b/api/src/Sozsoft.Platform.HttpApi.Host/appsettings.Production.json @@ -24,6 +24,10 @@ "StringEncryption": { "DefaultPassPhrase": "UQpiYfT79zRZ3yYH" }, + "Setup": { + "MigratorPath": "/srv/Sozsoft.Platform.DbMigrator", + "MigratorArgs": "--Seed=true" + }, "Serilog": { "MinimumLevel": { "Default": "Information" diff --git a/ui/src/views/setup/DatabaseSetup.tsx b/ui/src/views/setup/DatabaseSetup.tsx index 6df856a..c31d951 100644 --- a/ui/src/views/setup/DatabaseSetup.tsx +++ b/ui/src/views/setup/DatabaseSetup.tsx @@ -49,25 +49,51 @@ const DatabaseSetup = () => { setStatus('restarting') let attempt = 0 + let successCount = 0 + const REQUIRED_SUCCESS = 2 // sunucunun kararlı olduğunu doğrulamak için arka arkaya 2 başarılı yanıt const tick = async () => { attempt++ setPollCountdown(attempt) try { - const res = await fetch(applicationConfigurationUrl(false), { method: 'GET' }) - if (res.status !== 503) { - // Full ABP application is ready — reload the page - window.location.href = '/' - return + const res = await fetch( + `${import.meta.env.VITE_API_URL ?? ''}${applicationConfigurationUrl(false)}`, + { + method: 'GET', + headers: { Accept: 'application/json' }, + cache: 'no-store', + }, + ) + if (res.status === 200) { + try { + const json = await res.json() + // ABP config yanıtının geçerli olduğunu doğrula (currentUser alanı her zaman bulunur) + if (json && typeof json.currentUser === 'object') { + successCount++ + if (successCount >= REQUIRED_SUCCESS) { + // Sunucu tamamen hazır — yönlendir + window.location.href = '/' + return + } + // Bir sonraki doğrulama denemesi + pollTimerRef.current = setTimeout(tick, 1000) + return + } + } catch { + // JSON parse hatası — sunucu henüz tam hazır değil + } } + // Başarısız — sıfırla ve tekrar dene + successCount = 0 } catch { - // Server not responding yet — expected, retry + // Sunucu henüz yanıt vermiyor — bekleniyor, tekrar dene + successCount = 0 } pollTimerRef.current = setTimeout(tick, 2000) } // İlk denemeden önce kısa bir bekleme (sunucunun kapanma süresi) - pollTimerRef.current = setTimeout(tick, 2500) + pollTimerRef.current = setTimeout(tick, 3000) } const startMigration = () => {