Initial commit: Lagerbestand Blazor Projekt
This commit is contained in:
commit
cba9414e92
105 files changed
+36431
No files matched your search
@@ -0,0 +1,452 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user