Compare commits

..

1 Commits

Author SHA1 Message Date
Mark McDowall
e739a93a2a Fixed: Loading calendar on older browsers
(cherry picked from commit edfc12e27a00fd927df0de6ccb3961efe8f5dc3b)
2024-12-17 05:02:57 +00:00
18 changed files with 681 additions and 868 deletions

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '0.4.8'
majorVersion: '0.4.6'
minorVersion: $[counter('minorVersion', 1)]
readarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(readarrVersion)'

View File

@@ -182,7 +182,7 @@ module.exports = (env) => {
loose: true,
debug: false,
useBuiltIns: 'entry',
corejs: '3.39'
corejs: 3
}
]
]

View File

@@ -8,6 +8,7 @@ window.console.debug = window.console.debug || function() {};
window.console.warn = window.console.warn || function() {};
window.console.assert = window.console.assert || function() {};
// TODO: Remove in v5, well suppoprted in browsers
if (!String.prototype.startsWith) {
Object.defineProperty(String.prototype, 'startsWith', {
enumerable: false,
@@ -20,6 +21,7 @@ if (!String.prototype.startsWith) {
});
}
// TODO: Remove in v5, well suppoprted in browsers
if (!String.prototype.endsWith) {
Object.defineProperty(String.prototype, 'endsWith', {
enumerable: false,
@@ -34,8 +36,14 @@ if (!String.prototype.endsWith) {
});
}
// TODO: Remove in v5, use `includes` instead
if (!('contains' in String.prototype)) {
String.prototype.contains = function(str, startIndex) {
return String.prototype.indexOf.call(this, str, startIndex) !== -1;
};
}
// For Firefox ESR 115 support
if (!Object.groupBy) {
import('core-js/actual/object/group-by');
}

View File

@@ -25,10 +25,10 @@
"defaults"
],
"dependencies": {
"@fortawesome/fontawesome-free": "6.7.1",
"@fortawesome/fontawesome-svg-core": "6.7.1",
"@fortawesome/free-regular-svg-icons": "6.7.1",
"@fortawesome/free-solid-svg-icons": "6.7.1",
"@fortawesome/fontawesome-free": "6.6.0",
"@fortawesome/fontawesome-svg-core": "6.6.0",
"@fortawesome/free-regular-svg-icons": "6.6.0",
"@fortawesome/free-solid-svg-icons": "6.6.0",
"@fortawesome/react-fontawesome": "0.2.2",
"@microsoft/signalr": "6.0.25",
"@sentry/browser": "7.119.1",
@@ -86,13 +86,13 @@
"typescript": "5.1.6"
},
"devDependencies": {
"@babel/core": "7.26.0",
"@babel/eslint-parser": "7.25.9",
"@babel/plugin-proposal-export-default-from": "7.25.9",
"@babel/core": "7.25.8",
"@babel/eslint-parser": "7.25.8",
"@babel/plugin-proposal-export-default-from": "7.25.8",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/preset-env": "7.26.0",
"@babel/preset-react": "7.26.3",
"@babel/preset-typescript": "7.26.0",
"@babel/preset-env": "7.25.8",
"@babel/preset-react": "7.25.7",
"@babel/preset-typescript": "7.25.7",
"@types/lodash": "4.14.195",
"@types/react-lazyload": "3.2.3",
"@types/redux-actions": "2.6.5",
@@ -102,7 +102,7 @@
"babel-loader": "9.2.1",
"babel-plugin-inline-classnames": "2.0.1",
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
"core-js": "3.39.0",
"core-js": "3.38.1",
"css-loader": "6.8.1",
"css-modules-typescript-loader": "4.0.1",
"eslint": "8.57.1",

View File

@@ -99,35 +99,6 @@
<RootNamespace Condition="'$(ReadarrProject)'=='true'">$(MSBuildProjectName.Replace('Readarr','NzbDrone'))</RootNamespace>
</PropertyGroup>
<ItemGroup Condition="'$(TestProject)'!='true'">
<!-- Annotates .NET assemblies with repository information including SHA -->
<!-- Sentry uses this to link directly to GitHub at the exact version/file/line -->
<!-- This is built-in on .NET 8 and can be removed once the project is updated -->
<PackageReference Include="Microsoft.SourceLink.GitHub" />
</ItemGroup>
<!-- Sentry specific configuration: Only in Release mode -->
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<!-- https://docs.sentry.io/platforms/dotnet/configuration/msbuild/ -->
<!-- OrgSlug, ProjectSlug and AuthToken are required.
They can be set below, via argument to 'msbuild -p:' or environment variable -->
<SentryOrg></SentryOrg>
<SentryProject></SentryProject>
<SentryUrl></SentryUrl> <!-- If empty, assumed to be sentry.io -->
<SentryAuthToken></SentryAuthToken> <!-- Use env var instead: SENTRY_AUTH_TOKEN -->
<!-- Upload PDBs to Sentry, enabling stack traces with line numbers and file paths
without the need to deploy the application with PDBs -->
<SentryUploadSymbols>true</SentryUploadSymbols>
<!-- Source Link settings -->
<!-- https://github.com/dotnet/sourcelink/blob/main/docs/README.md#publishrepositoryurl -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- Embeds all source code in the respective PDB. This can make it a bit bigger but since it'll be uploaded
to Sentry and not distributed to run on the server, it helps debug crashes while making releases smaller -->
<EmbedAllSources>true</EmbedAllSources>
</PropertyGroup>
<!-- Standard testing packages -->
<ItemGroup Condition="'$(TestProject)'=='true'">
<PackageReference Include="Microsoft.NET.Test.Sdk" />

View File

@@ -17,16 +17,14 @@
<PackageVersion Include="Ical.Net" Version="4.3.1" />
<PackageVersion Include="ImpromptuInterface" Version="7.0.1" />
<PackageVersion Include="LazyCache" Version="2.4.0" />
<PackageVersion Include="Mailkit" Version="4.8.0" />
<PackageVersion Include="Mailkit" Version="3.6.0" />
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.35" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="2.1.7" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="6.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageVersion Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr22" />
<PackageVersion Include="Moq" Version="4.17.2" />
@@ -45,7 +43,7 @@
<PackageVersion Include="RestSharp" Version="106.15.0" />
<PackageVersion Include="Selenium.Support" Version="3.141.0" />
<PackageVersion Include="Selenium.WebDriver.ChromeDriver" Version="91.0.4472.10100" />
<PackageVersion Include="Sentry" Version="4.0.2" />
<PackageVersion Include="Sentry" Version="3.31.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.6" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.1.118" />

View File

@@ -4,7 +4,6 @@ using System.Linq;
using FluentAssertions;
using NLog;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation.Sentry;
using NzbDrone.Test.Common;
@@ -28,7 +27,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[SetUp]
public void Setup()
{
_subject = new SentryTarget("https://aaaaaaaaaaaaaaaaaaaaaaaaaa@sentry.io/111111", Mocker.GetMock<IAppFolderInfo>().Object);
_subject = new SentryTarget("https://aaaaaaaaaaaaaaaaaaaaaaaaaa@sentry.io/111111");
}
private LogEventInfo GivenLogEvent(LogLevel level, Exception ex, string message)

View File

@@ -42,18 +42,17 @@ namespace NzbDrone.Common
public void CreateZip(string path, IEnumerable<string> files)
{
_logger.Debug("Creating archive {0}", path);
using var zipFile = ZipFile.Create(path);
zipFile.BeginUpdate();
foreach (var file in files)
using (var zipFile = ZipFile.Create(path))
{
zipFile.Add(file, Path.GetFileName(file));
}
zipFile.BeginUpdate();
zipFile.CommitUpdate();
foreach (var file in files)
{
zipFile.Add(file, Path.GetFileName(file));
}
zipFile.CommitUpdate();
}
}
private void ExtractZip(string compressedFile, string destination)

View File

@@ -41,7 +41,7 @@ namespace NzbDrone.Common.Instrumentation
RegisterDebugger();
}
RegisterSentry(updateApp, appFolderInfo);
RegisterSentry(updateApp);
if (updateApp)
{
@@ -62,7 +62,7 @@ namespace NzbDrone.Common.Instrumentation
LogManager.ReconfigExistingLoggers();
}
private static void RegisterSentry(bool updateClient, IAppFolderInfo appFolderInfo)
private static void RegisterSentry(bool updateClient)
{
string dsn;
@@ -77,7 +77,7 @@ namespace NzbDrone.Common.Instrumentation
: "https://31e00a6c63ea42c8b5fe70358526a30d@sentry.servarr.com/4";
}
var target = new SentryTarget(dsn, appFolderInfo)
var target = new SentryTarget(dsn)
{
Name = "sentryTarget",
Layout = "${message}"

View File

@@ -9,7 +9,6 @@ using NLog;
using NLog.Common;
using NLog.Targets;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using Sentry;
namespace NzbDrone.Common.Instrumentation.Sentry
@@ -100,7 +99,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
public bool FilterEvents { get; set; }
public bool SentryEnabled { get; set; }
public SentryTarget(string dsn, IAppFolderInfo appFolderInfo)
public SentryTarget(string dsn)
{
_sdk = SentrySdk.Init(o =>
{
@@ -108,33 +107,9 @@ namespace NzbDrone.Common.Instrumentation.Sentry
o.AttachStacktrace = true;
o.MaxBreadcrumbs = 200;
o.Release = $"{BuildInfo.AppName}@{BuildInfo.Release}";
o.SetBeforeSend(x => SentryCleanser.CleanseEvent(x));
o.SetBeforeBreadcrumb(x => SentryCleanser.CleanseBreadcrumb(x));
o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
o.Environment = BuildInfo.Branch;
// Crash free run statistics (sends a ping for healthy and for crashes sessions)
o.AutoSessionTracking = false;
// Caches files in the event device is offline
// Sentry creates a 'sentry' sub directory, no need to concat here
o.CacheDirectoryPath = appFolderInfo.GetAppDataPath();
// default environment is production
if (!RuntimeInfo.IsProduction)
{
if (RuntimeInfo.IsDevelopment)
{
o.Environment = "development";
}
else if (RuntimeInfo.IsTesting)
{
o.Environment = "testing";
}
else
{
o.Environment = "other";
}
}
});
InitializeScope();
@@ -152,7 +127,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
{
SentrySdk.ConfigureScope(scope =>
{
scope.User = new SentryUser
scope.User = new User
{
Id = HashUtil.AnonymousToken()
};
@@ -194,7 +169,9 @@ namespace NzbDrone.Common.Instrumentation.Sentry
private void OnError(Exception ex)
{
if (ex is WebException webException)
var webException = ex as WebException;
if (webException != null)
{
var response = webException.Response as HttpWebResponse;
var statusCode = response?.StatusCode;
@@ -313,21 +290,13 @@ namespace NzbDrone.Common.Instrumentation.Sentry
}
}
var level = LoggingLevelMap[logEvent.Level];
var sentryEvent = new SentryEvent(logEvent.Exception)
{
Level = level,
Level = LoggingLevelMap[logEvent.Level],
Logger = logEvent.LoggerName,
Message = logEvent.FormattedMessage
};
if (level is SentryLevel.Fatal && logEvent.Exception is not null)
{
// Usages of 'fatal' here indicates the process will crash. In Sentry this is represented with
// the 'unhandled' exception flag
logEvent.Exception.SetSentryMechanism("Logger.Fatal", "Logger.Fatal was called", false);
}
sentryEvent.SetExtras(extras);
sentryEvent.SetFingerprint(fingerPrint);

View File

@@ -711,30 +711,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
item.CanMoveFiles.Should().BeTrue();
}
[TestCase("pausedUP")]
[TestCase("stoppedUP")]
public void should_be_removable_and_should_allow_move_files_if_max_ratio_reached_after_rounding_and_paused(string state)
{
GivenGlobalSeedLimits(1.0f);
GivenCompletedTorrent(state, ratio: 1.1006066990976857f);
var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeTrue();
item.CanMoveFiles.Should().BeTrue();
}
[TestCase("pausedUP")]
[TestCase("stoppedUP")]
public void should_be_removable_and_should_allow_move_files_if_just_under_max_ratio_reached_after_rounding_and_paused(string state)
{
GivenGlobalSeedLimits(1.0f);
GivenCompletedTorrent(state, ratio: 0.9999f);
var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeTrue();
item.CanMoveFiles.Should().BeTrue();
}
[TestCase("pausedUP")]
[TestCase("stoppedUP")]
public void should_be_removable_and_should_allow_move_files_if_overridden_max_ratio_reached_and_paused(string state)
@@ -747,30 +723,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
item.CanMoveFiles.Should().BeTrue();
}
[TestCase("pausedUP")]
[TestCase("stoppedUP")]
public void should_be_removable_and_should_allow_move_files_if_overridden_max_ratio_reached_after_rounding_and_paused(string state)
{
GivenGlobalSeedLimits(2.0f);
GivenCompletedTorrent(state, ratio: 1.1006066990976857f, ratioLimit: 1.1f);
var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeTrue();
item.CanMoveFiles.Should().BeTrue();
}
[TestCase("pausedUP")]
[TestCase("stoppedUP")]
public void should_be_removable_and_should_allow_move_files_if_just_under_overridden_max_ratio_reached_after_rounding_and_paused(string state)
{
GivenGlobalSeedLimits(2.0f);
GivenCompletedTorrent(state, ratio: 0.9999f, ratioLimit: 1.0f);
var item = Subject.GetItems().Single();
item.CanBeRemoved.Should().BeTrue();
item.CanMoveFiles.Should().BeTrue();
}
[TestCase("pausedUP")]
[TestCase("stoppedUP")]
public void should_not_be_removable_if_overridden_max_ratio_not_reached_and_paused(string state)

View File

@@ -66,19 +66,12 @@ namespace NzbDrone.Core.Backup
{
_logger.ProgressInfo("Starting Backup");
var backupFolder = GetBackupFolder(backupType);
_diskProvider.EnsureFolder(_backupTempFolder);
_diskProvider.EnsureFolder(backupFolder);
if (!_diskProvider.FolderWritable(backupFolder))
{
throw new UnauthorizedAccessException($"Backup folder {backupFolder} is not writable");
}
_diskProvider.EnsureFolder(GetBackupFolder(backupType));
var dateNow = DateTime.Now;
var backupFilename = $"readarr_backup_v{BuildInfo.Version}_{dateNow:yyyy.MM.dd_HH.mm.ss}.zip";
var backupPath = Path.Combine(backupFolder, backupFilename);
var backupPath = Path.Combine(GetBackupFolder(backupType), backupFilename);
Cleanup();

View File

@@ -620,14 +620,14 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
{
if (torrent.RatioLimit >= 0)
{
if (torrent.RatioLimit - torrent.Ratio <= 0.001f)
if (torrent.Ratio >= torrent.RatioLimit)
{
return true;
}
}
else if (torrent.RatioLimit == -2 && config.MaxRatioEnabled)
{
if (config.MaxRatio - torrent.Ratio <= 0.001f)
if (Math.Round(torrent.Ratio, 2) >= config.MaxRatio)
{
return true;
}

View File

@@ -4,7 +4,6 @@ using System.Net;
using Newtonsoft.Json.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
@@ -209,7 +208,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
private void AuthenticateClient(HttpRequestBuilder requestBuilder, TransmissionSettings settings, bool reauthenticate = false)
{
var authKey = $"{requestBuilder.BaseUrl}:{settings.Password}";
var authKey = string.Format("{0}:{1}", requestBuilder.BaseUrl, settings.Password);
var sessionId = _authSessionIDCache.Find(authKey);
@@ -221,26 +220,24 @@ namespace NzbDrone.Core.Download.Clients.Transmission
authLoginRequest.SuppressHttpError = true;
var response = _httpClient.Execute(authLoginRequest);
switch (response.StatusCode)
if (response.StatusCode == HttpStatusCode.MovedPermanently)
{
case HttpStatusCode.MovedPermanently:
var url = response.Headers.GetSingleValue("Location");
var url = response.Headers.GetSingleValue("Location");
throw new DownloadClientException("Remote site redirected to " + url);
case HttpStatusCode.Forbidden:
throw new DownloadClientException($"Failed to authenticate with Transmission. It may be necessary to add {BuildInfo.AppName}'s IP address to RPC whitelist.");
case HttpStatusCode.Conflict:
sessionId = response.Headers.GetSingleValue("X-Transmission-Session-Id");
throw new DownloadClientException("Remote site redirected to " + url);
}
else if (response.StatusCode == HttpStatusCode.Conflict)
{
sessionId = response.Headers.GetSingleValue("X-Transmission-Session-Id");
if (sessionId == null)
{
throw new DownloadClientException("Remote host did not return a Session Id.");
}
break;
default:
throw new DownloadClientAuthenticationException("Failed to authenticate with Transmission.");
if (sessionId == null)
{
throw new DownloadClientException("Remote host did not return a Session Id.");
}
}
else
{
throw new DownloadClientAuthenticationException("Failed to authenticate with Transmission.");
}
_logger.Debug("Transmission authentication succeeded.");

View File

@@ -6,7 +6,6 @@
<PackageReference Include="Dapper" />
<PackageReference Include="Diacritical.Net" />
<PackageReference Include="LazyCache" />
<PackageReference Include="Microsoft.Data.SqlClient" />
<PackageReference Include="Polly" />
<PackageReference Include="System.Text.Json" />
<PackageReference Include="System.Text.Encoding.CodePages" />

View File

@@ -1,4 +1,5 @@
using FluentValidation;
using FluentValidation.Validators;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Validation
@@ -9,5 +10,10 @@ namespace NzbDrone.Core.Validation
{
return ruleBuilder.Must(x => x.IsValidIpAddress()).WithMessage("Must contain wildcard (*) or a valid IP Address");
}
public static IRuleBuilderOptions<T, string> NotListenAllIp4Address<T>(this IRuleBuilder<T, string> ruleBuilder)
{
return ruleBuilder.SetValidator(new RegularExpressionValidator(@"^(?!0\.0\.0\.0)")).WithMessage("Use * instead of 0.0.0.0");
}
}
}

View File

@@ -34,6 +34,7 @@ namespace Readarr.Api.V1.Config
SharedValidator.RuleFor(c => c.BindAddress)
.ValidIpAddress()
.NotListenAllIp4Address()
.When(c => c.BindAddress != "*" && c.BindAddress != "localhost");
SharedValidator.RuleFor(c => c.Port).ValidPort();

1299
yarn.lock

File diff suppressed because it is too large Load Diff