using Management.Data; using Management.Security; using Microsoft.AspNetCore.Mvc; using System.Text.Json; namespace Management.Controllers.Admin; /// /// Admin endpoint for manually triggering metric sync. /// /// ENDPOINTS: /// POST /api/admin/sync/metrics/{clientId} - Sync a specific client /// POST /api/admin/sync/metrics - Sync all active clients /// /// Proxies to Gateway /api/sync/metrics/* using an internal service key. /// Gateway owns provider connectivity; Management owns the trigger surface. /// Configure via GATEWAY_URL + INTERNAL_SERVICE_KEY env vars. /// [ApiController] [Route("api/admin/sync")] public sealed class AdminMetricSyncController : AdminControllerBase { private readonly IHttpClientFactory _http; private readonly IConfiguration _config; public AdminMetricSyncController( SqlService sql, ClientContext client, ILogger log, IHttpClientFactory http, IConfiguration config) : base(sql, client, log) { _http = http; _config = config; } /// /// Trigger metric sync for a specific client. /// [HttpPost("metrics/{clientId}")] public async Task SyncClient( string clientId, [FromQuery] string? startDate, [FromQuery] string? endDate, CancellationToken ct) { Logger.LogInformation("[Admin] MetricSync triggered for client {ClientId} | By={User}", clientId, Client.Email); return await ProxyToGateway($"metrics/{Uri.EscapeDataString(clientId)}", startDate, endDate, ct); } /// /// Trigger metric sync for all active clients. /// [HttpPost("metrics")] public async Task SyncAll( [FromQuery] string? startDate, [FromQuery] string? endDate, CancellationToken ct) { Logger.LogInformation("[Admin] MetricSync ALL clients triggered | By={User}", Client.Email); return await ProxyToGateway("metrics/all", startDate, endDate, ct); } // ──────────────────────────────────────────────── // Proxy helper // ──────────────────────────────────────────────── private async Task ProxyToGateway( string path, string? startDate, string? endDate, CancellationToken ct) { var gatewayUrl = _config["GATEWAY_URL"]?.TrimEnd('/'); var serviceKey = _config["INTERNAL_SERVICE_KEY"]; if (string.IsNullOrWhiteSpace(gatewayUrl)) return StatusCode(500, new { ok = false, error = "GATEWAY_URL not configured" }); if (string.IsNullOrWhiteSpace(serviceKey)) return StatusCode(500, new { ok = false, error = "INTERNAL_SERVICE_KEY not configured" }); var qs = new List(); if (!string.IsNullOrWhiteSpace(startDate)) qs.Add($"startDate={Uri.EscapeDataString(startDate)}"); if (!string.IsNullOrWhiteSpace(endDate)) qs.Add($"endDate={Uri.EscapeDataString(endDate)}"); var url = $"{gatewayUrl}/api/sync/{path}"; if (qs.Count > 0) url += "?" + string.Join("&", qs); try { var client = _http.CreateClient(); using var req = new HttpRequestMessage(HttpMethod.Post, url); req.Headers.Add("X-Service-Key", serviceKey); using var resp = await client.SendAsync(req, ct); var body = await resp.Content.ReadAsStringAsync(ct); Logger.LogInformation("[Admin] MetricSync Gateway response {Status} | Path={Path}", (int)resp.StatusCode, path); return resp.IsSuccessStatusCode ? Content(body, "application/json") : StatusCode((int)resp.StatusCode, new { ok = false, error = $"Gateway returned {(int)resp.StatusCode}", detail = body }); } catch (Exception ex) { Logger.LogError(ex, "[Admin] MetricSync proxy error | Path={Path}", path); return StatusCode(500, new { ok = false, error = "Sync trigger failed", detail = ex.Message }); } } }