using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Net.Mime; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using MediaBrowser.Common.Api; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.System; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace Jellyfin.Api.Controllers; /// /// The system controller. /// public class SystemController : BaseJellyfinApiController { private readonly ILogger _logger; private readonly IServerApplicationHost _appHost; private readonly IApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; private readonly INetworkManager _networkManager; private readonly ISystemManager _systemManager; /// /// Initializes a new instance of the class. /// /// Instance of interface. /// Instance of interface. /// Instance of interface. /// Instance of interface. /// Instance of interface. /// Instance of interface. public SystemController( ILogger logger, IServerApplicationHost appHost, IServerApplicationPaths appPaths, IFileSystem fileSystem, INetworkManager networkManager, ISystemManager systemManager) { _logger = logger; _appHost = appHost; _appPaths = appPaths; _fileSystem = fileSystem; _networkManager = networkManager; _systemManager = systemManager; } /// /// Gets information about the server. /// /// Information retrieved. /// User does not have permission to retrieve information. /// A with info about the system. [HttpGet("Info")] [Authorize(Policy = Policies.FirstTimeSetupOrIgnoreParentalControl)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult GetSystemInfo() => _systemManager.GetSystemInfo(Request); /// /// Gets public information about the server. /// /// Information retrieved. /// A with public info about the system. [HttpGet("Info/Public")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetPublicSystemInfo() => _systemManager.GetPublicSystemInfo(Request); /// /// Pings the system. /// /// Information retrieved. /// The server name. [HttpGet("Ping", Name = "GetPingSystem")] [HttpPost("Ping", Name = "PostPingSystem")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult PingSystem() => _appHost.Name; /// /// Restarts the application. /// /// Server restarted. /// User does not have permission to restart server. /// No content. Server restarted. [HttpPost("Restart")] [Authorize(Policy = Policies.LocalAccessOrRequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult RestartApplication() { _systemManager.Restart(); return NoContent(); } /// /// Shuts down the application. /// /// Server shut down. /// User does not have permission to shutdown server. /// No content. Server shut down. [HttpPost("Shutdown")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult ShutdownApplication() { _systemManager.Shutdown(); return NoContent(); } /// /// Gets a list of available server log files. /// /// Information retrieved. /// User does not have permission to get server logs. /// An array of with the available log files. [HttpGet("Logs")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult GetServerLogs() { IEnumerable files; try { files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath, new[] { ".txt", ".log" }, true, false); } catch (IOException ex) { _logger.LogError(ex, "Error getting logs"); files = Enumerable.Empty(); } var result = files.Select(i => new LogFile { DateCreated = _fileSystem.GetCreationTimeUtc(i), DateModified = _fileSystem.GetLastWriteTimeUtc(i), Name = i.Name, Size = i.Length }) .OrderByDescending(i => i.DateModified) .ThenByDescending(i => i.DateCreated) .ThenBy(i => i.Name) .ToArray(); return result; } /// /// Gets information about the request endpoint. /// /// Information retrieved. /// User does not have permission to get endpoint information. /// with information about the endpoint. [HttpGet("Endpoint")] [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult GetEndpointInfo() { return new EndPointInfo { IsLocal = HttpContext.IsLocal(), IsInNetwork = _networkManager.IsInLocalNetwork(HttpContext.GetNormalizedRemoteIP()) }; } /// /// Gets a log file. /// /// The name of the log file to get. /// Log file retrieved. /// User does not have permission to get log files. /// The log file. [HttpGet("Logs/Log")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesFile(MediaTypeNames.Text.Plain)] public ActionResult GetLogFile([FromQuery, Required] string name) { var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath) .First(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase)); // For older files, assume fully static var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite; FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); return File(stream, "text/plain; charset=utf-8"); } /// /// Gets wake on lan information. /// /// Information retrieved. /// An with the WakeOnLan infos. [HttpGet("WakeOnLanInfo")] [Authorize] [Obsolete("This endpoint is obsolete.")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetWakeOnLanInfo() { var result = _networkManager.GetMacAddresses() .Select(i => new WakeOnLanInfo(i)); return Ok(result); } }