VEU.WathBody/3-app/2-cli/Server.WatchBody.Web/Config/ServerConfig.cs

92 lines
4.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Text.Json;
using Server.WatchBody.Core.Metrics;
using Server.WatchBody.Core.Programs;
namespace Server.WatchBody.Web.Config;
/// <summary>Атлет из конфига (Api.Athletes).</summary>
public class ConfigAthlete
{
public string Name { get; set; } = string.Empty;
public string ApiKey { get; set; } = string.Empty;
}
/// <summary>
/// Конфигурация сервера (appsettings.json; tech-requirements 4.2).
/// Ошибка валидации — фатальна: лог + stderr, код возврата 1.
/// </summary>
public class ServerConfig
{
public string ApiKey { get; set; } = string.Empty;
public List<ConfigAthlete> Athletes { get; set; } = [];
public string DbConnectionString { get; set; } = string.Empty;
/// <summary>Каталог миграций (--mode migrate, З-36); по умолчанию —
/// migrations рядом с исполняемым файлом.</summary>
public string MigrationsDir { get; set; } =
Path.Combine(AppContext.BaseDirectory, "migrations");
/// <summary>Пороги красных флагов (Safety.*, Р3-А; MetricsEngine).</summary>
public MetricsConfig Safety { get; set; } = new();
/// <summary>Пороги правил прогрессии/делоада/перерывов (Progression.*, m05).</summary>
public ProgressionConfig Progression { get; set; } = new();
/// <summary>Загрузка из json-файла (путь — из WATCHBODY_APPSETTINGS либо appsettings.json рядом).</summary>
public static ServerConfig Load(string path)
{
var json = File.ReadAllText(path);
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
var result = new ServerConfig();
if (root.TryGetProperty("Api", out var api))
{
if (api.TryGetProperty("Key", out var key)) result.ApiKey = key.GetString() ?? string.Empty;
if (api.TryGetProperty("Athletes", out var athletes) && athletes.ValueKind == JsonValueKind.Array)
{
foreach (var a in athletes.EnumerateArray())
{
result.Athletes.Add(new ConfigAthlete
{
Name = a.TryGetProperty("name", out var n) ? n.GetString() ?? string.Empty : string.Empty,
ApiKey = a.TryGetProperty("apiKey", out var k) ? k.GetString() ?? string.Empty : string.Empty,
});
}
}
}
if (root.TryGetProperty("Db", out var db))
{
if (db.TryGetProperty("ConnectionString", out var cs))
result.DbConnectionString = cs.GetString() ?? string.Empty;
if (db.TryGetProperty("MigrationsDir", out var md) && md.ValueKind == JsonValueKind.String)
result.MigrationsDir = md.GetString() ?? result.MigrationsDir;
}
result.Safety = MetricsConfig.Load(root);
result.Progression = ProgressionConfig.Load(root);
return result;
}
/// <summary>Список ошибок валидации (spec 01-config-bootstrap).</summary>
public List<string> Validate()
{
var errors = new List<string>();
if (string.IsNullOrWhiteSpace(ApiKey))
errors.Add("Api.Key пуст");
var seen = new HashSet<string>(StringComparer.Ordinal);
for (var i = 0; i < Athletes.Count; i++)
{
var a = Athletes[i];
if (string.IsNullOrWhiteSpace(a.Name)) errors.Add($"Api.Athletes[{i}].name пуст");
if (string.IsNullOrWhiteSpace(a.ApiKey)) errors.Add($"Api.Athletes[{i}].apiKey пуст");
else if (a.ApiKey == ApiKey) errors.Add($"Api.Athletes[{i}].apiKey совпадает с Api.Key");
else if (!seen.Add(a.ApiKey)) errors.Add($"дубликат apiKey у Api.Athletes[{i}]");
}
if (string.IsNullOrWhiteSpace(DbConnectionString))
errors.Add("Db.ConnectionString пуст");
errors.AddRange(Safety.Validate());
errors.AddRange(Progression.Validate());
return errors;
}
}