using System; using System.Diagnostics; using System.IO; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; namespace Sozsoft.Platform.Controllers; /// /// DB hazır olduğunda bile /setup sayfasından migration çalıştırmaya olanak tanır. /// [Route("api/setup")] [Authorize(Roles = "admin")] public class SetupController : ControllerBase { private readonly IConfiguration _configuration; private readonly IHostEnvironment _env; public SetupController( IConfiguration configuration, IHostEnvironment env) { _configuration = configuration; _env = env; } [HttpGet("application-status")] [AllowAnonymous] public IActionResult Status() { return Ok(new { dbExists = SetupAppRunner.DatabaseIsReady(_configuration) }); } [HttpPost("migrate")] public async Task Migrate(CancellationToken ct) { Response.ContentType = "text/event-stream; charset=utf-8"; Response.Headers["Cache-Control"] = "no-cache, no-store"; Response.Headers["X-Accel-Buffering"] = "no"; await Response.Body.FlushAsync(ct); async Task Send(string level, string message) { try { var payload = JsonSerializer.Serialize(new { level, message }); await Response.WriteAsync($"data: {payload}\n\n", ct); await Response.Body.FlushAsync(ct); } catch { } } var migratorPath = _configuration["Setup:MigratorPath"] ?? Path.GetFullPath(Path.Combine(_env.ContentRootPath, "..", "Sozsoft.Platform.DbMigrator")); await Send("info", "Database migration and seeding are being initiated..."); await Send("info", $"Migrator path: {migratorPath}"); var extraArgs = _configuration["Setup:MigratorArgs"] ?? "--Seed=true"; string fileName; string arguments; string workingDirectory; if (migratorPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && System.IO.File.Exists(migratorPath)) { fileName = "dotnet"; arguments = $"\"{migratorPath}\" {extraArgs}"; workingDirectory = Path.GetDirectoryName(migratorPath)!; } else if (Directory.Exists(migratorPath)) { 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) { fileName = "dotnet"; arguments = $"\"{dllFiles[0]}\" {extraArgs}"; workingDirectory = migratorPath; } else { fileName = "dotnet"; arguments = $"run --project \"{migratorPath}\" -- {extraArgs}"; workingDirectory = migratorPath; } } else { await Send("error", $"Migrator path not found or invalid: {migratorPath}"); await Send("done", "Failed."); return; } await Send("info", $"Running: {fileName} {arguments}"); Process? process = null; try { process = new Process { StartInfo = new ProcessStartInfo { FileName = fileName, Arguments = arguments, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, WorkingDirectory = workingDirectory, } }; process.Start(); async Task ReadStream(StreamReader reader, string level) { try { while (await reader.ReadLineAsync(ct) is { } line) { await Send(level, line); } } catch (OperationCanceledException) { } } await Task.WhenAll( ReadStream(process.StandardOutput, "info"), ReadStream(process.StandardError, "warn")); await process.WaitForExitAsync(ct); if (process.ExitCode == 0) { await Send("success", "Migration and seed completed successfully."); await Send("done", "Completed."); } else { await Send("error", $"Migration failed. Exit code: {process.ExitCode}"); await Send("done", "Failed."); } } catch (OperationCanceledException) { await Send("warn", "Migration request was canceled."); } catch (Exception ex) { await Send("error", $"Migration failed: {ex.Message}"); await Send("done", "Failed."); } finally { process?.Dispose(); } } }