mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Add initial management interface support.
This commit is contained in:
parent
82d27e6461
commit
c5d900b164
14
Jellyfin.Api/Attributes/ManagementAttribute.cs
Normal file
14
Jellyfin.Api/Attributes/ManagementAttribute.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Jellyfin.Api.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that the marked controller or method is only accessible via the management interface.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class ManagementAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
72
Jellyfin.Api/Controllers/ManagementController.cs
Normal file
72
Jellyfin.Api/Controllers/ManagementController.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Attributes;
|
||||
using Jellyfin.Api.Constants;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// The management controller.
|
||||
/// </summary>
|
||||
[Management]
|
||||
public class ManagementController : BaseJellyfinApiController
|
||||
{
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly INetworkManager _network;
|
||||
private readonly ILogger<ManagementController> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManagementController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="serverConfigurationManager">Instance of <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
/// <param name="appHost">Instance of <see cref="IServerApplicationHost"/> interface.</param>
|
||||
/// <param name="fileSystem">Instance of <see cref="IFileSystem"/> interface.</param>
|
||||
/// <param name="network">Instance of <see cref="INetworkManager"/> interface.</param>
|
||||
/// <param name="logger">Instance of <see cref="ILogger{SystemController}"/> interface.</param>
|
||||
public ManagementController(
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IServerApplicationHost appHost,
|
||||
IFileSystem fileSystem,
|
||||
INetworkManager network,
|
||||
ILogger<ManagementController> logger)
|
||||
{
|
||||
_appPaths = serverConfigurationManager.ApplicationPaths;
|
||||
_appHost = appHost;
|
||||
_fileSystem = fileSystem;
|
||||
_network = network;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about the server.
|
||||
/// </summary>
|
||||
/// <response code="200">Information retrieved.</response>
|
||||
/// <returns>A <see cref="SystemInfo"/> with info about the system.</returns>
|
||||
[HttpGet("Test")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult<int> GetTest()
|
||||
{
|
||||
return 123456; // secret
|
||||
}
|
||||
}
|
||||
}
|
@ -159,6 +159,9 @@ namespace Jellyfin.Server.Extensions
|
||||
})
|
||||
.AddMvc(opts =>
|
||||
{
|
||||
// Seperate the management routes and the general ones.
|
||||
opts.Filters.Add(typeof(ManagementInterfaceFilter));
|
||||
|
||||
// Allow requester to change between camelCase and PascalCase
|
||||
opts.RespectBrowserAcceptHeader = true;
|
||||
|
||||
|
96
Jellyfin.Server/Filters/ManagementInterfaceFilter.cs
Normal file
96
Jellyfin.Server/Filters/ManagementInterfaceFilter.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Attributes;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Jellyfin.Server.Filters
|
||||
{
|
||||
internal class ManagementInterfaceFilter : IActionFilter
|
||||
{
|
||||
private readonly List<(IPAddress Host, int Port)> managementEndpoints;
|
||||
|
||||
public ManagementInterfaceFilter(IConfiguration appConfig)
|
||||
{
|
||||
managementEndpoints = new List<(IPAddress Host, int Port)>();
|
||||
|
||||
if (appConfig.UseManagementInterface())
|
||||
{
|
||||
var socketPath = appConfig.GetManagementInterfaceSocketPath();
|
||||
var localhostPort = appConfig.GetManagementInterfaceLocalhostPort();
|
||||
bool useDefault = true;
|
||||
if (!string.IsNullOrEmpty(socketPath))
|
||||
{
|
||||
// TODO make this work, no idea where to get the SocketAddress or something similar
|
||||
managementEndpoints.Add((IPAddress.Any, 0));
|
||||
}
|
||||
|
||||
if (localhostPort > 0)
|
||||
{
|
||||
managementEndpoints.Add((IPAddress.Loopback, localhostPort));
|
||||
managementEndpoints.Add((IPAddress.IPv6Loopback, localhostPort));
|
||||
}
|
||||
|
||||
if (useDefault)
|
||||
{
|
||||
managementEndpoints.Add((IPAddress.Loopback, ServerConfiguration.DefaultManagementPort));
|
||||
managementEndpoints.Add((IPAddress.IPv6Loopback, ServerConfiguration.DefaultManagementPort));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
var isManagementRoute = IsManagementRoute(context);
|
||||
var isManagementListenEntrypoint = IsManagementListenEntrypoint(context);
|
||||
|
||||
if ((isManagementRoute && !isManagementListenEntrypoint) || (!isManagementRoute && isManagementListenEntrypoint))
|
||||
{
|
||||
context.Result = new NotFoundResult();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsManagementRoute(ActionExecutingContext context)
|
||||
{
|
||||
return HasAttribute<ManagementAttribute>(context);
|
||||
}
|
||||
|
||||
private bool HasAttribute<T>(ActionExecutingContext context)
|
||||
{
|
||||
var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
|
||||
if (controllerActionDescriptor != null)
|
||||
{
|
||||
// Check if the attribute exists on the action method
|
||||
if (controllerActionDescriptor.MethodInfo?.GetCustomAttributes(inherit: true)?.Any(a => a.GetType().Equals(typeof(T))) ?? false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the attribute exists on the controller
|
||||
if (controllerActionDescriptor.ControllerTypeInfo?.GetCustomAttributes(typeof(T), true)?.Any() ?? false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsManagementListenEntrypoint(ActionExecutingContext context)
|
||||
{
|
||||
return managementEndpoints.Contains((context.HttpContext.Connection.LocalIpAddress, context.HttpContext.Connection.LocalPort));
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ using Emby.Server.Implementations.Networking;
|
||||
using Jellyfin.Api.Controllers;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@ -372,6 +373,39 @@ namespace Jellyfin.Server
|
||||
options.ListenUnixSocket(socketPath);
|
||||
_logger.LogInformation("Kestrel listening to unix socket {SocketPath}", socketPath);
|
||||
}
|
||||
|
||||
// Enable the Management Interface
|
||||
if (startupConfig.UseManagementInterface())
|
||||
{
|
||||
var socketPath = startupConfig.GetManagementInterfaceSocketPath();
|
||||
var localhostPort = startupConfig.GetManagementInterfaceLocalhostPort();
|
||||
bool useDefault = true;
|
||||
if (!string.IsNullOrEmpty(socketPath))
|
||||
{
|
||||
// Workaround for https://github.com/aspnet/AspNetCore/issues/14134
|
||||
if (File.Exists(socketPath))
|
||||
{
|
||||
File.Delete(socketPath);
|
||||
}
|
||||
|
||||
options.ListenUnixSocket(socketPath);
|
||||
_logger.LogInformation("Management interface listening to unix socket {SocketPath}", socketPath);
|
||||
useDefault = false;
|
||||
}
|
||||
|
||||
if (localhostPort > 0)
|
||||
{
|
||||
options.ListenLocalhost(localhostPort);
|
||||
_logger.LogInformation("Management interface listening to localhost on port {LocalhostPort}", localhostPort);
|
||||
useDefault = false;
|
||||
}
|
||||
|
||||
if (useDefault)
|
||||
{
|
||||
options.ListenLocalhost(ServerConfiguration.DefaultManagementPort);
|
||||
_logger.LogInformation("Management interface listening to localhost on default port {DefaultManagementPort}", ServerConfiguration.DefaultManagementPort);
|
||||
}
|
||||
}
|
||||
})
|
||||
.ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(commandLineOpts, appPaths, startupConfig))
|
||||
.UseSerilog()
|
||||
|
@ -49,6 +49,21 @@ namespace MediaBrowser.Controller.Extensions
|
||||
/// </summary>
|
||||
public const string UnixSocketPathKey = "kestrel:socketPath";
|
||||
|
||||
/// <summary>
|
||||
/// The key for a setting that indicates whether the management interface should be enabled.
|
||||
/// </summary>
|
||||
public const string UseManagementInterfaceKey = "management:enabled";
|
||||
|
||||
/// <summary>
|
||||
/// The key for a setting that indicates whether the management interface should listen on localhost.
|
||||
/// </summary>
|
||||
public const string ManagementInterfaceLocalhostPortKey = "management:port";
|
||||
|
||||
/// <summary>
|
||||
/// The key for the management interface socket path.
|
||||
/// </summary>
|
||||
public const string ManagementInterfaceSocketPathKey = "management:socket";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the application should host static web content from the <see cref="IConfiguration"/>.
|
||||
/// </summary>
|
||||
@ -97,5 +112,29 @@ namespace MediaBrowser.Controller.Extensions
|
||||
/// <returns>The unix socket path.</returns>
|
||||
public static string GetUnixSocketPath(this IConfiguration configuration)
|
||||
=> configuration[UnixSocketPathKey];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether kestrel should enable the management interface from the <see cref="IConfiguration" />.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration to read the setting from.</param>
|
||||
/// <returns><c>true</c> if kestrel should bind to a unix socket, otherwise <c>false</c>.</returns>
|
||||
public static bool UseManagementInterface(this IConfiguration configuration)
|
||||
=> configuration.GetValue<bool>(UseManagementInterfaceKey);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the localhost port for the management interface from the <see cref="IConfiguration" />.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration to read the setting from.</param>
|
||||
/// <returns>The management interface address.</returns>
|
||||
public static int GetManagementInterfaceLocalhostPort(this IConfiguration configuration)
|
||||
=> configuration.GetValue<int>(ManagementInterfaceLocalhostPortKey);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path for the management interface socket from the <see cref="IConfiguration" />.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration to read the setting from.</param>
|
||||
/// <returns>The management interface socket path.</returns>
|
||||
public static string GetManagementInterfaceSocketPath(this IConfiguration configuration)
|
||||
=> configuration[ManagementInterfaceSocketPathKey];
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace MediaBrowser.Model.Configuration
|
||||
public class ServerConfiguration : BaseApplicationConfiguration
|
||||
{
|
||||
public const int DefaultHttpPort = 8096;
|
||||
public const int DefaultManagementPort = 12000;
|
||||
public const int DefaultHttpsPort = 8920;
|
||||
private string _baseUrl;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user