Compare commits

...
Author SHA1 Message Date
Kenneth Skovhede 621aada0d1 Reworked the task checker to use polling instead of relying on events 2024-07-19 09:33:20 +02:00
Kenneth Skovhede e5a3fed155 Stringify output 2024-07-19 08:58:48 +02:00
Kenneth Skovhede 67b133a206 Debug restore UI failure 2024-07-19 08:53:09 +02:00
Kenneth Skovhede 5b1df6fa61 Preloading 2024-07-19 08:44:26 +02:00
Kenneth Skovhede f50b49c56e Enable websocket by default 2024-07-18 23:38:33 +02:00
Kenneth Skovhede e05888e557 Testing wait for clickable elements 2024-07-18 23:28:45 +02:00
Kenneth Skovhede c36ec0cbc5 Added support for disabling websocket, and set it to default disabled 2024-07-18 23:17:15 +02:00
Kenneth Skovhede b968dba9ce Guard against multiple guard calls 2024-07-18 23:16:36 +02:00
Kenneth Skovhede 7a85b1763f Better enabling of logging 2024-07-18 22:51:33 +02:00
Kenneth Skovhede 599b4124f2 Test with logs on 2024-07-18 22:44:05 +02:00
Kenneth Skovhede 037e9e179f Merge remote-tracking branch 'origin/master' into feature/remove-httpserver-with-debug 2024-07-18 22:30:09 +02:00
Kenneth Skovhede b9d6084973 Yet-another rework of the reconnect 2024-07-18 22:28:18 +02:00
Kenneth Skovhede c52f08bc6e Added guard against case where the command happens faster than the events can register them 2024-07-18 22:27:39 +02:00
Kenneth Skovhede 85d5b1d74e Fixed error messages for requests with no response 2024-07-18 21:31:16 +02:00
Kenneth Skovhede 63fc93ab38 Updated localization files 2024-07-18 18:18:18 +02:00
Kenneth SkovhedeandGitHub 790f9c06fa Merge pull request #5274 from luixxiul/placeholders
Update placeholders without 'translate'
2024-07-18 18:15:57 +02:00
Suguru Hirahara 12407d4c03 Make placeholders translatable: storj.html and tardigrade.html 2024-07-18 11:28:41 -04:00
Suguru Hirahara 090ecf1af6 Update placeholders without 'translate' 2024-07-18 07:40:50 -04:00
Kenneth Skovhede 105f6e16ff Fixed origin mapping 2024-07-18 10:51:58 +02:00
Kenneth SkovhedeandGitHub 499a3ad575 Merge pull request #5262 from duplicati/feature/update-uplink-fixed-native-binaries
Prepared for new Uplink packages that correctly package the Storj binaries
2024-07-18 10:47:57 +02:00
Kenneth SkovhedeandGitHub f0107b476a Merge pull request #5270 from duplicati/feature/update-localizations
Update localizations
2024-07-18 10:35:37 +02:00
Kenneth Skovhede b7cac4cd51 Removed mixing url with hostnames 2024-07-18 10:34:13 +02:00
Kenneth SkovhedeandGitHub b55ba094b6 Merge pull request #5271 from npodbielski/feature/remove-httpserver
Add websocket support to httpserver
2024-07-18 10:33:04 +02:00
Kenneth Skovhede fecb2bf25a Updated to latest uplink.net packages 2024-07-18 10:08:57 +02:00
Kenneth Skovhede 877b1c811f Updated pot with new strings 2024-07-18 09:57:45 +02:00
Kenneth Skovhede 3bb06311a8 Added localization on additional strings 2024-07-18 09:56:19 +02:00
Kenneth SkovhedeandGitHub 8d96681db6 Merge pull request #5265 from luixxiul/DescriptionS3ClientLong
Fix string `DescriptionS3ClientLong`
2024-07-17 22:18:20 +02:00
Kenneth SkovhedeandGitHub f458355663 Merge pull request #5264 from luixxiul/B2applicationkeyDescription
Replace `B2applicationkeyDescriptionShort` with `B2applicationkeyDescriptionLong`
2024-07-17 22:18:07 +02:00
Kenneth SkovhedeandGitHub e9d7433a72 Merge pull request #5263 from duplicati/feature/report-errors-when-sending-http-report
Added check for valid status code after sending http request with report
2024-07-17 20:48:17 +02:00
Kenneth Skovhede 6195e07808 Updated translation files 2024-07-17 20:32:22 +02:00
Kenneth Skovhede 53059c01dd Pulled from Transifex 2024-07-17 20:26:03 +02:00
Kenneth Skovhede bb274822cc Fixed scripts and config to follow updated TX client 2024-07-17 20:25:35 +02:00
Kenneth Skovhede f74cee2932 Fixed #5267 2024-07-17 19:34:26 +02:00
natan 92f1de07ae Bring back /serverstate endpoint. Fix unauhthorized mdodal when cookie is missing.
Small code cleanup here and there.
2024-07-15 11:07:47 +02:00
Suguru Hirahara f040df8e2f Switch B2applicationkeyDescriptionShort and B2applicationkeyDescriptionLong 2024-07-14 22:38:58 -04:00
Suguru Hirahara ab7e640435 Fix string DescriptionS3ClientLong 2024-07-14 22:36:20 -04:00
natan 27ce549e75 cleanup WebsocketAccessor.cs and StatusService.cs a bit.
Remove /serverstate endpoint
2024-07-12 17:59:21 +02:00
natan 09d30b92e0 Merge remote-tracking branch 'refs/remotes/npodbielski/feature/remove-httpserver' into feature/remove-httpserver 2024-07-12 16:55:59 +02:00
natan bc0a47f768 Merge remote-tracking branch 'origin/feature/remove-httpserver' into feature/remove-httpserver 2024-07-12 16:53:18 +02:00
npodbielskiandGitHub 75bab41034 Merge pull request #1 from duplicati/remove-httpserver-updates-to-reconnect
Changed logic for reconnect to not rely on serverstate API.
2024-07-12 16:53:04 +02:00
Kenneth Skovhede 94d6ab76ee Changed logic for reconnect to not rely on serverstate API.
Changed websocket to send current state on initial connect.
2024-07-11 16:24:22 +02:00
Kenneth Skovhede 1d913e026f Added check for valid status code after sending http request with report 2024-07-10 09:22:07 +02:00
Kenneth Skovhede 974f6bff2f Removed unused file 2024-07-10 09:20:18 +02:00
natan 0ae9990f18 fix 401 in websocket 2024-07-09 15:49:17 +02:00
natan 18ff96c5fe Merge remote-tracking branch 'origin/feature/remove-httpserver' into feature/remove-httpserver 2024-07-09 13:24:13 +02:00
Kenneth Skovhede b39a68bda7 Prepared for new Uplink packages that correctly package the Storj binaries 2024-07-09 12:15:12 +02:00
Kenneth Skovhede 60437d7fc0 Removed duplicate entry in project file 2024-07-09 11:24:17 +02:00
Kenneth SkovhedeandGitHub 618d9015ab Merge pull request #5261 from grzesieklagocki/copyright
Add copyright information to libraries #5258
2024-07-09 11:23:32 +02:00
Grzegorz Łagocki 9b19bac7da Add Copyright Property to *.csproj files 2024-07-09 00:37:18 +02:00
Kenneth Skovhede 6917030417 Treat all auth operation failures as Unauthorized - 401 2024-07-07 10:36:42 +02:00
Kenneth Skovhede 44a45fcd04 Only generate signin token if password is autogenerated 2024-07-07 10:36:11 +02:00
Kenneth SkovhedeandGitHub 59b00a5a45 Merge pull request #5254 from duplicati/feature/update-mega-library
Updated Mega library to latest
2024-07-03 09:52:51 +02:00
Kenneth Skovhede 31f0cd02ab Updated Mega library to latest 2024-07-02 10:59:18 +02:00
122 changed files with 4254 additions and 3337 deletions
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
@@ -6,6 +6,8 @@
<RootNamespace>license_upgrader</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
</Project>
@@ -101,9 +101,7 @@ namespace Duplicati.Server.Database
settings[k.Key] = newsettings[k.Key];
// Prevent user from logging themselves out, by disabling the login and not knowing the password
var disableTrayIconLogin = Library.Utility.Utility.ParseBoolOption(settings, CONST.DISABLE_TRAY_ICON_LOGIN);
var autogeneratedPassword = Library.Utility.Utility.ParseBoolOption(settings, CONST.AUTOGENERATED_PASSPHRASE);
if (disableTrayIconLogin && autogeneratedPassword)
if (DisableTrayIconLogin && AutogeneratedPassphrase)
settings[CONST.DISABLE_TRAY_ICON_LOGIN] = false.ToString();
}
@@ -267,6 +265,14 @@ namespace Duplicati.Server.Database
}
}
public bool AutogeneratedPassphrase
{
get
{
return Duplicati.Library.Utility.Utility.ParseBool(settings[CONST.AUTOGENERATED_PASSPHRASE], false);
}
}
public int LastWebserverPort
{
get
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -30,3 +31,4 @@
</ItemGroup>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>Duplicati.CommandLine.AutoUpdater.Implementation</AssemblyName>
<RootNamespace>Duplicati.CommandLine.AutoUpdater</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -20,3 +21,4 @@
</ItemGroup>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Description>A backend debugging tool for Duplicati</Description>
<AssemblyName>Duplicati.CommandLine.BackendTester.Implementation</AssemblyName>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -27,3 +28,4 @@
</ItemGroup>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>Duplicati.CommandLine.BackendTool.Implementation</AssemblyName>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -20,3 +21,4 @@
</ItemGroup>
</Project>
@@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Description>A commandline version of Duplicati</Description>
<AssemblyName>Duplicati.CommandLine.Implementation</AssemblyName>
<ApplicationIcon>TrayWarning.ico</ApplicationIcon>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -34,4 +35,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
+7 -6
View File
@@ -20,8 +20,10 @@
// DEALINGS IN THE SOFTWARE.
using Duplicati.Library.Localization.Short;
namespace Duplicati.CommandLine.Strings {
internal static class Program {
namespace Duplicati.CommandLine.Strings
{
internal static class Program
{
public static string DeleteCommandNeedsOptions(string commandname, string[] options) { return LC.L(@"The command {0} needs at least one of the following options set: {1}", commandname, string.Join(", ", options)); }
public static string WrongNumberOfCommandsError_v2(int actualcommands, int expectedcommands, string[] commands) { return LC.L(@"Found {0} commands but expected {1}, commands:
{2}", actualcommands, expectedcommands, string.Join(System.Environment.NewLine, commands ?? new string[0])); }
@@ -47,13 +49,12 @@ namespace Duplicati.CommandLine.Strings {
public static string IncludeShort { get { return LC.L(@"Include files"); } }
public static string ExcludeLong { get { return LC.L(@"Exclude files that match this filter. The special character * means any number of character, and the special character ? means any single character, use *.txt to exclude all files with a txt extension. Regular expressions are also supported and can be supplied by using hard braces, i.e. [.*\.txt]. Filter groups (which encapsulate a built-in set of well-known files and folders) can be specified by using curly braces, i.e. {{TemporaryFiles}}."); } }
public static string ExcludeShort { get { return LC.L(@"Exclude files"); } }
public static string ControlFilesOptionShort { get { return LC.L(@"If this option is used with a backup operation, it is interpreted as a list of files to add to the filesets. When used with list or restore, it will list or restore the control files instead of the normal files."); } }
public static string ControlFilesOptionLong { get { return LC.L(@"Use control files"); } }
public static string ControlFilesOptionShort { get { return LC.L(@"Use control files"); } }
public static string ControlFilesOptionLong { get { return LC.L(@"If this option is used with a backup operation, it is interpreted as a list of files to add to the filesets. When used with list or restore, it will list or restore the control files instead of the normal files."); } }
public static string QuietConsoleOptionLong { get { return LC.L(@"If this option is set, progress reports and other messages that would normally go to the console will be redirected to the log."); } }
public static string QuietConsoleOptionShort { get { return LC.L(@"Disable console output"); } }
public static string SkippingSourceArgumentsOnNonBackupOperation { get { return @"The --source argument was specified in the parameter file, but the current operation is not a backup operation, so the argument is ignored"; } }
// ReSharper disable once UnusedMember.Global
// This is a placeholder message that is intended to be used with the code
// for each error and log message. The idea is that the commandline will
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<AssemblyName>Duplicati.CommandLine.ConfigurationImporter.Implementation</AssemblyName>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -18,4 +19,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Description>RecoveryTool for Duplicati</Description>
<AssemblyName>Duplicati.CommandLine.RecoveryTool.Implementation</AssemblyName>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -25,3 +26,4 @@
</ItemGroup>
</Project>
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
@@ -10,6 +10,7 @@
<TrimMode>copyused</TrimMode>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -61,3 +62,4 @@
<ProjectReference Include="..\..\Tools\Duplicati.Tools.csproj" />
</ItemGroup>
</Project>
@@ -338,6 +338,9 @@ namespace Duplicati.GUI.TrayIcon
var response = await HTTPCLIENT.SendAsync(request, cts.Token);
response.EnsureSuccessStatusCode();
if (typeof(T) == typeof(string))
return (T)(object)await response.Content.ReadAsStringAsync();
using (var stream = await response.Content.ReadAsStreamAsync())
return await JsonSerializer.DeserializeAsync<T>(stream, serializerOptions);
}
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -31,4 +32,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\Duplicati.Library.Common.csproj" />
@@ -16,4 +17,4 @@
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Interface\Duplicati.Library.Interface.csproj" />
@@ -17,4 +18,4 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.KeyVault.Core" Version="3.0.4" />
@@ -30,4 +31,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -22,4 +23,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -22,8 +22,8 @@
using Duplicati.Library.Localization.Short;
namespace Duplicati.Library.Backend.Strings {
internal static class B2 {
public static string B2applicationkeyDescriptionShort { get { return LC.L(@"The ""B2 Cloud Storage Application Key"" can be obtained after logging into your Backblaze account, this can also be supplied through the ""auth-password"" property"); } }
public static string B2applicationkeyDescriptionLong { get { return LC.L(@"The ""B2 Cloud Storage Application Key"""); } }
public static string B2applicationkeyDescriptionLong { get { return LC.L(@"The ""B2 Cloud Storage Application Key"" can be obtained after logging into your Backblaze account, this can also be supplied through the ""auth-password"" property"); } }
public static string B2applicationkeyDescriptionShort { get { return LC.L(@"The ""B2 Cloud Storage Application Key"""); } }
public static string B2accountidDescriptionLong { get { return LC.L(@"The ""B2 Cloud Storage Account ID"" can be obtained after logging into your Backblaze account, this can also be supplied through the ""auth-username"" property"); } }
public static string B2accountidDescriptionShort { get { return LC.L(@"The ""B2 Cloud Storage Account ID"""); } }
public static string DisplayName { get { return LC.L(@"B2 Cloud Storage"); } }
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -24,4 +25,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -19,4 +20,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,11 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Description>Dropbox backend for Duplicati</Description>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -25,4 +23,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -20,3 +21,4 @@
</ItemGroup>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -19,4 +20,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -22,4 +23,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -23,6 +23,7 @@ using System;
using Duplicati.Library.Interface;
using System.Collections.Generic;
using System.Linq;
using Duplicati.Library.Localization.Short;
namespace Duplicati.Library.Backend.GoogleServices
{
@@ -67,19 +68,19 @@ namespace Duplicati.Library.Backend.GoogleServices
public string Key { get { return "gcs-getconfig"; } }
public string DisplayName { get { return "Google Cloud Storage configuration module"; } }
public string DisplayName { get { return LC.L("Google Cloud Storage configuration module"); } }
public string Description { get { return "Exposes Google Cloud Storage configuration as a web module"; } }
public string Description { get { return LC.L("Exposes Google Cloud Storage configuration as a web module"); } }
public System.Collections.Generic.IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, "The config to get", "Provides different config values", DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
return new List<ICommandLineArgument>([
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, LC.L("The config to get"), LC.L("Provides different config values"), DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
});
]);
}
}
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -21,3 +22,4 @@
</PackageReference>
</ItemGroup>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -21,3 +22,4 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>
@@ -1,12 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MegaApiClient" Version="1.10.3" />
<PackageReference Include="Otp.NET" Version="1.2.2" />
<PackageReference Include="MegaApiClient" Version="1.10.4" />
<PackageReference Include="Otp.NET" Version="1.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
@@ -23,4 +24,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -22,3 +23,4 @@
</ItemGroup>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -17,4 +18,4 @@
<ProjectReference Include="..\..\Logging\Duplicati.Library.Logging.csproj" />
<ProjectReference Include="..\..\Backend\OAuthHelper\Duplicati.Library.OAuthHelper.csproj" />
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -22,4 +23,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -23,6 +23,7 @@ using System;
using Duplicati.Library.Interface;
using System.Collections.Generic;
using System.Linq;
using Duplicati.Library.Localization.Short;
namespace Duplicati.Library.Backend.OpenStack
{
@@ -63,19 +64,19 @@ namespace Duplicati.Library.Backend.OpenStack
public string Key { get { return "openstack-getconfig"; } }
public string DisplayName { get { return "OpenStack configuration module"; } }
public string DisplayName { get { return LC.L("OpenStack configuration module"); } }
public string Description { get { return "Exposes OpenStack configuration as a web module"; } }
public string Description { get { return LC.L("Exposes OpenStack configuration as a web module"); } }
public System.Collections.Generic.IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, "The config to get", "Provides different config values", DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
return new List<ICommandLineArgument>([
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, LC.L("The config to get"), LC.L("Provides different config values"), DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
});
]);
}
}
}
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -21,4 +22,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -28,4 +29,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
+8 -8
View File
@@ -21,8 +21,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Duplicati.Library.Interface;
using Duplicati.Library.Localization.Short;
namespace Duplicati.Library.Backend
{
@@ -52,11 +52,11 @@ namespace Duplicati.Library.Backend
options.TryGetValue(KEY_CONFIGTYPE, out k);
if (string.IsNullOrWhiteSpace(k))
k = DEFAULT_CONFIG_TYPE_STR;
ConfigType ct;
if (!Enum.TryParse<ConfigType>(k, true, out ct))
ct = DEFAULT_CONFIG_TYPE;
switch (ct)
{
case ConfigType.RegionHosts:
@@ -72,18 +72,18 @@ namespace Duplicati.Library.Backend
public string Key { get { return "s3-getconfig"; } }
public string DisplayName { get { return "S3 configuration module"; } }
public string DisplayName { get { return LC.L("S3 configuration module"); } }
public string Description { get { return "Exposes S3 configuration as a web module"; } }
public string Description { get { return LC.L("Exposes S3 configuration as a web module"); } }
public IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, "The config to get", "Provides different config values", DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
return new List<ICommandLineArgument>([
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, LC.L("The config to get"), LC.L("Provides different config values"), DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
});
]);
}
}
+8 -7
View File
@@ -25,6 +25,7 @@ using System.Linq;
using Duplicati.Library.Interface;
using Amazon.IdentityManagement;
using Amazon.IdentityManagement.Model;
using Duplicati.Library.Localization.Short;
namespace Duplicati.Library.Backend
{
@@ -67,20 +68,20 @@ namespace Duplicati.Library.Backend
public string Key { get { return "s3-iamconfig"; } }
public string DisplayName { get { return "S3 IAM support module"; } }
public string DisplayName { get { return LC.L("S3 IAM support module"); } }
public string Description { get { return "Exposes S3 IAM manipulation as a web module"; } }
public string Description { get { return LC.L("Exposes S3 IAM manipulation as a web module"); } }
public IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(KEY_OPERATION, CommandLineArgument.ArgumentType.Enumeration, "The operation to perform", "Selects the operation to perform", null, Enum.GetNames(typeof(Operation))),
new CommandLineArgument(KEY_USERNAME, CommandLineArgument.ArgumentType.String, "The username", "The Amazon Access Key ID"),
new CommandLineArgument(KEY_PASSWORD, CommandLineArgument.ArgumentType.String, "The password", "The Amazon Secret Key"),
});
return new List<ICommandLineArgument>([
new CommandLineArgument(KEY_OPERATION, CommandLineArgument.ArgumentType.Enumeration, LC.L("The operation to perform"), LC.L("Selects the operation to perform"), null, Enum.GetNames(typeof(Operation))),
new CommandLineArgument(KEY_USERNAME, CommandLineArgument.ArgumentType.String, LC.L("The username"), LC.L("The Amazon Access Key ID")),
new CommandLineArgument(KEY_PASSWORD, CommandLineArgument.ArgumentType.String, LC.L("The password"), LC.L("The Amazon Secret Key")),
]);
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ namespace Duplicati.Library.Backend.Strings
public static string S3ServerNameDescriptionShort { get { return LC.L(@"Specifies an alternate S3 server name"); } }
public static string S3ClientDescriptionShort { get { return LC.L(@"Specifies the S3 client library to use"); } }
public static string DescriptionUseSSLLong { get { return LC.L(@"Use this flag to communicate using Secure Socket Layer (SSL) over http (https). Note that bucket names containing a period has problems with SSL connections."); } }
public static string DescriptionS3ClientLong { get { return LC.L(@"Set either to aws or minio . Then either the AWS SDK or Minio SDK will used to communicate with S3 services."); } }
public static string DescriptionS3ClientLong { get { return LC.L(@"Set either to aws or minio. Then either the AWS SDK or Minio SDK will be used to communicate with S3 services."); } }
public static string DescriptionUseSSLShort { get { return LC.L(@"Instructs Duplicati to use an SSL (https) connection"); } }
public static string DescriptionDisableChunkEncodingLong { get { return LC.L(@"This disables chunk encoding for the aws client, which is not supported by all S3 providers."); } }
public static string DescriptionDisableChunkEncodingShort { get { return LC.L(@"Disable chunk encoding (aws client only)"); } }
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -25,3 +26,4 @@
</ItemGroup>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Interface\Duplicati.Library.Interface.csproj" />
@@ -25,4 +26,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -22,4 +23,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -4,19 +4,15 @@
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Description>Storj backend for Duplicati</Description>
</PropertyGroup>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="uplink.NET" Version="2.12.3363" />
<!-- There is a packaging issue, in the way the Storj binaries are included: https://github.com/TopperDEL/uplink.net/issues/39 -->
<!-- The release build process now manually injects the correct binary components based on the runtime target until resolved. -->
<!-- The conditionals below ensure that building locally and debugging should still work -->
<PackageReference Condition="$(RuntimeIdentifier.StartsWith('win-'))" Include="uplink.NET.Win" Version="2.12.3363" />
<PackageReference Condition="$(RuntimeIdentifier.StartsWith('linux-'))" Include="uplink.NET.Linux" Version="2.12.3363" />
<PackageReference Condition="$(RuntimeIdentifier.StartsWith('osx-'))" Include="uplink.NET.Mac" Version="2.12.3363" />
<PackageReference Include="uplink.NET" Version="2.13.3484" />
<PackageReference Include="uplink.NET.Win" Version="2.13.3484" />
<PackageReference Include="uplink.NET.Linux" Version="2.13.3484" />
<PackageReference Include="uplink.NET.Mac" Version="2.13.3484" />
</ItemGroup>
<ItemGroup>
@@ -33,3 +29,4 @@
</PackageReference>
</ItemGroup>
</Project>
@@ -19,6 +19,7 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using Duplicati.Library.Interface;
using Duplicati.Library.Localization.Short;
using System;
using System.Collections.Generic;
@@ -40,18 +41,18 @@ namespace Duplicati.Library.Backend.Storj
public string Key { get { return "storj-getconfig"; } }
public string DisplayName { get { return "Storj DCS configuration module"; } }
public string DisplayName { get { return LC.L("Storj DCS configuration module"); } }
public string Description { get { return "Exposes Storj DCS configuration as a web module"; } }
public string Description { get { return LC.L("Exposes Storj DCS configuration as a web module"); } }
public IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, "The config to get", "Provides different config values", DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
return new List<ICommandLineArgument>([
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, LC.L("The config to get"), LC.L("Provides different config values"), DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
});
]);
}
}
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -24,4 +25,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\Duplicati.Library.Common.csproj" />
@@ -23,4 +24,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -20,4 +21,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
+1 -1
View File
@@ -76,7 +76,7 @@ public static class BackendModules
private static bool IsStorjSupported =>
(OperatingSystem.IsWindows() && (RuntimeInformation.ProcessArchitecture == Architecture.X64 || RuntimeInformation.ProcessArchitecture == Architecture.X86 || RuntimeInformation.ProcessArchitecture == Architecture.Arm64))
||
(OperatingSystem.IsLinux() && (RuntimeInformation.ProcessArchitecture == Architecture.X64))
(OperatingSystem.IsLinux() && (RuntimeInformation.ProcessArchitecture == Architecture.X64 || RuntimeInformation.ProcessArchitecture == Architecture.Arm64 || RuntimeInformation.ProcessArchitecture == Architecture.Arm))
||
(OperatingSystem.IsMacOS() && (RuntimeInformation.ProcessArchitecture == Architecture.X64 || RuntimeInformation.ProcessArchitecture == Architecture.Arm64));
}
@@ -3,6 +3,7 @@
<TargetFramework>net8.0</TargetFramework>
<Description>Central reference to all backends</Description>
<RootNamespace>Duplicati.Library.Backend</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -31,4 +32,4 @@
<ProjectReference Include="..\Backend\WEBDAV\Duplicati.Library.Backend.WEBDAV.csproj" />
<ProjectReference Include="..\Backend\AliyunOSS\Duplicati.Library.Backend.AliyunOSS.csproj" />
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.IO</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -26,3 +27,4 @@
</ItemGroup>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Compression</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -29,4 +30,4 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -24,4 +25,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Encryption</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -23,4 +24,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Interface</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -17,4 +18,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -24,4 +25,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -11,4 +12,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -47,3 +48,4 @@
</ItemGroup>
</Project>
+12 -12
View File
@@ -287,24 +287,24 @@ namespace Duplicati.Library.Main.Strings
public static string UsebackgroundiopriorityShort { get { return LC.L("Sets the process to use low IO priority"); } }
public static string UsebackgroundiopriorityLong { get { return LC.L("This option instructions the operating system to set the current process to use the lowest IO priority level, which can make operations run slower but will interfere less with other operations running at the same time"); } }
public static string ExcludeemptyfoldersShort { get { return "Excludes empty folders"; } }
public static string ExcludeemptyfoldersLong { get { return "Use this option to remove all empty folders from a backup."; } }
public static string ExcludeemptyfoldersShort { get { return LC.L("Excludes empty folders"); } }
public static string ExcludeemptyfoldersLong { get { return LC.L("Use this option to remove all empty folders from a backup."); } }
public static string IgnorefilenamesShort { get { return LC.L("List of filenames that exclude folders"); } }
public static string IgnorefilenamesLong { get { return LC.L("Use this option to set a filename, or list of filenames, that indicate exclusion of a folder which contains it. A common use would be to have a file named something like \".nobackup\" and place this file into folders that should not be backed up."); } }
public static string RestoresymlinkmetadataShort { get { return "Apply metadata to symlinks"; } }
public static string RestoresymlinkmetadataLong { get { return "If symlink metadata is applied, it will usually mean changing the symlink target, instead of the symlink itself. For this reason, metadata is not applied to symlinks, but this option can be used to override this, such that metadata is applied to symlinks as well."; } }
public static string UnittestmodeShort { get { return "Activate unittest mode"; } }
public static string UnittestmodeLong { get { return "When running in unittest mode, no automatic fixes are applied, which assumes that the input data is always in perfect shape. This option is not intended for use in daily backups, but required for testing purposes to reveal potential problems."; } }
public static string RestoresymlinkmetadataShort { get { return LC.L("Apply metadata to symlinks"); } }
public static string RestoresymlinkmetadataLong { get { return LC.L("If symlink metadata is applied, it will usually mean changing the symlink target, instead of the symlink itself. For this reason, metadata is not applied to symlinks, but this option can be used to override this, such that metadata is applied to symlinks as well."); } }
public static string UnittestmodeShort { get { return LC.L("Activate unittest mode"); } }
public static string UnittestmodeLong { get { return LC.L("When running in unittest mode, no automatic fixes are applied, which assumes that the input data is always in perfect shape. This option is not intended for use in daily backups, but required for testing purposes to reveal potential problems."); } }
public static string ProfilealldatabasequeriesShort { get { return LC.L("Activates logging of all database queries"); } }
public static string ProfilealldatabasequeriesLong { get { return LC.L("To improve performance of the backups, frequent database queries are not logged by default. Enable this option to log all database queries, and remember to set either --{0}={2} or --{1}={2} to report the additional log data", "console-log-level", "log-file-log-level", nameof(Logging.LogMessageType.Profiling)); } }
public static string RebuildmissingdblockfilesShort { get { return "Rebuild dblock files when missing"; } }
public static string RebuildmissingdblockfilesLong { get { return "If dblock files are missing from the destination, you can attempt to rebuild them using local source data. However, since the local data may have changed, it may not be possible to retrieve all the required data and the process may be slow. Use this option to attempt to rebuild missing dblock files."; } }
public static string RebuildmissingdblockfilesShort { get { return LC.L("Rebuild dblock files when missing"); } }
public static string RebuildmissingdblockfilesLong { get { return LC.L("If dblock files are missing from the destination, you can attempt to rebuild them using local source data. However, since the local data may have changed, it may not be possible to retrieve all the required data and the process may be slow. Use this option to attempt to rebuild missing dblock files."); } }
public static string AutoCompactIntervalShort { get { return "Minimum time between auto compactions"; } }
public static string AutoCompactIntervalLong { get { return "The minimum amount of time that must elapse after the last compaction before another will be automatically triggered at the end of a backup job. Automatic compaction can be a long-running process and may not be desirable to run after every single backup."; } }
public static string AutoVacuumIntervalShort { get { return "Minimum time between auto vacuums"; } }
public static string AutoVacuumIntervalLong { get { return "The minimum amount of time that must elapse after the last vacuum before another will be automatically triggered at the end of a backup job. Automatic vacuum can be a long-running process and may not be desirable to run after every single backup."; } }
public static string AutoCompactIntervalShort { get { return LC.L("Minimum time between auto compactions"); } }
public static string AutoCompactIntervalLong { get { return LC.L("The minimum amount of time that must elapse after the last compaction before another will be automatically triggered at the end of a backup job. Automatic compaction can be a long-running process and may not be desirable to run after every single backup."); } }
public static string AutoVacuumIntervalShort { get { return LC.L("Minimum time between auto vacuums"); } }
public static string AutoVacuumIntervalLong { get { return LC.L("The minimum amount of time that must elapse after the last vacuum before another will be automatically triggered at the end of a backup job. Automatic vacuum can be a long-running process and may not be desirable to run after every single backup."); } }
}
internal static class Common
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Modules.Builtin</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -37,3 +38,4 @@
</ItemGroup>
</Project>
@@ -286,6 +286,8 @@ namespace Duplicati.Library.Modules.Builtin
response.ReasonPhrase,
responseContent
);
response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
@@ -6,7 +6,8 @@
<AssemblyName>SQLiteHelper</AssemblyName>
<Description>SQLiteHelper for Duplicati</Description>
<RootNamespace>Duplicati.Library.SQLiteHelper</RootNamespace>
</PropertyGroup>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Logging\Duplicati.Library.Logging.csproj" />
@@ -39,3 +40,4 @@
</ItemGroup>
</Project>
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -36,4 +37,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CoCoL" Version="1.7.1" />
@@ -22,4 +23,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Duplicati.Library.Utility</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -25,3 +26,4 @@
</ItemGroup>
</Project>
+3 -1
View File
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -320,3 +321,4 @@
<ProjectReference Include="..\Library\Utility\Duplicati.Library.Utility.csproj" />
</ItemGroup>
</Project>
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Library\Interface\Duplicati.Library.Interface.csproj" />
@@ -17,4 +18,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
+3 -1
View File
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>Duplicati.Server.Implementation</AssemblyName>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -53,3 +54,4 @@
</ItemGroup>
</Project>
+2 -2
View File
@@ -250,7 +250,7 @@ namespace Duplicati.Server
DuplicatiWebserver = StartWebServer(commandlineOptions, DataConnection).ConfigureAwait(false).GetAwaiter().GetResult();
if (FIXMEGlobal.Origin == "Server")
if (FIXMEGlobal.Origin == "Server" && DataConnection.ApplicationSettings.AutogeneratedPassphrase)
{
var signinToken = DuplicatiWebserver.Provider.GetRequiredService<IJWTTokenProvider>().CreateSigninToken("server-cli");
Console.WriteLine($"Server is now running on port {DuplicatiWebserver.Port}");
@@ -330,7 +330,7 @@ namespace Duplicati.Server
parsedOptions.AllowedHostnames);
if (mappedSettings.AllowedHostnames == null || !mappedSettings.AllowedHostnames.Any())
mappedSettings = mappedSettings with { AllowedHostnames = ["localhost", "127.0.0.1", "::1", "http://localhost:8200"] };
mappedSettings = mappedSettings with { AllowedHostnames = ["localhost", "127.0.0.1", "::1"] };
var server = new DuplicatiWebserver();
+9 -3
View File
@@ -291,14 +291,20 @@
<div id="connection-lost-dialog" ng-show="state.connectionState != 'connected'" class="connection-lost ng-cloak">
<div class="info">
<div class="title" translate>Connection lost</div>
<div class="content" ng-show="state.connectionState !='connecting'" translate translate-params-time="(state.connectionAttemptTimer + 1000) | timeremaining">
<div class="content" ng-show="state.connectionState == 'unauthorized'" translate>
Connection to server was rejected due to invalid authentication. Reload browser window to reconnect to server properly.
<br />
If this problem persist open this page from the TrayIcon instead.
</div>
<div class="content" ng-show="state.connectionState != 'unauthorized' && state.connectionState != 'connecting'" translate translate-params-time="(state.connectionAttemptTimer + 1000) | timeremaining">
The connection to the server is lost, attempting again in {{time}} ...
</div>
<div class="content" translate ng-hide="state.connectionState !='connecting'">
<div class="content" translate ng-hide="state.connectionState != 'connecting'">
Connecting to server …
</div>
<div class="content buttons">
<a href ng-show="state.connectionState != 'connecting'" ng-click="doReconnect()" class="button" translate>Connect now</a>
<a href ng-show="state.connectionState == 'unauthorized'" ng-click="reload()" class="button" translate>Reload</a>
<a href ng-show="state.connectionState != 'unauthorized' && state.connectionState != 'connecting'" ng-click="doReconnect()" class="button" translate>Connect now</a>
<a href ng-hide="state.connectionState != 'connecting'" class="button" style="cursor: default;" translate>Connecting...</a>
</div>
</div>
File diff suppressed because one or more lines are too long
@@ -17,6 +17,10 @@ backupApp.controller('AppController', function($scope, $cookies, $location, AppS
ServerStatus.reconnect();
};
$scope.reload = function() {
location.reload();
};
$scope.resume = function() {
ServerStatus.resume().then(function() {}, AppUtils.connectionError);
};
@@ -377,9 +377,11 @@ backupApp.controller('RestoreController', function ($rootScope, $scope, $routePa
AppService.get('/task/' + taskid).then(function(resp) {
$scope.ConnectionProgress = gettextCatalog.getString('Starting the restore process …');
console.log('Starting the restore process …', resp);
if (resp.data.Status == 'Completed')
{
AppService.post('/backup/' + backupid + '/restore', p).then(function(resp) {
console.log('Restoring files …', resp);
$scope.ConnectionProgress = gettextCatalog.getString('Restoring files …');
var t2 = $scope.taskid = resp.data.ID;
ServerStatus.callWhenTaskCompletes(t2, function() { $scope.onRestoreComplete(t2); });
@@ -403,6 +405,7 @@ backupApp.controller('RestoreController', function ($rootScope, $scope, $routePa
$scope.ConnectionProgress = gettextCatalog.getString('Starting the restore process …');
AppService.post('/backup/' + $scope.BackupID + '/restore', p).then(function(resp) {
$scope.ConnectionProgress = gettextCatalog.getString('Restoring files …');
console.log('Restoring files …', resp);
var t2 = $scope.taskid = resp.data.ID;
ServerStatus.callWhenTaskCompletes(t2, function() { $scope.onRestoreComplete(t2); });
}, handleError);
@@ -410,9 +413,11 @@ backupApp.controller('RestoreController', function ($rootScope, $scope, $routePa
};
$scope.onRestoreComplete = function(taskid) {
console.log('onRestoreComplete: ', taskid);
AppService.get('/task/' + taskid).then(function(resp) {
$scope.connecting = false;
$scope.ConnectionProgress = '';
console.log('Restore complete message: ', JSON.stringify(resp.data));
if (resp.data.Status == 'Completed')
{
@@ -82,6 +82,10 @@ backupApp.service('AppService', function ($http, $cookies, $q, $cookies, DialogS
return deferred.promise;
};
this.clearAccessToken = function () {
self.access_token = null;
self.access_token_promise = null;
}
// Returns a promise that resolves to the access token
this.getAccessToken = function () {
@@ -1,8 +1,10 @@
backupApp.service('ServerStatus', function ($rootScope, $timeout, AppService, AppUtils, gettextCatalog) {
const useWebsocket = true;
var longpolltime = 5 * 60 * 1000;
var waitingfortask = {};
var waitingfortaskTimers = {};
var state = {
lastEventId: -1,
@@ -15,6 +17,7 @@ backupApp.service('ServerStatus', function ($rootScope, $timeout, AppService, Ap
connectionState: 'connected',
connectionAttemptTimer: 0,
failedConnectionAttempts: 0,
failedAuthAttempts: 0,
lastPgEvent: null,
updaterState: 'waiting',
updateDownloadLink: null,
@@ -90,24 +93,70 @@ backupApp.service('ServerStatus', function ($rootScope, $timeout, AppService, Ap
return AppService.post('/serverstate/pause' + (duration == null ? '' : '?duration=' + duration));
};
function notifyTaskCompleted(taskid) {
if (waitingfortaskTimers[taskid] != null) {
window.clearInterval(waitingfortaskTimers[taskid]);
delete waitingfortaskTimers[taskid];
}
if (waitingfortask[taskid] != null) {
for(var i in waitingfortask[taskid])
waitingfortask[taskid][i]();
delete waitingfortask[taskid];
}
}
function checkTaskState(taskid) {
AppService.get('/task/' + taskid).then(
resp => {
if (resp.data.Status == 'Completed' || resp.data.Status == 'Failed')
notifyTaskCompleted(taskid);
},
// Ignore errors
resp => {
if (resp.status == 404)
notifyTaskCompleted(taskid);
}
);
}
this.callWhenTaskCompletes = function (taskid, callback) {
if (waitingfortask[taskid] == null)
{
waitingfortask[taskid] = [];
// Guard against cases where the taks state is incorrectly reported
// This happens on really fast completion, and also sometimes on longer running tasks
waitingfortaskTimers[taskid] = window.setInterval(() => {
if (waitingfortask[taskid] == null)
{
if (waitingfortaskTimers[taskid] != null) {
window.clearInterval(waitingfortaskTimers[taskid]);
delete waitingfortaskTimers[taskid];
}
return;
}
checkTaskState(taskid);
}, 1000);
}
waitingfortask[taskid].push(callback);
};
var lastTaskId = null;
$rootScope.$on('serverstatechanged.activeTask', function () {
var currentTaskId = state.activeTask == null ? null : state.activeTask.Item1;
// This appears to be broken, and causes the UI to hang
// We should re-write the task check system to use websockets and not poll
if (lastTaskId != null && currentTaskId != lastTaskId && waitingfortask[lastTaskId] != null) {
for (var i in waitingfortask[lastTaskId])
waitingfortask[lastTaskId][i]();
delete waitingfortask[lastTaskId];
}
// var lastTaskId = null;
// $rootScope.$on('serverstatechanged.activeTask', function () {
// var currentTaskId = state.activeTask == null ? null : state.activeTask.Item1;
lastTaskId = currentTaskId;
});
// if (lastTaskId != null && currentTaskId != lastTaskId && waitingfortask[lastTaskId] != null)
// checkTaskState(lastTaskId);
// lastTaskId = currentTaskId;
// });
var progressPollTimer = null;
var progressPollInProgress = false;
@@ -144,18 +193,18 @@ backupApp.service('ServerStatus', function ($rootScope, $timeout, AppService, Ap
}
};
var longPollRetryTimer = null;
var countdownForReconnect = function (m) {
if (longPollRetryTimer != null) {
window.clearInterval(longPollRetryTimer);
longPollRetryTimer = null;
let websocketReconnectTimer = null;
const countdownForReconnect = function (m) {
if (websocketReconnectTimer != null) {
window.clearInterval(websocketReconnectTimer);
websocketReconnectTimer = null;
}
var retryAt = new Date(new Date().getTime() + 15000);
const retryAt = new Date(new Date().getTime() + 15000);
state.connectionAttemptTimer = new Date() - retryAt;
$rootScope.$broadcast('serverstatechanged');
longPollRetryTimer = window.setInterval(function () {
websocketReconnectTimer = window.setInterval(function () {
state.connectionAttemptTimer = retryAt - new Date();
if (state.connectionAttemptTimer <= 0)
m();
@@ -230,6 +279,7 @@ backupApp.service('ServerStatus', function ($rootScope, $timeout, AppService, Ap
// Clear error indicators
state.failedConnectionAttempts = 0;
state.failedAuthAttempts = 0;
if (state.connectionState != 'connected') {
state.connectionState = 'connected';
@@ -248,69 +298,79 @@ backupApp.service('ServerStatus', function ($rootScope, $timeout, AppService, Ap
if (state.activeTask != null)
startUpdateProgressPoll();
if (!useWebsocket)
updateServerState(false);
}
const webSocketUnauthorizedCode = 4401;
const unauthorizedCode = 401;
function handleConnectionError(response) {
state.failedConnectionAttempts++;
var errorMessage = AppService.responseErrorMessage(response);
if (response.status === webSocketUnauthorizedCode || response.status === unauthorizedCode)
{
AppService.clearAccessToken();
state.failedAuthAttempts++;
}
// First failure, we ignore
if (state.connectionState == 'connected' && state.failedConnectionAttempts == 1) {
// Try again
countdownForReconnect(function() { updateServerState(); });
} else if (response.status == 401) {
// Change state to connected to hide the connecting message, which is on top of the login message from the AppService
state.connectionState = 'connected';
// Notify
updateServerState();
} else if (state.failedAuthAttempts > 2 && (response.status === webSocketUnauthorizedCode || response.status === unauthorizedCode)) {
state.connectionState = 'unauthorized';
$rootScope.$broadcast('serverstatechanged');
} else {
state.connectionState = 'disconnected';
// Notify
$rootScope.$broadcast('serverstatechanged');
countdownForReconnect(function() { updateServerState(); });
countdownForReconnect(function () {
updateServerState(true);
});
}
}
var updateServerState = function () {
var updateServerState = function (fastcall) {
if (state.connectionState !== 'connected') {
state.connectionState = 'connecting';
$rootScope.$broadcast('serverstatechanged');
}
const url = window.location.origin + '/api/v1/serverstate';
AppService.get(url, {timeout: state.lastEventId > 0 ? longpolltime : 5000}).then(
handleServerState, handleConnectionError
);
self.reconnect(fastcall);
};
updateServerState();
this.reconnect = function () {
console.log("Reconnecting websocket");
window.clearInterval(longPollRetryTimer);
console.log('ws://' + window.location.host + '/notifications?token=' + AppService.access_token)
const w = new WebSocket('ws://' + window.location.host + '/notifications?token=' + AppService.access_token)
w.addEventListener("open", (event) => {
state.connectionState = 'connected';
$rootScope.$broadcast('serverstatechanged.connectionState', state.connectionState);
$rootScope.$broadcast('serverstatechanged');
});
const reconnect_websocket = function () {
window.clearInterval(websocketReconnectTimer);
const w = new WebSocket(`ws://${window.location.host}/notifications?token=${AppService.access_token}`)
w.addEventListener("message", (event) => {
console.log("Message from server ", event.data);
const status = JSON.parse(event.data);
handleServerState({data: status});
});
w.addEventListener("close", (event) => {
console.log("Websocket closed. Reconnecting...", event);
handleConnectionError("Websocket disconnected.");
countdownForReconnect(() => websocket = this.reconnect());
window.websocket = null;
handleConnectionError({status: event.code});
});
return w;
};
AppService.access_token_promise.then(() => {
let websocket = this.reconnect()
window.websocket = websocket;
});
const reconnect_longpoll = function (fastcall) {
const url = '/serverstate/?lasteventid=' + parseInt(state.lastEventId) + '&longpoll=' + (((!fastcall) && (state.lastEventId > 0)) ? 'true' : 'false') + '&duration=' + parseInt((longpolltime-1000) / 1000) + 's';
AppService.get(url, fastcall ? null : {timeout: state.lastEventId > 0 ? longpolltime : 5000}).then(
handleServerState, handleConnectionError
);
}
this.reconnect = function (fastcall) {
AppService.getAccessToken().then(() => {
if (useWebsocket)
window.websocket = reconnect_websocket();
else
reconnect_longpoll(fastcall);
}, resp => {
handleConnectionError(resp);
});
}
this.reconnect();
});
@@ -37,15 +37,15 @@
<div class="input text">
<label for="openstack_tenantname" translate>Tenant Name</label>
<input type="text" name="openstack_tenantname" id="openstack_tenantname" ng-model="$parent.openstack_tenantname" placeholder="optional tenant name" title="{{'If you do not enter an API Key, the tenant name is required' | translate}}"/>
<input type="text" name="openstack_tenantname" id="openstack_tenantname" ng-model="$parent.openstack_tenantname" placeholder="{{'Optional tenant name' | translate}}" title="{{'If you do not enter an API Key, the tenant name is required' | translate}}"/>
</div>
<div class="input text">
<label for="openstack_apikey" translate>API Key</label>
<input type="text" name="openstack_apikey" id="openstack_apikey" ng-model="$parent.openstack_apikey" placeholder="optional api key" title="{{'Some OpenStack providers allow an API key instead of a password and tenant name' | translate}}" />
<input type="text" name="openstack_apikey" id="openstack_apikey" ng-model="$parent.openstack_apikey" placeholder="{{'Optional api key' | translate}}" title="{{'Some OpenStack providers allow an API key instead of a password and tenant name' | translate}}" />
</div>
<div class="input text">
<label for="openstack_region" translate>Container region</label>
<input type="text" name="openstack_region" id="openstack_region" ng-model="$parent.openstack_region" placeholder="optional region" title="{{'The region parameter is only used when creating a bucket' | translate}}"/>
<input type="text" name="openstack_region" id="openstack_region" ng-model="$parent.openstack_region" placeholder="{{'Optional region' | translate}}" title="{{'The region parameter is only used when creating a bucket' | translate}}"/>
</div>
@@ -1,13 +1,13 @@
<div class="input text">
<label for="rclone_local_repository" translate>Local repository</label>
<input type="text" name="rclone_local_repository" id="rclone_local_repository" ng-model="$parent.rclone_local_repository" placeholder="{{'local repository, eg local'}}" />
<input type="text" name="rclone_local_repository" id="rclone_local_repository" ng-model="$parent.rclone_local_repository" placeholder="{{'local repository, eg local' | translate}}" />
</div>
<div class="input text">
<label for="rclone_remote_repository" translate>Remote repository</label>
<input type="text" name="rclone_remote_repository" id="rclone_remote-repository" ng-model="$parent.Server" placeholder="{{'remote repository, eg. remote'}}" />
<input type="text" name="rclone_remote_repository" id="rclone_remote-repository" ng-model="$parent.Server" placeholder="{{'remote repository, eg. remote' | translate}}" />
</div>
<div class="input text">
<label for="rclone_remote_path" translate>Remote path</label>
<input type="text" name="rclone_remote_path" id="rclone_remote_path" ng-model="$parent.Path" placeholder="{{'remote path, eg backup'}}" />
<input type="text" name="rclone_remote_path" id="rclone_remote_path" ng-model="$parent.Path" placeholder="{{'remote path, eg backup' | translate}}" />
</div>
<div>Make sure that rclone is in your path, or add the location to rclone via the advanced options.</div>
<div>Make sure that rclone is in your path, or add the location to rclone via the advanced options.</div>
@@ -53,7 +53,7 @@
</select>
<input ng-hide="contains_value(s3_storageclasses, s3_storageclass)" type="text" id="s3_storageclass_custom"
ng-model="$parent.s3_storageclass_custom" placeholder="Custom bucket storage class"/>
ng-model="$parent.s3_storageclass_custom" placeholder="{{'Custom bucket storage class' | translate}}"/>
</div>
@@ -31,13 +31,13 @@
</div>
<div class="input text" ng-show="$parent.storj_auth_method == 'Access grant'">
<label for="storj_shared_access" translate>Access grant</label>
<input type="text" name="storj_shared_access" id="storj_shared_access" ng-model="$parent.storj_shared_access" placeholder="{{'The access grant instead of the info above'}}" />
<input type="text" name="storj_shared_access" id="storj_shared_access" ng-model="$parent.storj_shared_access" placeholder="{{'The access grant instead of the info above' | translate}}" />
</div>
<div class="input text">
<label for="storj_bucket" translate>Bucket</label>
<input type="text" name="storj_bucket" id="storj_bucket" ng-model="$parent.storj_bucket" placeholder="{{'The bucket for storing the backup'}}" />
<input type="text" name="storj_bucket" id="storj_bucket" ng-model="$parent.storj_bucket" placeholder="{{'The bucket for storing the backup' | translate}}" />
</div>
<div class="input text">
<label for="storj_folder" translate>Folder path</label>
<input type="text" name="storj_folder" id="storj_folder" ng-model="$parent.storj_folder" placeholder="{{'The folder within the bucket for storing the backup'}}" />
<input type="text" name="storj_folder" id="storj_folder" ng-model="$parent.storj_folder" placeholder="{{'The folder within the bucket for storing the backup' | translate}}" />
</div>
@@ -31,13 +31,13 @@
</div>
<div class="input text" ng-show="$parent.tardigrade_auth_method == 'Access grant'">
<label for="tardigrade_shared_access" translate>Access grant</label>
<input type="text" name="tardigrade_shared_access" id="tardigrade_shared_access" ng-model="$parent.tardigrade_shared_access" placeholder="{{'The access grant instead of the info above'}}" />
<input type="text" name="tardigrade_shared_access" id="tardigrade_shared_access" ng-model="$parent.tardigrade_shared_access" placeholder="{{'The access grant instead of the info above' | translate}}" />
</div>
<div class="input text">
<label for="tardigrade_bucket" translate>Bucket</label>
<input type="text" name="tardigrade_bucket" id="tardigrade_bucket" ng-model="$parent.tardigrade_bucket" placeholder="{{'The bucket for storing the backup'}}" />
<input type="text" name="tardigrade_bucket" id="tardigrade_bucket" ng-model="$parent.tardigrade_bucket" placeholder="{{'The bucket for storing the backup' | translate}}" />
</div>
<div class="input text">
<label for="tardigrade_folder" translate>Folder path</label>
<input type="text" name="tardigrade_folder" id="tardigrade_folder" ng-model="$parent.tardigrade_folder" placeholder="{{'The folder within the bucket for storing the backup'}}" />
<input type="text" name="tardigrade_folder" id="tardigrade_folder" ng-model="$parent.tardigrade_folder" placeholder="{{'The folder within the bucket for storing the backup' | translate}}" />
</div>
@@ -14,22 +14,22 @@
<div class="input textarea linklabel">
<label for="target"><a href class="target" ng-click="EditUriState = true">Target URL &gt;</a></label>
<textarea name="target" id="target" ng-model="TargetURL" placeholder="Enter a url, or click the 'Target URL &gt;' link"></textarea>
<textarea name="target" id="target" ng-model="TargetURL" placeholder="{{'Enter a url, or click the \'Target URL &gt;\' link | translate}}"></textarea>
</div>
<div class="input textarea">
<label for="arguments">Commandline arguments</label>
<textarea id="arguments" ng-model="Arguments" string-array-as-text placeholder="Enter one argument per line without quotes, e.g. *.txt"></textarea>
<textarea id="arguments" ng-model="Arguments" string-array-as-text placeholder="{{'Enter one argument per line without quotes, e.g. *.txt' | translate}}"></textarea>
</div>
<div class="input textarea" ng-show="ShowAdvancedTextArea">
<label for="backupOptions">Advanced options</label>
<div>
<a href ng-click="ShowAdvancedTextArea = false" class="advanced-toggle"><i class="fa fa-check"></i> Edit as text</a>
<textarea id="backupOptions" ng-model="ExtendedOptions" string-array-as-text placeholder="Enter one option per line in command-line format, eg. --dblock-size=100MB"></textarea>
<textarea id="backupOptions" ng-model="ExtendedOptions" string-array-as-text placeholder="{{'Enter one option per line in command-line format, eg. --dblock-size=100MB' | translate}}"></textarea>
</div>
</div>
<div class="input" ng-hide="ShowAdvancedTextArea">
<label for="backupOptions">Advanced options</label>
<a href ng-click="ShowAdvancedTextArea = true" class="advanced-toggle"><i class="fa"></i> Edit as text</a>
+3 -2
View File
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>Duplicati.Service.Implementation</AssemblyName>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Library\Utility\Duplicati.Library.Utility.csproj" />
@@ -16,4 +17,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
+3 -2
View File
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -31,4 +32,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
+3 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
@@ -6,6 +6,7 @@
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
<RootNamespace>Duplicati.UnitTest</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -44,3 +45,4 @@
</ItemGroup>
</Project>
@@ -4,8 +4,7 @@ namespace Duplicati.WebserverCore.Abstractions.Notifications;
public interface IWebsocketAccessor
{
void AddConnection(WebSocket newConnection);
WebSocket[] OpenConnections { get; }
Task AddConnection(WebSocket newConnection);
Task Send<T>(T data);
Task HandleClientMessage(string message);
}
@@ -7,6 +7,7 @@
<Nullable>enable</Nullable>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<RootNamespace>Duplicati.WebserverCore</RootNamespace>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -23,3 +24,4 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.3" />
</ItemGroup>
</Project>
@@ -162,8 +162,12 @@ public partial class DuplicatiWebserver
public Task Start(InitSettings settings)
{
var allowedOrigins = settings.AllowedHostnames.Any(x => x == "*")
? []
: settings.AllowedHostnames.Select(x => settings.Certificate == null ? $"http://{x}:{settings.Port}" : $"https://{x}:{settings.Port}");
App.AddEndpoints()
.UseNotifications(settings.AllowedHostnames, "/notifications");
.UseNotifications(allowedOrigins, "/notifications");
return App.RunAsync();
}
+5 -7
View File
@@ -33,13 +33,12 @@ public partial class Auth : IEndpointV1
catch (Exception ex)
{
Log.WriteErrorMessage(LOGTAG, "RefreshTokenError", ex, "Failed to refresh token");
if (ex is UserReportedHttpException)
throw;
// Any error with the refresh token should be treated as unauthorized
throw new UnauthorizedException("Failed to refresh token");
}
}
throw new UnauthorizedException("Failed to refresh token");
throw new UnauthorizedException("Authorization failed due to missing cookie.");
});
group.MapPost("auth/signin", async ([FromServices] ILoginProvider loginProvider, [FromServices] JWTConfig jWTConfig, [FromServices] IHttpContextAccessor httpContextAccessor, [FromBody] Dto.SigninInputDto input, CancellationToken ct) =>
@@ -55,8 +54,8 @@ public partial class Auth : IEndpointV1
catch (Exception ex)
{
Log.WriteErrorMessage(LOGTAG, "SigninTokenError", ex, "Failed to sign in");
if (ex is UserReportedHttpException)
throw;
// Any error with the signin token should be treated as unauthorized
if (ex is SecurityTokenExpiredException)
throw new UnauthorizedException("Signin token expired");
@@ -77,8 +76,7 @@ public partial class Auth : IEndpointV1
catch (Exception ex)
{
Log.WriteErrorMessage(LOGTAG, "PasswordLoginError", ex, "Failed to log in");
if (ex is UserReportedHttpException)
throw;
// Any error with the password should be treated as unauthorized
throw new UnauthorizedException("Failed to log in");
}
});
@@ -1,6 +1,6 @@
using Duplicati.Library.RestAPI;
using Duplicati.Server;
using Duplicati.WebserverCore.Abstractions;
using Duplicati.WebserverCore.Dto;
using Duplicati.WebserverCore.Exceptions;
using Microsoft.AspNetCore.Mvc;
@@ -10,18 +10,26 @@ public class ServerState : IEndpointV1
{
public static void Map(RouteGroupBuilder group)
{
group.MapGet("/serverstate", ([FromQuery] long? lastEventId, [FromQuery] bool? longpoll, string? duration, [FromServices] IStatusService statusService)
=> Execute(lastEventId, longpoll ?? false, duration, statusService))
group.MapGet("/serverstate",
([FromQuery] long? lastEventId, [FromQuery] bool? longpoll, string? duration,
[FromServices] IStatusService statusService, [FromServices] EventPollNotify statusEventNotifier)
=> Execute(lastEventId, longpoll ?? false, duration, statusService, statusEventNotifier))
.RequireAuthorization();
group.MapPost("/serverstate/pause", ([FromServices] IStatusService statusService, [FromServices] LiveControls liveControls, [FromQuery] string? duration) => ExecutePause(statusService, liveControls, duration)).RequireAuthorization();
group.MapPost("/serverstate/resume", ([FromServices] IStatusService statusService, [FromServices] LiveControls liveControls) => ExecuteResume(statusService, liveControls)).RequireAuthorization();
group.MapPost("/serverstate/pause",
([FromServices] IStatusService statusService, [FromServices] LiveControls liveControls,
[FromQuery] string? duration) => ExecutePause(liveControls, duration)).RequireAuthorization();
group.MapPost("/serverstate/resume",
([FromServices] IStatusService statusService, [FromServices] LiveControls liveControls) =>
ExecuteResume(liveControls)).RequireAuthorization();
}
private static Dto.ServerStatusDto Execute(long? lastEventId, bool longpoll, string? duration, IStatusService statusService)
private static ServerStatusDto Execute(long? lastEventId, bool longpoll, string? duration,
IStatusService statusService, EventPollNotify statusEventNotifier)
{
var status = statusService.GetStatus();
if (longpoll == true)
if (longpoll)
{
if (lastEventId == null)
throw new BadRequestException("When activating long poll, the request must include the last event id");
@@ -29,27 +37,41 @@ public class ServerState : IEndpointV1
if (duration == null)
throw new BadRequestException("When activating long poll, the request must include the duration");
var ts = TimeSpan.Zero;
try { ts = Library.Utility.Timeparser.ParseTimeSpan(duration); }
catch { throw new BadRequestException("When activating long poll, the request must include the duration and it must be a valid time span"); }
TimeSpan ts;
try
{
ts = Library.Utility.Timeparser.ParseTimeSpan(duration);
}
catch
{
throw new BadRequestException(
"When activating long poll, the request must include the duration and it must be a valid time span");
}
if (ts.TotalSeconds < 10 || ts.TotalSeconds > 3600)
if (ts.TotalSeconds is < 10 or > 3600)
throw new BadRequestException("The duration must be between 10 seconds and 1 hour");
var id = FIXMEGlobal.StatusEventNotifyer.Wait(lastEventId.Value, (int)ts.TotalMilliseconds);
var id = statusEventNotifier.Wait(lastEventId.Value, (int)ts.TotalMilliseconds);
// If the id changed, regenerate the response object
if (id != lastEventId)
status = statusService.GetStatus();
}
return status;
}
private static void ExecutePause(IStatusService statusService, LiveControls liveControls, string? duration)
private static void ExecutePause(LiveControls liveControls, string? duration)
{
var ts = TimeSpan.Zero;
if (duration != null)
try { ts = Library.Utility.Timeparser.ParseTimeSpan(duration); }
catch { throw new BadRequestException("The duration must be a valid time span"); }
try
{
ts = Library.Utility.Timeparser.ParseTimeSpan(duration);
}
catch
{
throw new BadRequestException("The duration must be a valid time span");
}
if (ts.TotalMilliseconds > 0)
liveControls.Pause(ts);
@@ -57,7 +79,7 @@ public class ServerState : IEndpointV1
liveControls.Pause();
}
private static void ExecuteResume(IStatusService statusService, LiveControls liveControls)
private static void ExecuteResume(LiveControls liveControls)
{
liveControls.Resume();
}
@@ -1,19 +1,17 @@
using System.Net.WebSockets;
using System.Text;
using Duplicati.WebserverCore.Abstractions;
using Duplicati.WebserverCore.Abstractions.Notifications;
using Duplicati.WebserverCore.Exceptions;
namespace Duplicati.WebserverCore.Middlewares;
public static class WebsocketExtensions
{
public static IApplicationBuilder UseNotifications(this IApplicationBuilder app, IEnumerable<string> allowedHostnames, string notificationPath)
public static IApplicationBuilder UseNotifications(this IApplicationBuilder app, IEnumerable<string> allowedOrigins, string notificationPath)
{
var opts = new WebSocketOptions();
if (!allowedHostnames.Any(x => x == "*"))
foreach (var allowedHostname in allowedHostnames)
opts.AllowedOrigins.Add(allowedHostname);
if (allowedOrigins.Any())
foreach (var origin in allowedOrigins)
opts.AllowedOrigins.Add(origin);
app.UseWebSockets(opts);
return app.Use(async (context, next) =>
@@ -26,7 +24,9 @@ public static class WebsocketExtensions
{
if (context.User.Identity?.IsAuthenticated == false)
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await webSocket.CloseAsync((WebSocketCloseStatus)4401, "User is not authenticated!",
CancellationToken.None);
return;
}
@@ -34,7 +34,7 @@ public static class WebsocketExtensions
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
websocketAccessor.AddConnection(webSocket);
await websocketAccessor.AddConnection(webSocket);
await HandleClientData(webSocket, websocketAccessor);
}
else
@@ -45,7 +45,8 @@ public static class WebsocketExtensions
});
}
private static async Task HandleClientData(WebSocket webSocket, IWebsocketAccessor websocketAccessor, CancellationToken cancellationToken = default)
private static async Task HandleClientData(WebSocket webSocket, IWebsocketAccessor websocketAccessor,
CancellationToken cancellationToken = default)
{
var buffer = new byte[1024 * 4];
@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System.Net.WebSockets;
using Duplicati.Server;
using Duplicati.WebserverCore.Abstractions;
@@ -9,34 +10,42 @@ namespace Duplicati.WebserverCore.Notifications;
public class WebsocketAccessor : IWebsocketAccessor
{
private List<WebSocket> _connections = new();
private readonly ConcurrentBag<WebSocket> _connections = new();
private readonly JsonSerializerSettings m_jsonSettings;
private readonly IStatusService _statusService;
public WebsocketAccessor(JsonSerializerSettings jsonSettings, EventPollNotify eventPollNotify, IServiceProvider serviceProvider)
public WebsocketAccessor(JsonSerializerSettings jsonSettings, EventPollNotify eventPollNotify,
IStatusService statusService)
{
m_jsonSettings = jsonSettings;
eventPollNotify.NewEvent += async (_, _) =>
{
using var scope = serviceProvider.CreateScope();
var statusService = scope.ServiceProvider.GetRequiredService<IStatusService>();
await Send(statusService.GetStatus());
};
_statusService = statusService;
eventPollNotify.NewEvent += async (_, _) => { await Send(_statusService.GetStatus()); };
}
public void AddConnection(WebSocket newConnection)
public async Task AddConnection(WebSocket newConnection)
{
await SendInitialStatus(newConnection);
_connections.Add(newConnection);
ClearClosed();
}
private async Task SendInitialStatus(WebSocket connection)
=> await Send(_statusService.GetStatus(), [connection]);
private void ClearClosed()
{
_connections = _connections.Where(c => c.State == WebSocketState.Open).ToList();
var openConnections = _connections.Where(c => c.State == WebSocketState.Open).ToArray();
_connections.Clear();
foreach (var connection in openConnections)
{
if (!_connections.Contains(connection))
{
_connections.Add(connection);
}
}
}
public WebSocket[] OpenConnections => Connections.ToArray();
private IEnumerable<WebSocket> Connections
{
get
@@ -46,19 +55,23 @@ public class WebsocketAccessor : IWebsocketAccessor
}
}
public async Task Send<T>(T data)
private Task Send<T>(T data, IEnumerable<WebSocket> connections)
{
var json = JsonConvert.SerializeObject(data, m_jsonSettings);
var bytes = json.GetBytes();
foreach (var webSocket in Connections)
{
await webSocket.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None);
}
return Task.WhenAll(
connections
.Where(c => c.State == WebSocketState.Open)
.Select(c => c.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None))
);
}
public async Task HandleClientMessage(string message)
public Task Send<T>(T data) => Send(data, Connections);
public Task HandleClientMessage(string message)
{
//TODO: handle client message
return Task.CompletedTask;
}
}
@@ -3,60 +3,39 @@ using Duplicati.Library.RestAPI.Abstractions;
using Duplicati.Server;
using Duplicati.Server.Serialization;
using Duplicati.WebserverCore.Abstractions;
using Duplicati.WebserverCore.Abstractions.Notifications;
using Duplicati.WebserverCore.Dto;
namespace Duplicati.WebserverCore.Services;
public class StatusService : IStatusService
public class StatusService(
LiveControls liveControls,
UpdatePollThread updatePollThread,
IUpdateService updateService,
IWorkerThreadsManager workerThreadsManager,
ISettingsService settingsService,
IScheduler scheduler,
EventPollNotify eventPollNotify,
INotificationUpdateService notificationUpdateService)
: IStatusService
{
private readonly LiveControls m_liveControls;
private readonly UpdatePollThread m_updatePollThread;
private readonly IUpdateService m_updateService;
private readonly IWorkerThreadsManager m_workerThreadsManager;
private readonly ISettingsService m_settingsService;
private readonly IScheduler m_scheduler;
private readonly EventPollNotify m_eventPollNotify;
private readonly INotificationUpdateService m_notificationUpdateService;
public StatusService(LiveControls liveControls,
UpdatePollThread updatePollThread,
IUpdateService updateService,
IWorkerThreadsManager workerThreadsManager,
ISettingsService settingsService,
IScheduler scheduler,
EventPollNotify eventPollNotify,
INotificationUpdateService notificationUpdateService,
IWebsocketAccessor websocketAccessor)
{
m_liveControls = liveControls;
m_updatePollThread = updatePollThread;
m_updateService = updateService;
m_workerThreadsManager = workerThreadsManager;
m_settingsService = settingsService;
m_scheduler = scheduler;
m_eventPollNotify = eventPollNotify;
m_notificationUpdateService = notificationUpdateService;
}
public ServerStatusDto GetStatus()
{
var status = new ServerStatusDto
{
UpdatedVersion = GetUpdatedVersion(),
UpdaterState = m_updatePollThread.ThreadState,
UpdateDownloadProgress = m_updatePollThread.DownloadProgess,
ActiveTask = m_workerThreadsManager.CurrentTask,
SchedulerQueueIds = m_scheduler.GetSchedulerQueueIds(),
LastEventID = m_eventPollNotify.EventNo,
LastDataUpdateID = m_notificationUpdateService.LastDataUpdateId,
LastNotificationUpdateID = m_notificationUpdateService.LastNotificationUpdateId,
UpdaterState = updatePollThread.ThreadState,
UpdateDownloadProgress = updatePollThread.DownloadProgess,
ActiveTask = workerThreadsManager.CurrentTask,
SchedulerQueueIds = scheduler.GetSchedulerQueueIds(),
LastEventID = eventPollNotify.EventNo,
LastDataUpdateID = notificationUpdateService.LastDataUpdateId,
LastNotificationUpdateID = notificationUpdateService.LastNotificationUpdateId,
ProgramState = GetProgramState(),
HasWarning = m_settingsService.GetSettings().UnackedWarning,
HasError = m_settingsService.GetSettings().UnackedError,
EstimatedPauseEnd = m_liveControls.EstimatedPauseEnd,
HasWarning = settingsService.GetSettings().UnackedWarning,
HasError = settingsService.GetSettings().UnackedError,
EstimatedPauseEnd = liveControls.EstimatedPauseEnd,
SuggestedStatusIcon = MapStateToIcon(),
UpdateDownloadLink = m_settingsService.GetSettings().UpdateCheckNewVersion
UpdateDownloadLink = settingsService.GetSettings().UpdateCheckNewVersion
};
PullSettings(status);
PullLiveControls(status);
@@ -65,20 +44,20 @@ public class StatusService : IStatusService
private void PullLiveControls(ServerStatusDto status)
{
status.EstimatedPauseEnd = m_liveControls.EstimatedPauseEnd;
status.EstimatedPauseEnd = liveControls.EstimatedPauseEnd;
status.ProgramState = GetProgramState();
status.SuggestedStatusIcon = MapStateToIcon();
}
private SuggestedStatusIcon MapStateToIcon()
{
if (m_workerThreadsManager.CurrentTask == null)
if (workerThreadsManager.CurrentTask == null)
{
if (m_liveControls.State == LiveControls.LiveControlState.Paused)
if (liveControls.State == LiveControls.LiveControlState.Paused)
return SuggestedStatusIcon.Paused;
//TODO: why settings have some errors or warning? those are not settings. Should be moved to some kind of overall system status service
var settings = m_settingsService.GetSettings();
var settings = settingsService.GetSettings();
if (settings.UnackedError)
return SuggestedStatusIcon.ReadyError;
@@ -88,31 +67,31 @@ public class StatusService : IStatusService
return SuggestedStatusIcon.Ready;
}
return m_liveControls.State == LiveControls.LiveControlState.Running
return liveControls.State == LiveControls.LiveControlState.Running
? SuggestedStatusIcon.Active
: SuggestedStatusIcon.ActivePaused;
}
private void PullSettings(ServerStatusDto status)
{
status.HasError = m_settingsService.GetSettings().UnackedError;
status.HasWarning = m_settingsService.GetSettings().UnackedWarning;
status.UpdateDownloadLink = m_settingsService.GetSettings().UpdateCheckNewVersion;
status.HasError = settingsService.GetSettings().UnackedError;
status.HasWarning = settingsService.GetSettings().UnackedWarning;
status.UpdateDownloadLink = settingsService.GetSettings().UpdateCheckNewVersion;
}
private string? GetUpdatedVersion()
{
return m_updateService.GetUpdateInfo()?.Version;
return updateService.GetUpdateInfo()?.Version;
}
private LiveControlState GetProgramState()
{
return m_liveControls.State switch
return liveControls.State switch
{
LiveControls.LiveControlState.Running => LiveControlState.Running,
LiveControls.LiveControlState.Paused => LiveControlState.Paused,
_ => throw new ArgumentOutOfRangeException(
$"Value of {m_liveControls.State} could not be converted to {nameof(LiveControlState)}")
$"Value of {liveControls.State} could not be converted to {nameof(LiveControlState)}")
};
}
}
@@ -38,7 +38,7 @@ public class TokenFamilyStore(Connection connection) : ITokenFamilyStore
family = new ITokenFamilyStore.TokenFamily(reader.GetString(0), reader.GetString(1), reader.GetInt32(2));
});
return Task.FromResult(family ?? throw new Exceptions.NotFoundException("Token family not found"));
return Task.FromResult(family ?? throw new Exceptions.UnauthorizedException("Token family not found"));
}
public Task<ITokenFamilyStore.TokenFamily> IncrementTokenFamily(ITokenFamilyStore.TokenFamily tokenFamily, CancellationToken ct)
@@ -1,6 +0,0 @@
namespace Duplicati.WebserverCore;
public class TemporaryIoCAccessor
{
public static IServiceProvider Provider { get; set; }
}
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>Duplicati.WindowsService.Implementation</AssemblyName>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -27,4 +28,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Description>AutoUpdater tool for Duplicati</Description>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -17,4 +18,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Description>A backend debugging tool for Duplicati</Description>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -17,4 +18,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Description>Tool for file-level access to remote destinations</Description>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -17,4 +18,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Description>Configuration Import Tool for Duplicati</Description>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -17,4 +18,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Description>RecoveryTool for Duplicati</Description>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -17,4 +18,4 @@
</PackageReference>
</ItemGroup>
</Project>
</Project>
@@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Description>The Server SharpAESCrypt implementation</Description>
<AssemblyName>Duplicati.CommandLine.SharpAESCrypt</AssemblyName>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -18,4 +19,4 @@
<ProjectReference Include="..\..\..\Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj" />
</ItemGroup>
</Project>
</Project>
@@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Description>The Server Snapshots implementation</Description>
<AssemblyName>Duplicati.CommandLine.Snapshots</AssemblyName>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -18,4 +19,4 @@
<ProjectReference Include="..\..\..\Duplicati\Library\Snapshots\Duplicati.Library.Snapshots.csproj" />
</ItemGroup>
</Project>
</Project>
@@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Description>Commandline implementation of Duplicati</Description>
<Copyright>Copyright © 2024 Team Duplicati, MIT license</Copyright>
</PropertyGroup>
<ItemGroup>
@@ -18,3 +19,4 @@
</ItemGroup>
</Project>

Some files were not shown because too many files have changed in this diff Show More