1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-03-05 13:20:20 -05:00

Optionally show mini profiler

This commit is contained in:
Mark McDowall
2025-12-25 18:33:31 -08:00
parent 8fcab2d321
commit c3706c3c92
19 changed files with 137 additions and 25 deletions

View File

@@ -58,6 +58,8 @@
};
</script>
__MINI_PROFILER__
<% for (key in htmlWebpackPlugin.files.js) { %><script type="text/javascript" src="<%= htmlWebpackPlugin.files.js[key] %>" data-no-hash></script><% } %>
<% for (key in htmlWebpackPlugin.files.css) { %><link href="<%= htmlWebpackPlugin.files.css[key] %>" rel="stylesheet"></link><% } %>

View File

@@ -5,4 +5,6 @@ public class AppOptions
public string InstanceName { get; set; }
public string Theme { get; set; }
public bool? LaunchBrowser { get; set; }
public bool? ProfilerEnabled { get; set; }
public string ProfilerPosition { get; set; }
}

View File

@@ -69,6 +69,8 @@ namespace NzbDrone.Core.Configuration
string PostgresMainDbConnectionString { get; }
string PostgresLogDbConnectionString { get; }
bool TrustCgnatIpAddresses { get; }
bool ProfilerEnabled { get; }
string ProfilerPosition { get; }
}
public class ConfigFileProvider : IConfigFileProvider
@@ -234,6 +236,8 @@ namespace NzbDrone.Core.Configuration
? enumValue
: GetValueEnum("AuthenticationRequired", AuthenticationRequiredType.Enabled);
public bool TrustCgnatIpAddresses => _authOptions.TrustCgnatIpAddresses ?? GetValueBoolean("TrustCgnatIpAddresses", false, persist: false);
public bool AnalyticsEnabled => _logOptions.AnalyticsEnabled ?? GetValueBoolean("AnalyticsEnabled", true, persist: false);
public string Branch => _updateOptions.Branch ?? GetValue("Branch", "main").ToLowerInvariant();
@@ -312,6 +316,9 @@ namespace NzbDrone.Core.Configuration
public string SyslogLevel => _logOptions.SyslogLevel ?? GetValue("SyslogLevel", LogLevel, persist: false).ToLowerInvariant();
public bool ProfilerEnabled => _appOptions.ProfilerEnabled ?? GetValueBoolean("ProfilerEnabled", false, persist: false);
public string ProfilerPosition => _appOptions.ProfilerPosition ?? GetValue("ProfilerPosition", "bottom-right", persist: false);
public int GetValueInt(string key, int defaultValue, bool persist = true)
{
return Convert.ToInt32(GetValue(key, defaultValue, persist));
@@ -499,7 +506,5 @@ namespace NzbDrone.Core.Configuration
{
SetValue("ApiKey", GenerateApiKey());
}
public bool TrustCgnatIpAddresses => _authOptions.TrustCgnatIpAddresses ?? GetValueBoolean("TrustCgnatIpAddresses", false, persist: false);
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using Dapper;
@@ -10,7 +9,7 @@ namespace NzbDrone.Core.Datastore
{
public interface IDatabase
{
IDbConnection OpenConnection();
DbConnection OpenConnection();
Version Version { get; }
int Migration { get; }
DatabaseType DatabaseType { get; }
@@ -20,17 +19,17 @@ namespace NzbDrone.Core.Datastore
public class Database : IDatabase
{
private readonly string _databaseName;
private readonly Func<IDbConnection> _datamapperFactory;
private readonly Func<DbConnection> _datamapperFactory;
private readonly Logger _logger = NzbDroneLogger.GetLogger(typeof(Database));
public Database(string databaseName, Func<IDbConnection> datamapperFactory)
public Database(string databaseName, Func<DbConnection> datamapperFactory)
{
_databaseName = databaseName;
_datamapperFactory = datamapperFactory;
}
public IDbConnection OpenConnection()
public DbConnection OpenConnection()
{
return _datamapperFactory();
}
@@ -50,7 +49,7 @@ namespace NzbDrone.Core.Datastore
get
{
using var db = _datamapperFactory();
var dbConnection = db as DbConnection;
var dbConnection = db;
return DatabaseVersionParser.ParseServerVersion(dbConnection.ServerVersion);
}

View File

@@ -1,5 +1,5 @@
using System;
using System.Data;
using System.Data.Common;
namespace NzbDrone.Core.Datastore
{
@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Datastore
_databaseType = _database == null ? DatabaseType.SQLite : _database.DatabaseType;
}
public IDbConnection OpenConnection()
public DbConnection OpenConnection()
{
return _database.OpenConnection();
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Data;
using System.Data.Common;
using StackExchange.Profiling;
using StackExchange.Profiling.Data;
namespace NzbDrone.Core.Datastore
{
@@ -18,9 +20,16 @@ namespace NzbDrone.Core.Datastore
_databaseType = _database == null ? DatabaseType.SQLite : _database.DatabaseType;
}
public IDbConnection OpenConnection()
public DbConnection OpenConnection()
{
return _database.OpenConnection();
var connection = _database.OpenConnection();
if (_databaseType == DatabaseType.PostgreSQL)
{
return new ProfiledImplementations.NpgSqlConnection(connection, MiniProfiler.Current);
}
return new ProfiledDbConnection(connection, MiniProfiler.Current);
}
public Version Version => _database.Version;

View File

@@ -0,0 +1,15 @@
using System.Data.Common;
using StackExchange.Profiling.Data;
namespace NzbDrone.Core.Datastore;
public static class ProfiledImplementations
{
public class NpgSqlConnection : ProfiledDbConnection
{
public NpgSqlConnection(DbConnection connection, IDbProfiler profiler)
: base(connection, profiler)
{
}
}
}

View File

@@ -9,6 +9,7 @@
<PackageReference Include="MailKit" Version="4.14.1" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="10.0.1" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" />
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.5.4" />
<PackageReference Include="Openur.FFMpegCore" Version="5.4.0.31" />
<PackageReference Include="Openur.FFprobeStatic" Version="8.0.1.289" />
<PackageReference Include="Polly" Version="8.6.5" />

View File

@@ -4,6 +4,7 @@
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="10.1.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.1" />

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using DryIoc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
@@ -35,6 +36,7 @@ using Sonarr.Http.ClientSchema;
using Sonarr.Http.ErrorManagement;
using Sonarr.Http.Frontend;
using Sonarr.Http.Middleware;
using StackExchange.Profiling;
using IPNetwork = System.Net.IPNetwork;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
@@ -236,6 +238,45 @@ namespace NzbDrone.Host
});
services.AddAppAuthentication();
services.AddOptions<MiniProfilerOptions>()
.Configure<IConfigFileProvider>((options, configFileProvider) =>
{
options.RouteBasePath = "/profiler";
switch (configFileProvider.Theme)
{
case "light":
options.ColorScheme = ColorScheme.Light;
break;
case "dark":
options.ColorScheme = ColorScheme.Dark;
break;
default:
options.ColorScheme = ColorScheme.Auto;
break;
}
switch (configFileProvider.ProfilerPosition)
{
case "top-left":
options.PopupRenderPosition = RenderPosition.Left;
break;
case "top-right":
options.PopupRenderPosition = RenderPosition.Right;
break;
case "bottom-left":
options.PopupRenderPosition = RenderPosition.BottomLeft;
break;
default:
options.PopupRenderPosition = RenderPosition.BottomRight;
break;
}
options.IgnoredPaths.Add("/MediaCover");
});
services.AddMiniProfiler();
}
public void Configure(IApplicationBuilder app,
@@ -312,6 +353,7 @@ namespace NzbDrone.Host
app.UseMiddleware<BufferingMiddleware>(new List<string> { "/api/v3/command", "/api/v5/command" });
app.UseWebSockets();
app.UseMiniProfiler();
// Enable middleware to serve generated Swagger as a JSON endpoint.
if (BuildInfo.IsDebug)
@@ -325,6 +367,7 @@ namespace NzbDrone.Host
app.UseEndpoints(x =>
{
x.MapHub<MessageHub>("/signalr/messages").RequireAuthorization("SignalR");
x.MapPost("/profiler/results", context => Task.CompletedTask).RequireAuthorization("UI");
x.MapControllers();
});
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
@@ -27,9 +28,9 @@ namespace Sonarr.Http.Frontend.Mappers
protected string HtmlPath;
protected string UrlBase;
protected override Stream GetContentStream(string filePath)
protected override Stream GetContentStream(HttpContext context, string filePath)
{
var text = GetHtmlText();
var text = GetHtmlText(context);
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
@@ -39,7 +40,7 @@ namespace Sonarr.Http.Frontend.Mappers
return stream;
}
protected virtual string GetHtmlText()
protected virtual string GetHtmlText(HttpContext context)
{
if (RuntimeInfo.IsProduction && _generatedContent != null)
{

View File

@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Sonarr.Http.Frontend.Mappers
@@ -7,6 +8,6 @@ namespace Sonarr.Http.Frontend.Mappers
{
string Map(string resourceUrl);
bool CanHandle(string resourceUrl);
Task<IActionResult> GetResponse(string resourceUrl);
Task<IActionResult> GetResponse(HttpContext context, string resourceUrl);
}
}

View File

@@ -1,9 +1,12 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Http;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using StackExchange.Profiling;
namespace Sonarr.Http.Frontend.Mappers
{
@@ -38,5 +41,30 @@ namespace Sonarr.Http.Frontend.Mappers
!resourceUrl.Contains('.') &&
!resourceUrl.StartsWith("/login");
}
protected override string GetHtmlText(HttpContext context)
{
var html = base.GetHtmlText(context);
if (_configFileProvider.ProfilerEnabled)
{
var includes = MiniProfiler.Current?.RenderIncludes(context);
if (includes == null || includes.Value.IsNullOrWhiteSpace())
{
html = html.Replace("__MINI_PROFILER__", "");
}
else
{
html = html.Replace("__MINI_PROFILER__", includes.Value);
}
}
else
{
html = html.Replace("__MINI_PROFILER__", "");
}
return html;
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Http;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
@@ -33,9 +34,9 @@ namespace Sonarr.Http.Frontend.Mappers
return resourceUrl.StartsWith("/login");
}
protected override string GetHtmlText()
protected override string GetHtmlText(HttpContext context)
{
var html = base.GetHtmlText();
var html = base.GetHtmlText(context);
var theme = _configFileProvider.Theme;
html = html.Replace("_THEME_", theme);

View File

@@ -2,6 +2,7 @@ using System;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using NzbDrone.Core.MediaCover;
@@ -31,7 +32,7 @@ namespace Sonarr.Http.Frontend.Mappers
return resourceUrl.StartsWith("/MediaCoverProxy/", StringComparison.InvariantCultureIgnoreCase);
}
public async Task<IActionResult> GetResponse(string resourceUrl)
public async Task<IActionResult> GetResponse(HttpContext context, string resourceUrl)
{
var match = _regex.Match(resourceUrl);

View File

@@ -2,6 +2,7 @@ using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Net.Http.Headers;
@@ -31,7 +32,7 @@ namespace Sonarr.Http.Frontend.Mappers
public abstract bool CanHandle(string resourceUrl);
public Task<IActionResult> GetResponse(string resourceUrl)
public Task<IActionResult> GetResponse(HttpContext context, string resourceUrl)
{
var filePath = Map(resourceUrl);
@@ -42,7 +43,7 @@ namespace Sonarr.Http.Frontend.Mappers
contentType = "application/octet-stream";
}
return Task.FromResult<IActionResult>(new FileStreamResult(GetContentStream(filePath), new MediaTypeHeaderValue(contentType)
return Task.FromResult<IActionResult>(new FileStreamResult(GetContentStream(context, filePath), new MediaTypeHeaderValue(contentType)
{
Encoding = contentType == "text/plain" ? Encoding.UTF8 : null
}));
@@ -53,7 +54,7 @@ namespace Sonarr.Http.Frontend.Mappers
return Task.FromResult<IActionResult>(null);
}
protected virtual Stream GetContentStream(string filePath)
protected virtual Stream GetContentStream(HttpContext context, string filePath)
{
return File.OpenRead(filePath);
}

View File

@@ -1,4 +1,5 @@
using System.IO;
using Microsoft.AspNetCore.Http;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
@@ -27,7 +28,7 @@ namespace Sonarr.Http.Frontend.Mappers
return FilePath;
}
protected override Stream GetContentStream(string filePath)
protected override Stream GetContentStream(HttpContext context, string filePath)
{
var text = GetFileText();

View File

@@ -54,7 +54,7 @@ namespace Sonarr.Http.Frontend
if (mapper != null)
{
var result = await mapper.GetResponse(path);
var result = await mapper.GetResponse(Request.HttpContext, path);
if (result != null)
{

View File

@@ -5,6 +5,7 @@
<ItemGroup>
<PackageReference Include="FluentValidation" Version="9.5.4" />
<PackageReference Include="ImpromptuInterface" Version="8.0.6" />
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.5.4" />
<PackageReference Include="NLog" Version="5.5.1" />
</ItemGroup>
<ItemGroup>