using LANCommander.SDK.Enums; using LANCommander.Server.Data; using LANCommander.Server.Data.Enums; using LANCommander.Server.Data.Models; using LANCommander.Server.Services; using LANCommander.Server.Services.Models; using LANCommander.Server.Settings.Enums; using LANCommander.Server.Settings.Models; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Octokit; namespace LANCommander.Server.Startup; public static class Database { public static WebApplicationBuilder AddDatabase(this WebApplicationBuilder builder, string[] args) { builder.Services.AddDbContextFactory(); builder.Services.AddDbContext(); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); return builder; } public static WebApplication UseDatabase(this WebApplication app, string[] args) { var settings = app.Services.GetRequiredService>(); var databaseProviderParameter = args.FirstOrDefault(arg => arg.StartsWith("--database-provider="))?.Split('=', 2).Last(); var connectionStringParameter = args.FirstOrDefault(arg => arg.StartsWith("--connection-string="))?.Split('=', 2).Last(); if (!String.IsNullOrWhiteSpace(databaseProviderParameter)) DatabaseContext.Provider = Enum.Parse(databaseProviderParameter); else DatabaseContext.Provider = settings.Value.Server.Database.Provider; if (!String.IsNullOrWhiteSpace(connectionStringParameter)) DatabaseContext.ConnectionString = connectionStringParameter; else DatabaseContext.ConnectionString = settings.Value.Server.Database.ConnectionString; return app; } public static async Task MigrateDatabaseAsync(this WebApplication app) { if (DatabaseContext.Provider != DatabaseProvider.Unknown) { using var scope = app.Services.CreateAsyncScope(); using var db = scope.ServiceProvider.GetRequiredService(); var logger = scope.ServiceProvider.GetRequiredService>(); var settingsProvider = scope.ServiceProvider.GetRequiredService>(); logger.LogDebug("Migrating database if required"); if ((await db.Database.GetPendingMigrationsAsync()).Any()) { if (DatabaseContext.Provider == DatabaseProvider.SQLite) { var dataSource = new SqliteConnectionStringBuilder(settingsProvider.CurrentValue.Server.Database.ConnectionString).DataSource; var backupName = Path.Combine("Backups", $"LANCommander.db.{DateTime.Now.ToString("dd-MM-yyyy-HH.mm.ss.bak")}"); if (File.Exists(dataSource)) { logger.LogInformation("Migrations pending, database will be backed up to {BackupName}", backupName); File.Copy(dataSource, backupName); } } await db.Database.MigrateAsync(); var storageLocationService = scope.ServiceProvider.GetRequiredService(); var archiveLocation = await storageLocationService.AddMissingAsync(l => l.Type == StorageLocationType.Archive && l.Default, new StorageLocation { Path = "Uploads", Type = StorageLocationType.Archive, Default = true, }); if (!Directory.Exists(archiveLocation.Value.Path)) Directory.CreateDirectory(archiveLocation.Value.Path); var mediaLocation = await storageLocationService.AddMissingAsync(l => l.Type == StorageLocationType.Media && l.Default, new StorageLocation { Path = "Media", Type = StorageLocationType.Media, Default = true, }); if (!Directory.Exists(mediaLocation.Value.Path)) Directory.CreateDirectory(mediaLocation.Value.Path); var saveLocation = await storageLocationService.AddMissingAsync(l => l.Type == StorageLocationType.Save && l.Default, new StorageLocation { Path = "Saves", Type = StorageLocationType.Save, Default = true, }); if (!Directory.Exists(saveLocation.Value.Path)) Directory.CreateDirectory(saveLocation.Value.Path); } else logger.LogDebug("No pending migrations are available. Skipping database migration."); } } }