LANCommander/LANCommander.Server.Services/SetupService.cs

180 lines
6.5 KiB
C#
Raw Permalink Normal View History

using LANCommander.SDK;
using LANCommander.SDK.Enums;
using LANCommander.Server.Data;
2024-10-12 18:36:00 -05:00
using LANCommander.Server.Data.Enums;
using LANCommander.Server.Data.Models;
using LANCommander.Server.Services.Factories;
using LANCommander.Server.Settings.Enums;
using Microsoft.AspNetCore.Identity;
2024-10-12 18:36:00 -05:00
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MySqlConnector;
using Npgsql;
namespace LANCommander.Server.Services
{
public class SetupService(
ILogger<SetupService> logger,
SettingsProvider<Settings.Settings> settingsProvider,
IdentityContextFactory identityContextFactory,
IServiceProvider serviceProvider) : BaseService(logger, settingsProvider), IDisposable
2024-10-12 18:36:00 -05:00
{
public async Task<bool> IsSetupInitialized()
{
2025-11-17 20:31:11 -06:00
try
{
if (DatabaseContext.Provider == DatabaseProvider.Unknown)
return false;
using var identityContext = await identityContextFactory.CreateAsync();
var admins = await identityContext.UserManager.GetUsersInRoleAsync(RoleService.AdministratorRoleName);
2025-11-17 20:31:11 -06:00
return admins.Any();
}
catch (Exception ex)
{
logger.LogError(ex, ex.Message);
2025-11-17 20:31:11 -06:00
return false;
}
}
public async Task ChangeProviderAsync(DatabaseProvider provider, string connectionString)
2024-10-12 18:36:00 -05:00
{
DatabaseContext.Provider = provider;
DatabaseContext.ConnectionString = connectionString;
2024-10-12 18:36:00 -05:00
using (var scope = serviceProvider.CreateScope())
2024-10-12 18:36:00 -05:00
{
var dbFactory = scope.ServiceProvider.GetService<IDbContextFactory<DatabaseContext>>();
var db = await dbFactory.CreateDbContextAsync();
2024-10-12 18:36:00 -05:00
if ((await db.Database.GetPendingMigrationsAsync()).Any())
{
if (provider == DatabaseProvider.SQLite)
{
var dataSource = new SqliteConnectionStringBuilder(connectionString).DataSource;
var backupName = AppPaths.GetConfigPath("Backups",
$"LANCommander.db.{DateTime.Now.ToString("dd-MM-yyyy-HH.mm.ss.bak")}");
2024-10-12 18:36:00 -05:00
if (File.Exists(dataSource))
{
_logger.LogInformation("Migrations pending, database will be backed up to {BackupName}", backupName);
2024-10-12 18:36:00 -05:00
File.Copy(dataSource, backupName);
}
}
await db.Database.MigrateAsync();
}
await db.DisposeAsync();
2024-10-12 18:36:00 -05:00
}
}
public static bool ValidateConnectionString(DatabaseProvider provider, string connectionString)
{
switch (provider)
{
case DatabaseProvider.SQLite:
return ValidateSqliteConnectionString(connectionString);
case DatabaseProvider.PostgreSQL:
return ValidatePostgreSqlConnectionString(connectionString);
case DatabaseProvider.MySQL:
return ValidateMySqlConnectionString(connectionString);
default:
return false;
}
}
public static bool ValidateSqliteConnectionString(string connectionString)
{
using (SqliteConnection connection = new SqliteConnection(connectionString))
{
connection.Open();
return true;
}
}
public static bool ValidatePostgreSqlConnectionString(string connectionString)
{
using (NpgsqlConnection connection = new NpgsqlConnection(connectionString))
{
connection.Open();
return true;
}
}
public static bool ValidateMySqlConnectionString(string connectionString)
{
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
return true;
}
}
public async Task UpdatePaths(IEnumerable<StorageLocation> storageLocations)
{
if (!storageLocations.Any(l => l.Type == StorageLocationType.Archive && l.Default))
throw new Exception("Missing a default archive location");
if (!storageLocations.Any(l => l.Type == StorageLocationType.Media && l.Default))
throw new Exception("Missing a default media location");
if (!storageLocations.Any(l => l.Type == StorageLocationType.Save && l.Default))
throw new Exception("Missing a default save location");
using (var scope = serviceProvider.CreateScope())
{
var dbFactory = scope.ServiceProvider.GetService<IDbContextFactory<DatabaseContext>>();
var db = await dbFactory.CreateDbContextAsync();
foreach (var storageLocation in storageLocations)
{
2026-07-24 18:00:32 -05:00
// Store the configured (possibly relative) path, but create the resolved physical directory.
var resolvedPath = AppPaths.ResolveStorageLocationPath(storageLocation.Path);
if (!Directory.Exists(resolvedPath))
Directory.CreateDirectory(resolvedPath);
try
{
if (storageLocation.Id == Guid.Empty)
{
await db.StorageLocations.AddAsync(storageLocation);
}
else
{
var existingStorageLocation = await db.StorageLocations.FindAsync(storageLocation.Id);
// Copy scalar values
db.Entry(existingStorageLocation).CurrentValues.SetValues(storageLocation);
}
await db.SaveChangesAsync();
}
catch (Exception ex)
{
logger.LogError(ex, "Could not create storage location {Path}", storageLocation.Path);
throw new Exception($"Could not create storage location {storageLocation.Path}");
}
}
}
}
2025-01-22 20:54:41 -06:00
public void Dispose()
{
}
2024-10-12 18:36:00 -05:00
}
}