453 lines
20 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|