using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using Sockeye.Models; using Sockeye.Util; using Sockeye.Api.ControllerHelpers; using Sockeye.Biz; using System.Threading.Tasks; using System; using System.IO; namespace Sockeye.Api.Controllers { /// /// Log files controller /// [ApiController] [ApiVersion("8.0")] [Route("api/v{version:apiVersion}/log-file")] //[Produces("application/json")] [Authorize] public class LogFilesController : ControllerBase { private readonly AyContext ct; private readonly ILogger log; private readonly ApiServerState serverState; /// /// ctor /// /// /// /// public LogFilesController(AyContext dbcontext, ILogger logger, ApiServerState apiServerState) { ct = dbcontext; log = logger; serverState = apiServerState; } /// /// Get server log /// /// /// A single log file in plain text [HttpGet("{logname}")] public ActionResult GetLog([FromRoute] string logname) { //NOTE: this route deliberately open even when server closed as a troubleshooting measure if (!Authorized.HasReadFullRole(HttpContext.Items, SockType.LogFile)) { return StatusCode(403, new ApiNotAuthorizedResponse()); } if (!ModelState.IsValid) { return BadRequest(new ApiErrorResponse(ModelState)); } //stream the file contents into a json object and return //build the full path from the log file name and defined path var logFilePath = System.IO.Path.Combine(ServerBootConfig.SOCKEYE_LOG_PATH, logname); //does file exist? if (!System.IO.File.Exists(logFilePath)) { return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); } //Log //nlog update now locks file for writing so needs to be opened this way FileStreamOptions fso = new FileStreamOptions(); fso.Access = FileAccess.Read; fso.Mode = FileMode.Open; fso.Share = FileShare.ReadWrite; using (StreamReader sr = new StreamReader(logFilePath, fso)) { return Content(sr.ReadToEnd()); } //return Content(System.IO.File.ReadAllText(logFilePath)); } /// /// Get list of operations logs /// /// [HttpGet()] public ActionResult ListLogs() { //NOTE: this route deliberately open even when server closed as a troubleshooting measure if (!Authorized.HasReadFullRole(HttpContext.Items, SockType.LogFile)) { return StatusCode(403, new ApiNotAuthorizedResponse()); } if (!ModelState.IsValid) { return BadRequest(new ApiErrorResponse(ModelState)); } //Iterate all log files and build return var files = System.IO.Directory.GetFiles(ServerBootConfig.SOCKEYE_LOG_PATH, "log-sockeye*.txt"); var ret = files.Where(z => !z.EndsWith("ayanova.txt")).OrderByDescending(z => z).Select(z => System.IO.Path.GetFileName(z)).ToList(); ret.Insert(0, "log-sockeye.txt"); return Ok(ApiOkResponse.Response(ret)); } /// /// Download log /// /// /// download token /// A single log file [AllowAnonymous] [HttpGet("download/{logname}")] public async Task DownloadLog([FromRoute] string logname, [FromQuery] string t) { //NOTE: this route deliberately open even when server closed as a troubleshooting measure if (!ModelState.IsValid) return BadRequest(new ApiErrorResponse(ModelState)); var user = await UserBiz.ValidateDownloadTokenAndReturnUserAsync(t, ct); if (user == null) { await Task.Delay(Sockeye.Util.ServerBootConfig.FAILED_AUTH_DELAY);//DOS protection return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED)); } if (!Authorized.HasReadFullRole(user.Roles, SockType.LogFile)) return StatusCode(403, new ApiNotAuthorizedResponse()); var logFilePath = System.IO.Path.Combine(ServerBootConfig.SOCKEYE_LOG_PATH, logname); if (!System.IO.File.Exists(logFilePath)) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); //Note: this works with new Nlog file locking for write so it must be opening it as read and shareable return PhysicalFile(logFilePath, "text/plain", $"{DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss")}-{logname}"); } //------------ } }