using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
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.
///
[Route("/System")]
public class SystemController : BaseJellyfinApiController
{
private readonly IServerApplicationHost _appHost;
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly INetworkManager _network;
private readonly ILogger _logger;
///
/// Initializes a new instance of the class.
///
/// Instance of interface.
/// Instance of interface.
/// Instance of interface.
/// Instance of interface.
/// Instance of interface.
public SystemController(
IServerConfigurationManager serverConfigurationManager,
IServerApplicationHost appHost,
IFileSystem fileSystem,
INetworkManager network,
ILogger logger)
{
_appPaths = serverConfigurationManager.ApplicationPaths;
_appHost = appHost;
_fileSystem = fileSystem;
_network = network;
_logger = logger;
}
///
/// Gets information about the server.
///
/// Information retrieved.
/// A with info about the system.
[HttpGet("Info")]
// TODO: Authorize EscapeParentalControl
[Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> GetSystemInfo()
{
return await _appHost.GetSystemInfo(CancellationToken.None).ConfigureAwait(false);
}
///
/// Gets public information about the server.
///
/// Information retrieved.
/// A with public info about the system.
[HttpGet("Info/Public")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> GetPublicSystemInfo()
{
return await _appHost.GetPublicSystemInfo(CancellationToken.None).ConfigureAwait(false);
}
///
/// Pings the system.
///
/// Information retrieved.
/// The server name.
[HttpGet("Ping")]
[HttpPost("Ping")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult PingSystem()
{
return _appHost.Name;
}
///
/// Restarts the application.
///
/// Server restarted.
/// No content. Server restarted.
[HttpPost("Restart")]
// TODO: Authorize AllowLocal = true
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult RestartApplication()
{
Task.Run(async () =>
{
await Task.Delay(100).ConfigureAwait(false);
_appHost.Restart();
});
return NoContent();
}
///
/// Shuts down the application.
///
/// Server shut down.
/// No content. Server shut down.
[HttpPost("Shutdown")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult ShutdownApplication()
{
Task.Run(async () =>
{
await Task.Delay(100).ConfigureAwait(false);
await _appHost.Shutdown().ConfigureAwait(false);
});
return NoContent();
}
///
/// Gets a list of available server log files.
///
/// Information retrieved.
/// An array of with the available log files.
[HttpGet("Logs")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
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.
/// with information about the endpoint.
[HttpGet("Endpoint")]
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetEndpointInfo()
{
return new EndPointInfo
{
IsLocal = Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress),
IsInNetwork = _network.IsInLocalNetwork(Request.HttpContext.Connection.RemoteIpAddress.ToString())
};
}
///
/// Gets a log file.
///
/// The name of the log file to get.
/// Log file retrieved.
/// The log file.
[HttpGet("Logs/Log")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
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);
return File(stream, "text/plain");
}
///
/// Gets wake on lan information.
///
/// Information retrieved.
/// An with the WakeOnLan infos.
[HttpGet("WakeOnLanInfo")]
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetWakeOnLanInfo()
{
var result = _appHost.GetWakeOnLanInfo();
return Ok(result);
}
}
}