using System.Text.Json; using EngineeringSync.Infrastructure; using EngineeringSync.Service.Api; using EngineeringSync.Service.Hubs; using EngineeringSync.Service.Services; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); builder.Host.UseWindowsService(); // Datenbankpfad: neben der .exe oder im AppData-Ordner (dev: aktuelles Verzeichnis) var dbPath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "EngineeringSync", "engineeringsync.db"); Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!); builder.Services.AddInfrastructure(dbPath); builder.Services.AddSignalR(); builder.Services.AddSingleton(); builder.Services.AddHostedService(sp => sp.GetRequiredService()); builder.Services.AddSingleton(); // Kestrel nur auf localhost binden (kein öffentlicher Port) builder.WebHost.UseUrls("http://localhost:5050"); var app = builder.Build(); // Datenbankmigrationen beim Start await using (var scope = app.Services.CreateAsyncScope()) { var db = scope.ServiceProvider.GetRequiredService(); await db.Database.MigrateAsync(); // WAL-Modus aktivieren (muss nach dem Öffnen der DB gesetzt werden) await db.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL;"); // First-Run-Config laden (falls vorhanden) var watcher = scope.ServiceProvider.GetRequiredService(); await ProcessFirstRunConfigAsync(db, watcher); } async Task ProcessFirstRunConfigAsync(AppDbContext db, WatcherService watcher) { var configPath = Path.Combine(AppContext.BaseDirectory, "firstrun-config.json"); if (!File.Exists(configPath)) return; try { var json = await File.ReadAllTextAsync(configPath); var root = JsonDocument.Parse(json); var firstRunElement = root.RootElement.GetProperty("FirstRun"); var projectName = firstRunElement.GetProperty("ProjectName").GetString() ?? "Imported Project"; var engineeringPath = firstRunElement.GetProperty("EngineeringPath").GetString() ?? ""; var simulationPath = firstRunElement.GetProperty("SimulationPath").GetString() ?? ""; var fileExtensionsRaw = firstRunElement.GetProperty("FileExtensions").GetString() ?? ""; var watchAllFiles = firstRunElement.TryGetProperty("WatchAllFiles", out var watchAllElement) ? watchAllElement.GetBoolean() : false; // Wenn WatchAllFiles=true oder FileExtensions="*", dann leerer String (alle Dateien) var fileExtensions = (watchAllFiles || fileExtensionsRaw == "*") ? "" : fileExtensionsRaw; // Backup-Einstellungen (optional, mit Standardwerten für Rückwärtskompatibilität) var backupEnabled = true; string? backupPath = null; var maxBackupsPerFile = 0; if (root.RootElement.TryGetProperty("Backup", out var backupElement)) { if (backupElement.TryGetProperty("BackupEnabled", out var be)) backupEnabled = be.GetBoolean(); if (backupElement.TryGetProperty("BackupPath", out var bp) && bp.ValueKind != JsonValueKind.Null) backupPath = bp.GetString(); if (backupElement.TryGetProperty("MaxBackupsPerFile", out var mb)) maxBackupsPerFile = mb.GetInt32(); } // Nur erstellen, wenn die Verzeichnisse existieren if (!Directory.Exists(engineeringPath) || !Directory.Exists(simulationPath)) { System.Console.WriteLine( $"[FirstRunConfig] WARNUNG: Engineering- oder Simulations-Pfad existiert nicht. " + $"Engineering={engineeringPath}, Simulation={simulationPath}"); return; } // Prüfen: Existiert bereits ein Projekt mit diesem Namen? var existingProject = await db.Projects .FirstOrDefaultAsync(p => p.Name == projectName); if (existingProject != null) { System.Console.WriteLine( $"[FirstRunConfig] Projekt '{projectName}' existiert bereits. Überspringe Import."); return; } // Neue ProjectConfig erstellen var project = new EngineeringSync.Domain.Entities.ProjectConfig { Name = projectName, EngineeringPath = engineeringPath, SimulationPath = simulationPath, FileExtensions = fileExtensions, IsActive = true, CreatedAt = DateTime.UtcNow, BackupEnabled = backupEnabled, BackupPath = backupPath, MaxBackupsPerFile = maxBackupsPerFile }; db.Projects.Add(project); await db.SaveChangesAsync(); System.Console.WriteLine( $"[FirstRunConfig] Projekt '{projectName}' wurde importiert und aktiviert. " + $"FileExtensions='{fileExtensions}'"); // Initialer Scan: Unterschiede zwischen Engineering- und Simulations-Ordner erkennen try { await watcher.ScanExistingFilesAsync(project); } catch (Exception ex) { System.Console.WriteLine( $"[FirstRunConfig] FEHLER beim initialen Scan für '{projectName}': {ex.Message}"); } // Konfigurationsdatei umbenennen, damit sie beim nächsten Start nicht wieder verarbeitet wird // (WatcherService.ExecuteAsync lädt das Projekt automatisch aus der DB beim Host-Start) var processedPath = configPath + ".processed"; if (File.Exists(processedPath)) File.Delete(processedPath); File.Move(configPath, processedPath); System.Console.WriteLine($"[FirstRunConfig] Konfigurationsdatei verarbeitet: {configPath} → {processedPath}"); } catch (Exception ex) { System.Console.WriteLine( $"[FirstRunConfig] FEHLER beim Verarbeiten der Erstkonfiguration: {ex.Message}"); } } app.MapHub("/notifications"); app.MapProjectsApi(); app.MapChangesApi(); app.Run();