using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; namespace TaxBaik.Web.Controllers; [ApiController] [Route("api/client-logs")] [AllowAnonymous] [EnableRateLimiting("client-logs")] public class ClientLogsController(ILogger logger) : ControllerBase { [HttpPost] public IActionResult Post([FromBody] ClientLogEntry entry) { if (string.IsNullOrWhiteSpace(entry.Message)) { return BadRequest(); } var logMessage = "ClientLog {Level} {Source} {Message} Url={Url} Route={Route} Screen={Screen} Feature={Feature} Action={Action} Step={Step} Entity={Entity} EntityId={EntityId} DataKey={DataKey} BuildVersion={BuildVersion} UserAgent={UserAgent} Stack={Stack}"; var args = new object?[] { entry.Level ?? "error", entry.Source ?? "unknown", entry.Message, entry.Url ?? string.Empty, entry.Route ?? string.Empty, entry.Screen ?? string.Empty, entry.Feature ?? string.Empty, entry.Action ?? string.Empty, entry.Step ?? string.Empty, entry.Entity ?? string.Empty, entry.EntityId ?? string.Empty, entry.DataKey ?? string.Empty, entry.BuildVersion ?? string.Empty, entry.UserAgent ?? string.Empty, entry.Stack ?? string.Empty }; // Client errors (level: error) → Telegram alert // Client warnings (level: warning/info) → Log file only if (entry.Level?.Equals("error", StringComparison.OrdinalIgnoreCase) ?? true) { logger.LogError(logMessage, args); } else { logger.LogWarning(logMessage, args); } return Ok(); } } public sealed class ClientLogEntry { public string? Level { get; set; } public string? Source { get; set; } public string? Message { get; set; } public string? Url { get; set; } public string? Route { get; set; } public string? Screen { get; set; } public string? Feature { get; set; } public string? Action { get; set; } public string? Step { get; set; } public string? Entity { get; set; } public string? EntityId { get; set; } public string? DataKey { get; set; } public string? BuildVersion { get; set; } public string? UserAgent { get; set; } public string? Stack { get; set; } }