Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
621aada0d1 | ||
|
|
e5a3fed155 | ||
|
|
67b133a206 | ||
|
|
5b1df6fa61 | ||
|
|
f50b49c56e | ||
|
|
e05888e557 | ||
|
|
c36ec0cbc5 | ||
|
|
b968dba9ce | ||
|
|
7a85b1763f | ||
|
|
599b4124f2 | ||
|
|
037e9e179f | ||
|
|
b9d6084973 | ||
|
|
c52f08bc6e | ||
|
|
85d5b1d74e | ||
|
|
63fc93ab38 | ||
|
|
790f9c06fa | ||
|
|
12407d4c03 | ||
|
|
090ecf1af6 | ||
|
|
105f6e16ff | ||
|
|
499a3ad575 | ||
|
|
f0107b476a | ||
|
|
b7cac4cd51 | ||
|
|
b55ba094b6 | ||
|
|
fecb2bf25a | ||
|
|
877b1c811f | ||
|
|
3bb06311a8 | ||
|
|
8d96681db6 | ||
|
|
f458355663 | ||
|
|
e9d7433a72 | ||
|
|
6195e07808 | ||
|
|
53059c01dd | ||
|
|
bb274822cc | ||
|
|
f74cee2932 | ||
|
|
92f1de07ae | ||
|
|
f040df8e2f | ||
|
|
ab7e640435 | ||
|
|
27ce549e75 | ||
|
|
09d30b92e0 | ||
|
|
bc0a47f768 | ||
|
|
75bab41034 | ||
|
|
94d6ab76ee | ||
|
|
1d913e026f | ||
|
|
974f6bff2f | ||
|
|
0ae9990f18 | ||
|
|
18ff96c5fe | ||
|
|
b39a68bda7 | ||
|
|
60437d7fc0 | ||
|
|
618d9015ab | ||
|
|
9b19bac7da | ||
|
|
6917030417 | ||
|
|
44a45fcd04 | ||
|
|
59b00a5a45 | ||
|
|
31f0cd02ab |
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
@@ -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)))
|
||||
|
||||
});
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
+3
-3
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 ></a></label>
|
||||
<textarea name="target" id="target" ng-model="TargetURL" placeholder="Enter a url, or click the 'Target URL >' link"></textarea>
|
||||
<textarea name="target" id="target" ng-model="TargetURL" placeholder="{{'Enter a url, or click the \'Target URL >\' 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
+3
-2
@@ -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>
|
||||
|
||||
+3
-2
@@ -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
Reference in New Issue
Block a user