Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled
147 lines
6.0 KiB
C#
147 lines
6.0 KiB
C#
using Microsoft.AspNetCore.DataProtection;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
using OTSSignsOrchestrator.Core.Data;
|
|
using OTSSignsOrchestrator.Core.Models.Entities;
|
|
|
|
namespace OTSSignsOrchestrator.Core.Services;
|
|
|
|
/// <summary>
|
|
/// Reads and writes typed application settings from the AppSetting table.
|
|
/// Sensitive values are encrypted/decrypted transparently via DataProtection.
|
|
/// </summary>
|
|
public class SettingsService
|
|
{
|
|
private readonly XiboContext _db;
|
|
private readonly IDataProtector _protector;
|
|
private readonly ILogger<SettingsService> _logger;
|
|
|
|
// ── Category constants ─────────────────────────────────────────────────
|
|
public const string CatGit = "Git";
|
|
public const string CatMySql = "MySql";
|
|
public const string CatSmtp = "Smtp";
|
|
public const string CatPangolin = "Pangolin";
|
|
public const string CatNfs = "Nfs";
|
|
public const string CatDefaults = "Defaults";
|
|
|
|
// ── Key constants ──────────────────────────────────────────────────────
|
|
// Git
|
|
public const string GitRepoUrl = "Git.RepoUrl";
|
|
public const string GitRepoPat = "Git.RepoPat";
|
|
|
|
// MySQL Admin
|
|
public const string MySqlHost = "MySql.Host";
|
|
public const string MySqlPort = "MySql.Port";
|
|
public const string MySqlAdminUser = "MySql.AdminUser";
|
|
public const string MySqlAdminPassword = "MySql.AdminPassword";
|
|
|
|
// SMTP
|
|
public const string SmtpServer = "Smtp.Server";
|
|
public const string SmtpPort = "Smtp.Port";
|
|
public const string SmtpUsername = "Smtp.Username";
|
|
public const string SmtpPassword = "Smtp.Password";
|
|
public const string SmtpUseTls = "Smtp.UseTls";
|
|
public const string SmtpUseStartTls = "Smtp.UseStartTls";
|
|
public const string SmtpRewriteDomain = "Smtp.RewriteDomain";
|
|
public const string SmtpHostname = "Smtp.Hostname";
|
|
public const string SmtpFromLineOverride = "Smtp.FromLineOverride";
|
|
|
|
// Pangolin
|
|
public const string PangolinEndpoint = "Pangolin.Endpoint";
|
|
|
|
// NFS
|
|
public const string NfsServer = "Nfs.Server";
|
|
public const string NfsExport = "Nfs.Export";
|
|
public const string NfsExportFolder = "Nfs.ExportFolder";
|
|
public const string NfsOptions = "Nfs.Options";
|
|
|
|
// Instance Defaults
|
|
public const string DefaultCmsImage = "Defaults.CmsImage";
|
|
public const string DefaultNewtImage = "Defaults.NewtImage";
|
|
public const string DefaultMemcachedImage = "Defaults.MemcachedImage";
|
|
public const string DefaultQuickChartImage = "Defaults.QuickChartImage";
|
|
public const string DefaultCmsServerNameTemplate = "Defaults.CmsServerNameTemplate";
|
|
public const string DefaultThemeHostPath = "Defaults.ThemeHostPath";
|
|
public const string DefaultMySqlDbTemplate = "Defaults.MySqlDbTemplate";
|
|
public const string DefaultMySqlUserTemplate = "Defaults.MySqlUserTemplate";
|
|
public const string DefaultPhpPostMaxSize = "Defaults.PhpPostMaxSize";
|
|
public const string DefaultPhpUploadMaxFilesize = "Defaults.PhpUploadMaxFilesize";
|
|
public const string DefaultPhpMaxExecutionTime = "Defaults.PhpMaxExecutionTime";
|
|
|
|
public SettingsService(
|
|
XiboContext db,
|
|
IDataProtectionProvider dataProtection,
|
|
ILogger<SettingsService> logger)
|
|
{
|
|
_db = db;
|
|
_protector = dataProtection.CreateProtector("OTSSignsOrchestrator.Settings");
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <summary>Get a single setting value, decrypting if sensitive.</summary>
|
|
public async Task<string?> GetAsync(string key)
|
|
{
|
|
var setting = await _db.AppSettings.FindAsync(key);
|
|
if (setting == null) return null;
|
|
return setting.IsSensitive && setting.Value != null
|
|
? Unprotect(setting.Value)
|
|
: setting.Value;
|
|
}
|
|
|
|
/// <summary>Get a setting with a fallback default.</summary>
|
|
public async Task<string> GetAsync(string key, string defaultValue)
|
|
=> await GetAsync(key) ?? defaultValue;
|
|
|
|
/// <summary>Set a single setting, encrypting if sensitive.</summary>
|
|
public async Task SetAsync(string key, string? value, string category, bool isSensitive = false)
|
|
{
|
|
var setting = await _db.AppSettings.FindAsync(key);
|
|
if (setting == null)
|
|
{
|
|
setting = new AppSetting { Key = key, Category = category, IsSensitive = isSensitive };
|
|
_db.AppSettings.Add(setting);
|
|
}
|
|
|
|
setting.Value = isSensitive && value != null ? _protector.Protect(value) : value;
|
|
setting.IsSensitive = isSensitive;
|
|
setting.Category = category;
|
|
setting.UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
/// <summary>Save multiple settings in a single transaction.</summary>
|
|
public async Task SaveManyAsync(IEnumerable<(string Key, string? Value, string Category, bool IsSensitive)> settings)
|
|
{
|
|
foreach (var (key, value, category, isSensitive) in settings)
|
|
await SetAsync(key, value, category, isSensitive);
|
|
|
|
await _db.SaveChangesAsync();
|
|
_logger.LogInformation("Saved {Count} setting(s)",
|
|
settings is ICollection<(string, string?, string, bool)> c ? c.Count : -1);
|
|
}
|
|
|
|
/// <summary>Get all settings in a category (values decrypted).</summary>
|
|
public async Task<Dictionary<string, string?>> GetCategoryAsync(string category)
|
|
{
|
|
var settings = await _db.AppSettings
|
|
.Where(s => s.Category == category)
|
|
.ToListAsync();
|
|
|
|
return settings.ToDictionary(
|
|
s => s.Key,
|
|
s => s.IsSensitive && s.Value != null ? Unprotect(s.Value) : s.Value);
|
|
}
|
|
|
|
private string? Unprotect(string protectedValue)
|
|
{
|
|
try
|
|
{
|
|
return _protector.Unprotect(protectedValue);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "Failed to unprotect setting value — returning null");
|
|
return null;
|
|
}
|
|
}
|
|
}
|