feat: Add main application views and structure
Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled
Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled
- Implemented CreateInstanceView for creating new instances. - Added HostsView for managing SSH hosts with CRUD operations. - Created InstancesView for displaying and managing instances. - Developed LogsView for viewing operation logs. - Introduced SecretsView for managing secrets associated with hosts. - Established SettingsView for configuring application settings. - Created MainWindow as the main application window with navigation. - Added app manifest and configuration files for logging and settings.
This commit is contained in:
147
OTSSignsOrchestrator.Core/Services/SettingsService.cs
Normal file
147
OTSSignsOrchestrator.Core/Services/SettingsService.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
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 CatCifs = "Cifs";
|
||||
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";
|
||||
|
||||
// CIFS
|
||||
public const string CifsServer = "Cifs.Server";
|
||||
public const string CifsShareBasePath = "Cifs.ShareBasePath";
|
||||
public const string CifsUsername = "Cifs.Username";
|
||||
public const string CifsPassword = "Cifs.Password";
|
||||
public const string CifsOptions = "Cifs.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user