Files
ReifenSpaeth/Lagerbestand/Services/SiteSettingsService.cs
T

453 lines
20 KiB
C#

using System.Text.Json;
using Lagerbestand.Data;
using Lagerbestand.Models;
using Microsoft.EntityFrameworkCore;
namespace Lagerbestand.Services;
public interface ISiteSettingsService
{
Task<PromoBannerSettings> GetPromoBannerSettingsAsync();
Task SavePromoBannerSettingsAsync(PromoBannerSettings settings);
Task<LexwareApiSettings> GetLexwareApiSettingsAsync();
Task SaveLexwareApiSettingsAsync(LexwareApiSettings settings);
Task<TwoFactorSettings> GetTwoFactorSettingsAsync();
Task SaveTwoFactorSettingsAsync(TwoFactorSettings settings);
Task<SmtpSettings> GetSmtpSettingsAsync();
Task SaveSmtpSettingsAsync(SmtpSettings settings);
Task<SessionSettings> GetSessionSettingsAsync();
Task SaveSessionSettingsAsync(SessionSettings settings);
/// <summary>
/// Gets a raw setting value by key
/// </summary>
Task<string?> GetValueAsync(string key);
/// <summary>
/// Sets a raw setting value by key
/// </summary>
Task SetValueAsync(string key, string value);
}
public class SiteSettingsService : ISiteSettingsService
{
private const string PromoBannerKey = "PromoBanner";
private const string LexwareApiKey = "LexwareApi";
private const string TwoFactorKey = "TwoFactor";
private const string SmtpKey = "Smtp";
private const string SessionKey = "Session";
private readonly AppDbContext _context;
private readonly ILogger<SiteSettingsService> _logger;
public SiteSettingsService(AppDbContext context, ILogger<SiteSettingsService> logger)
{
_context = context;
_logger = logger;
}
public async Task<PromoBannerSettings> GetPromoBannerSettingsAsync()
{
try
{
// Quick timeout to prevent blocking app startup
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var setting = await _context.SiteSettings
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Key == PromoBannerKey, cts.Token);
if (setting == null)
{
return new PromoBannerSettings();
}
var settings = JsonSerializer.Deserialize<PromoBannerSettings>(setting.Value);
return settings ?? new PromoBannerSettings();
}
catch (OperationCanceledException)
{
_logger.LogDebug("Banner settings query timed out, returning default");
return new PromoBannerSettings();
}
catch (MySqlConnector.MySqlException ex) when (ex.Number == 1146)
{
// Table doesn't exist yet - this is normal during first startup
_logger.LogDebug("SiteSettings table not found, returning default settings");
return new PromoBannerSettings();
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load promo banner settings");
return new PromoBannerSettings();
}
}
public async Task SavePromoBannerSettingsAsync(PromoBannerSettings settings)
{
try
{
var existing = await _context.SiteSettings
.FirstOrDefaultAsync(s => s.Key == PromoBannerKey);
var json = JsonSerializer.Serialize(settings);
if (existing != null)
{
existing.Value = json;
existing.UpdatedAt = DateTime.UtcNow;
_context.SiteSettings.Update(existing);
}
else
{
var newSetting = new SiteSettings
{
Key = PromoBannerKey,
Value = json,
CreatedAt = DateTime.UtcNow
};
_context.SiteSettings.Add(newSetting);
}
await _context.SaveChangesAsync();
_logger.LogInformation("Promo banner settings saved successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to save promo banner settings");
throw;
}
}
public async Task<LexwareApiSettings> GetLexwareApiSettingsAsync()
{
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var setting = await _context.SiteSettings
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Key == LexwareApiKey, cts.Token);
if (setting == null)
return new LexwareApiSettings();
var settings = JsonSerializer.Deserialize<LexwareApiSettings>(setting.Value);
return settings ?? new LexwareApiSettings();
}
catch (OperationCanceledException)
{
_logger.LogDebug("Lexware settings query timed out, returning default");
return new LexwareApiSettings();
}
catch (MySqlConnector.MySqlException ex) when (ex.Number == 1146)
{
_logger.LogDebug("SiteSettings table not found, returning default Lexware settings");
return new LexwareApiSettings();
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load Lexware API settings");
return new LexwareApiSettings();
}
}
public async Task SaveLexwareApiSettingsAsync(LexwareApiSettings settings)
{
try
{
var existing = await _context.SiteSettings
.FirstOrDefaultAsync(s => s.Key == LexwareApiKey);
var json = JsonSerializer.Serialize(settings);
if (existing != null)
{
existing.Value = json;
existing.UpdatedAt = DateTime.UtcNow;
_context.SiteSettings.Update(existing);
}
else
{
var newSetting = new SiteSettings
{
Key = LexwareApiKey,
Value = json,
CreatedAt = DateTime.UtcNow
};
_context.SiteSettings.Add(newSetting);
}
await _context.SaveChangesAsync();
_logger.LogInformation("Lexware API settings saved successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to save Lexware API settings");
throw;
}
}
public async Task<TwoFactorSettings> GetTwoFactorSettingsAsync()
{
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var setting = await _context.SiteSettings
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Key == TwoFactorKey, cts.Token);
if (setting == null)
return new TwoFactorSettings();
return JsonSerializer.Deserialize<TwoFactorSettings>(setting.Value) ?? new TwoFactorSettings();
}
catch (OperationCanceledException)
{
_logger.LogDebug("TwoFactor settings query timed out, returning default");
return new TwoFactorSettings();
}
catch (MySqlConnector.MySqlException ex) when (ex.Number == 1146)
{
return new TwoFactorSettings();
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load TwoFactor settings");
return new TwoFactorSettings();
}
}
public async Task SaveTwoFactorSettingsAsync(TwoFactorSettings settings)
{
try
{
var existing = await _context.SiteSettings
.FirstOrDefaultAsync(s => s.Key == TwoFactorKey);
var json = JsonSerializer.Serialize(settings);
if (existing != null)
{
existing.Value = json;
existing.UpdatedAt = DateTime.UtcNow;
_context.SiteSettings.Update(existing);
}
else
{
_context.SiteSettings.Add(new SiteSettings
{
Key = TwoFactorKey,
Value = json,
CreatedAt = DateTime.UtcNow
});
}
await _context.SaveChangesAsync();
_logger.LogInformation("TwoFactor settings saved successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to save TwoFactor settings");
throw;
}
}
public async Task<SmtpSettings> GetSmtpSettingsAsync()
{
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var setting = await _context.SiteSettings
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Key == SmtpKey, cts.Token);
if (setting == null)
return new SmtpSettings();
return JsonSerializer.Deserialize<SmtpSettings>(setting.Value) ?? new SmtpSettings();
}
catch (OperationCanceledException)
{
_logger.LogDebug("SMTP settings query timed out, returning default");
return new SmtpSettings();
}
catch (MySqlConnector.MySqlException ex) when (ex.Number == 1146)
{
return new SmtpSettings();
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load SMTP settings");
return new SmtpSettings();
}
}
public async Task SaveSmtpSettingsAsync(SmtpSettings settings)
{
try
{
var existing = await _context.SiteSettings
.FirstOrDefaultAsync(s => s.Key == SmtpKey);
var json = JsonSerializer.Serialize(settings);
if (existing != null)
{
existing.Value = json;
existing.UpdatedAt = DateTime.UtcNow;
_context.SiteSettings.Update(existing);
}
else
{
_context.SiteSettings.Add(new SiteSettings
{
Key = SmtpKey,
Value = json,
CreatedAt = DateTime.UtcNow
});
}
await _context.SaveChangesAsync();
_logger.LogInformation("SMTP settings saved successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to save SMTP settings");
throw;
}
}
public async Task<SessionSettings> GetSessionSettingsAsync()
{
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var setting = await _context.SiteSettings
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Key == SessionKey, cts.Token);
if (setting == null)
return new SessionSettings();
return JsonSerializer.Deserialize<SessionSettings>(setting.Value) ?? new SessionSettings();
}
catch (OperationCanceledException)
{
_logger.LogDebug("Session settings query timed out, returning default");
return new SessionSettings();
}
catch (MySqlConnector.MySqlException ex) when (ex.Number == 1146)
{
return new SessionSettings();
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load Session settings");
return new SessionSettings();
}
}
public async Task SaveSessionSettingsAsync(SessionSettings settings)
{
try
{
// Clamp to safe ranges before persisting
settings.IdleTimeoutMinutes = settings.GetSafeIdleTimeoutMinutes();
settings.CircuitRetentionHours = settings.GetSafeCircuitRetentionHours();
var existing = await _context.SiteSettings
.FirstOrDefaultAsync(s => s.Key == SessionKey);
var json = JsonSerializer.Serialize(settings);
if (existing != null)
{
existing.Value = json;
existing.UpdatedAt = DateTime.UtcNow;
_context.SiteSettings.Update(existing);
}
else
{
_context.SiteSettings.Add(new SiteSettings
{
Key = SessionKey,
Value = json,
CreatedAt = DateTime.UtcNow
});
}
await _context.SaveChangesAsync();
_logger.LogInformation("Session settings saved successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to save Session settings");
throw;
}
}
public async Task<string?> GetValueAsync(string key)
{
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var setting = await _context.SiteSettings
.AsNoTracking()
.FirstOrDefaultAsync(s => s.Key == key, cts.Token);
return setting?.Value;
}
catch (OperationCanceledException)
{
_logger.LogDebug("Settings query for '{Key}' timed out", key);
return null;
}
catch (MySqlConnector.MySqlException ex) when (ex.Number == 1146)
{
_logger.LogDebug("SiteSettings table not found for key '{Key}'", key);
return null;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load setting '{Key}'", key);
return null;
}
}
public async Task SetValueAsync(string key, string value)
{
try
{
var existing = await _context.SiteSettings
.FirstOrDefaultAsync(s => s.Key == key);
if (existing != null)
{
existing.Value = value;
existing.UpdatedAt = DateTime.UtcNow;
_context.SiteSettings.Update(existing);
}
else
{
var newSetting = new SiteSettings
{
Key = key,
Value = value,
CreatedAt = DateTime.UtcNow
};
_context.SiteSettings.Add(newSetting);
}
await _context.SaveChangesAsync();
_logger.LogInformation("Setting '{Key}' saved successfully", key);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to save setting '{Key}'", key);
throw;
}
}
}