More FIXMEGlobal cleanup
This commit is contained in:
@@ -48,12 +48,7 @@ namespace Duplicati.Library.RestAPI
|
||||
/// <summary>
|
||||
/// A delegate method for creating a copy of the current progress state
|
||||
/// </summary>
|
||||
public static Func<Duplicati.Server.Serialization.Interface.IProgressEventData> GenerateProgressState;
|
||||
|
||||
/// <summary>
|
||||
/// The status event signaler, used to control long polling of status updates
|
||||
/// </summary>
|
||||
public static EventPollNotify StatusEventNotifyer => Provider.GetRequiredService<EventPollNotify>();
|
||||
public static Func<Server.Serialization.Interface.IProgressEventData> GenerateProgressState;
|
||||
|
||||
/// <summary>
|
||||
/// For keeping and incrementing last last events Ids of db save and last notification
|
||||
@@ -71,28 +66,16 @@ namespace Duplicati.Library.RestAPI
|
||||
/// </summary>
|
||||
public static string DataFolder;
|
||||
|
||||
/// <summary>
|
||||
/// This is the scheduling thread
|
||||
/// </summary>
|
||||
public static ISchedulerService Scheduler => Provider.GetRequiredService<ISchedulerService>();
|
||||
|
||||
/// <summary>
|
||||
/// The log redirect handler
|
||||
/// </summary>
|
||||
public static readonly LogWriteHandler LogHandler = new LogWriteHandler();
|
||||
|
||||
/// <summary>
|
||||
/// The update poll thread.
|
||||
/// </summary>
|
||||
public static UpdatePollThread UpdatePoller => Provider.GetRequiredService<UpdatePollThread>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used to check the origin of the web server (e.g. Tray icon or a stand alone Server)
|
||||
/// </summary>
|
||||
public static string Origin = "Server";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The application exit event
|
||||
/// </summary>
|
||||
|
||||
@@ -29,6 +29,8 @@ using Duplicati.Server.Serialization;
|
||||
using Duplicati.Library.RestAPI;
|
||||
using Duplicati.Library.Utility;
|
||||
using System.Threading.Tasks;
|
||||
using Duplicati.Server.Database;
|
||||
using Duplicati.Server.Serialization.Interface;
|
||||
|
||||
namespace Duplicati.Server
|
||||
{
|
||||
@@ -52,6 +54,12 @@ namespace Duplicati.Server
|
||||
public Func<Task>? OnStarting { get; set; }
|
||||
public Func<Exception?, Task>? OnFinished { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback to be executed when the task is finished.
|
||||
/// Only supported for the delete operation for now.
|
||||
/// </summary>
|
||||
internal Action<IRunnerData>? AfterTaskFinished { get; set; }
|
||||
|
||||
public DuplicatiOperation Operation { get; internal set; }
|
||||
public Serialization.Interface.IBackup? Backup { get; internal set; }
|
||||
public IDictionary<string, string?>? ExtraOptions { get; internal set; }
|
||||
@@ -141,9 +149,6 @@ namespace Duplicati.Server
|
||||
{
|
||||
m_taskID = System.Threading.Interlocked.Increment(ref RunnerTaskID);
|
||||
}
|
||||
|
||||
public Task Execute()
|
||||
=> Task.Run(() => Runner.Run(this, true));
|
||||
}
|
||||
|
||||
private class CustomRunnerTask : RunnerData
|
||||
@@ -166,7 +171,7 @@ namespace Duplicati.Server
|
||||
return new CustomRunnerTask(runner);
|
||||
}
|
||||
|
||||
public static IRunnerData CreateTask(DuplicatiOperation operation, Serialization.Interface.IBackup backup, IDictionary<string, string?>? extraOptions = null, string[]? filterStrings = null, string[]? extraArguments = null, int pageSize = 0, int pageOffset = 0)
|
||||
public static IRunnerData CreateTask(DuplicatiOperation operation, IBackup backup, IDictionary<string, string?>? extraOptions = null, string[]? filterStrings = null, string[]? extraArguments = null, int pageSize = 0, int pageOffset = 0)
|
||||
{
|
||||
return new RunnerData()
|
||||
{
|
||||
@@ -180,7 +185,18 @@ namespace Duplicati.Server
|
||||
};
|
||||
}
|
||||
|
||||
public static IRunnerData CreateListTask(Serialization.Interface.IBackup backup, string[]? filters, bool onlyPrefix, bool allVersions, bool folderContents, DateTime time)
|
||||
public static IRunnerData CreateDeleteTask(IBackup backup, IDictionary<string, string?> extraOptions, Action<IRunnerData>? afterTaskFinished = null)
|
||||
{
|
||||
return new RunnerData()
|
||||
{
|
||||
Operation = DuplicatiOperation.Delete,
|
||||
Backup = backup,
|
||||
ExtraOptions = extraOptions,
|
||||
AfterTaskFinished = afterTaskFinished
|
||||
};
|
||||
}
|
||||
|
||||
public static IRunnerData CreateListTask(IBackup backup, string[]? filters, bool onlyPrefix, bool allVersions, bool folderContents, DateTime time)
|
||||
{
|
||||
var dict = new Dictionary<string, string?>();
|
||||
if (onlyPrefix)
|
||||
@@ -199,7 +215,7 @@ namespace Duplicati.Server
|
||||
filters);
|
||||
}
|
||||
|
||||
public static IRunnerData CreateListFilesetsTask(Serialization.Interface.IBackup backup, Dictionary<string, string?>? extraOptions = null)
|
||||
public static IRunnerData CreateListFilesetsTask(IBackup backup, Dictionary<string, string?>? extraOptions = null)
|
||||
{
|
||||
return CreateTask(
|
||||
DuplicatiOperation.ListFilesets,
|
||||
@@ -207,7 +223,7 @@ namespace Duplicati.Server
|
||||
extraOptions ?? new Dictionary<string, string?>());
|
||||
}
|
||||
|
||||
public static IRunnerData CreateListFolderContents(Serialization.Interface.IBackup backup, string[] folders, DateTime time, int pageSize, int pageOffset)
|
||||
public static IRunnerData CreateListFolderContents(IBackup backup, string[]? folders, DateTime time, int pageSize, int pageOffset)
|
||||
{
|
||||
var dict = new Dictionary<string, string?>();
|
||||
if (time.Ticks > 0)
|
||||
@@ -222,7 +238,7 @@ namespace Duplicati.Server
|
||||
pageOffset: pageOffset);
|
||||
}
|
||||
|
||||
public static IRunnerData ListFileVersionsTask(Serialization.Interface.IBackup backup, string[] filepaths, int pageSize, int pageOffset)
|
||||
public static IRunnerData ListFileVersionsTask(IBackup backup, string[]? filepaths, int pageSize, int pageOffset)
|
||||
{
|
||||
var dict = new Dictionary<string, string?>();
|
||||
return CreateTask(
|
||||
@@ -234,7 +250,7 @@ namespace Duplicati.Server
|
||||
pageOffset: pageOffset);
|
||||
}
|
||||
|
||||
public static IRunnerData CreateSearchEntriesTask(Serialization.Interface.IBackup backup, string[] filters, string[] folders, DateTime time, int pageSize, int pageOffset)
|
||||
public static IRunnerData CreateSearchEntriesTask(IBackup backup, string[]? filters, string[]? folders, DateTime time, int pageSize, int pageOffset)
|
||||
{
|
||||
var dict = new Dictionary<string, string?>();
|
||||
if (time.Ticks > 0)
|
||||
@@ -251,7 +267,7 @@ namespace Duplicati.Server
|
||||
}
|
||||
|
||||
|
||||
public static IRunnerData CreateRestoreTask(Serialization.Interface.IBackup backup, string[]? filters,
|
||||
public static IRunnerData CreateRestoreTask(IBackup backup, string[]? filters,
|
||||
DateTime time, string? restoreTarget, bool overwrite, bool restore_permissions,
|
||||
bool skip_metadata, string? passphrase)
|
||||
{
|
||||
@@ -276,7 +292,7 @@ namespace Duplicati.Server
|
||||
}
|
||||
private class MessageSink : Library.Main.IMessageSink
|
||||
{
|
||||
private class ProgressState : Serialization.Interface.IProgressEventData
|
||||
private class ProgressState : IProgressEventData
|
||||
{
|
||||
private readonly string? m_backupID;
|
||||
private readonly long m_taskID;
|
||||
@@ -394,18 +410,18 @@ namespace Duplicati.Server
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static string GetCommandLine(IRunnerData data)
|
||||
public static string GetCommandLine(Connection databaseConnection, IRunnerData data)
|
||||
{
|
||||
var backup = data.Backup;
|
||||
if (backup == null)
|
||||
throw new ArgumentNullException(nameof(backup));
|
||||
|
||||
var options = ApplyOptions(backup, GetCommonOptions());
|
||||
var options = ApplyOptions(databaseConnection, backup, GetCommonOptions(databaseConnection));
|
||||
if (data.ExtraOptions != null)
|
||||
foreach (var k in data.ExtraOptions)
|
||||
options[k.Key] = k.Value;
|
||||
|
||||
var cf = FIXMEGlobal.DataConnection.Filters;
|
||||
var cf = databaseConnection.Filters;
|
||||
var bf = backup.Filters;
|
||||
|
||||
var sources =
|
||||
@@ -440,18 +456,18 @@ namespace Duplicati.Server
|
||||
return cmd.ToString();
|
||||
}
|
||||
|
||||
public static string[] GetCommandLineParts(IRunnerData data)
|
||||
public static string[] GetCommandLineParts(Connection databaseConnection, IRunnerData data)
|
||||
{
|
||||
var backup = data.Backup;
|
||||
if (backup == null)
|
||||
throw new ArgumentNullException(nameof(backup));
|
||||
|
||||
var options = ApplyOptions(backup, GetCommonOptions());
|
||||
var options = ApplyOptions(databaseConnection, backup, GetCommonOptions(databaseConnection));
|
||||
if (data.ExtraOptions != null)
|
||||
foreach (var k in data.ExtraOptions)
|
||||
options[k.Key] = k.Value;
|
||||
|
||||
var cf = FIXMEGlobal.DataConnection.Filters;
|
||||
var cf = databaseConnection.Filters;
|
||||
var bf = backup.Filters;
|
||||
|
||||
var sources =
|
||||
@@ -480,7 +496,15 @@ namespace Duplicati.Server
|
||||
return parts.ToArray();
|
||||
}
|
||||
|
||||
public static IBasicResults? Run(IRunnerData data, bool fromQueue)
|
||||
public static IBasicResults? Run(Connection databaseConnection, EventPollNotify eventPollNotify, IQueuedTask data, bool fromQueue)
|
||||
{
|
||||
if (data is IRunnerData runnerData)
|
||||
return Run(databaseConnection, eventPollNotify, runnerData, fromQueue);
|
||||
|
||||
throw new ArgumentException("Invalid task type", nameof(data));
|
||||
}
|
||||
|
||||
private static IBasicResults? Run(Connection databaseConnection, EventPollNotify eventPollNotify, IRunnerData data, bool fromQueue)
|
||||
{
|
||||
data.TaskStarted = DateTime.Now;
|
||||
if (data is CustomRunnerTask task)
|
||||
@@ -489,13 +513,13 @@ namespace Duplicati.Server
|
||||
{
|
||||
var sink = new MessageSink(task.TaskID, null);
|
||||
FIXMEGlobal.GenerateProgressState = sink.Copy;
|
||||
FIXMEGlobal.StatusEventNotifyer.SignalNewEvent();
|
||||
eventPollNotify.SignalNewEvent();
|
||||
|
||||
task.Run(sink);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FIXMEGlobal.DataConnection.LogError(string.Empty, "Failed while executing custom task", ex);
|
||||
databaseConnection.LogError(string.Empty, "Failed while executing custom task", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -518,10 +542,10 @@ namespace Duplicati.Server
|
||||
if (fromQueue)
|
||||
{
|
||||
FIXMEGlobal.GenerateProgressState = () => sink.Copy();
|
||||
FIXMEGlobal.StatusEventNotifyer.SignalNewEvent();
|
||||
eventPollNotify.SignalNewEvent();
|
||||
}
|
||||
|
||||
var options = ApplyOptions(backup, GetCommonOptions());
|
||||
var options = ApplyOptions(databaseConnection, backup, GetCommonOptions(databaseConnection));
|
||||
if (data.ExtraOptions != null)
|
||||
foreach (var k in data.ExtraOptions)
|
||||
options[k.Key] = k.Value;
|
||||
@@ -529,7 +553,7 @@ namespace Duplicati.Server
|
||||
// Pack in the system or task config for easy restore
|
||||
if (data.Operation == DuplicatiOperation.Backup && options.ContainsKey("store-task-config"))
|
||||
{
|
||||
tempfolder = StoreTaskConfigAndGetTempFolder(data, options);
|
||||
tempfolder = StoreTaskConfigAndGetTempFolder(databaseConnection, data, options);
|
||||
}
|
||||
|
||||
// Attach a log scope that tags all messages to relay the TaskID and BackupID
|
||||
@@ -557,7 +581,7 @@ namespace Duplicati.Server
|
||||
catch { }
|
||||
|
||||
((RunnerData)data).Controller = controller;
|
||||
var appSettings = FIXMEGlobal.DataConnection.ApplicationSettings;
|
||||
var appSettings = databaseConnection.ApplicationSettings;
|
||||
data.UpdateThrottleSpeeds(appSettings.UploadSpeedLimit, appSettings.DownloadSpeedLimit);
|
||||
|
||||
// Pass on the provider, will be replaced if configured in the backup
|
||||
@@ -573,7 +597,7 @@ namespace Duplicati.Server
|
||||
{
|
||||
case DuplicatiOperation.Backup:
|
||||
{
|
||||
var filter = ApplyFilter(backup, GetCommonFilter());
|
||||
var filter = ApplyFilter(backup, GetCommonFilter(databaseConnection));
|
||||
var sources =
|
||||
(from n in backup.Sources
|
||||
let p = SpecialFolders.ExpandEnvironmentVariables(n)
|
||||
@@ -581,62 +605,62 @@ namespace Duplicati.Server
|
||||
select p).ToArray();
|
||||
|
||||
var r = controller.Backup(sources, filter);
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
case DuplicatiOperation.List:
|
||||
{
|
||||
var r = controller.List(data.FilterStrings, null);
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
case DuplicatiOperation.Repair:
|
||||
{
|
||||
var r = controller.Repair(data.FilterStrings == null ? null : new Library.Utility.FilterExpression(data.FilterStrings));
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
case DuplicatiOperation.RepairUpdate:
|
||||
{
|
||||
var r = controller.UpdateDatabaseWithVersions();
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
case DuplicatiOperation.Remove:
|
||||
{
|
||||
var r = controller.Delete();
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
case DuplicatiOperation.Restore:
|
||||
{
|
||||
var r = controller.Restore(data.FilterStrings);
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
case DuplicatiOperation.Verify:
|
||||
{
|
||||
var r = controller.Test();
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
case DuplicatiOperation.Compact:
|
||||
{
|
||||
var r = controller.Compact();
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
case DuplicatiOperation.CreateReport:
|
||||
{
|
||||
using (var tf = new TempFile())
|
||||
using (var tf = new Library.Utility.TempFile())
|
||||
{
|
||||
var r = controller.CreateLogDatabase(tf);
|
||||
var tempid = FIXMEGlobal.DataConnection.RegisterTempFile("create-bug-report", r.TargetPath, DateTime.Now.AddDays(3));
|
||||
var tempid = databaseConnection.RegisterTempFile("create-bug-report", r.TargetPath, DateTime.Now.AddDays(3));
|
||||
|
||||
if (string.Equals(tf, r.TargetPath, Utility.ClientFilenameStringComparison))
|
||||
tf.Protected = true;
|
||||
|
||||
FIXMEGlobal.DataConnection.RegisterNotification(
|
||||
databaseConnection.RegisterNotification(
|
||||
NotificationType.Information,
|
||||
"Bugreport ready",
|
||||
"Bugreport is ready for download",
|
||||
@@ -656,7 +680,7 @@ namespace Duplicati.Server
|
||||
case DuplicatiOperation.ListRemote:
|
||||
{
|
||||
var r = controller.ListRemote();
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -675,21 +699,21 @@ namespace Duplicati.Server
|
||||
System.IO.File.Delete(dbpath);
|
||||
}
|
||||
}
|
||||
FIXMEGlobal.DataConnection.DeleteBackup(backup);
|
||||
FIXMEGlobal.Scheduler.Reschedule();
|
||||
databaseConnection.DeleteBackup(backup);
|
||||
(data as RunnerData)?.AfterTaskFinished?.Invoke(data);
|
||||
return null;
|
||||
}
|
||||
case DuplicatiOperation.Vacuum:
|
||||
{
|
||||
var r = controller.Vacuum();
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
case DuplicatiOperation.ListFilesets:
|
||||
{
|
||||
var r = controller.ListFilesets();
|
||||
UpdateMetadata(backup, r);
|
||||
UpdateMetadataBase(databaseConnection, eventPollNotify, backup, r);
|
||||
return r;
|
||||
}
|
||||
case DuplicatiOperation.ListFolderContents:
|
||||
@@ -714,9 +738,9 @@ namespace Duplicati.Server
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FIXMEGlobal.DataConnection.LogError(data.Backup?.ID, string.Format("Failed while executing {0} \"{1}\" (id: {2})", data.Operation, data.Backup?.Name, data.Backup?.ID), ex);
|
||||
databaseConnection.LogError(data.Backup?.ID, string.Format("Failed while executing {0} \"{1}\" (id: {2})", data.Operation, data.Backup?.Name, data.Backup?.ID), ex);
|
||||
if (data.Backup != null)
|
||||
UpdateMetadataError(data.Backup, ex);
|
||||
UpdateMetadataError(databaseConnection, data.Backup, ex);
|
||||
Library.UsageReporter.Reporter.Report(ex);
|
||||
|
||||
if (!fromQueue)
|
||||
@@ -731,7 +755,7 @@ namespace Duplicati.Server
|
||||
}
|
||||
}
|
||||
|
||||
private static TempFolder? StoreTaskConfigAndGetTempFolder(IRunnerData data, Dictionary<string, string?> options)
|
||||
private static TempFolder? StoreTaskConfigAndGetTempFolder(Connection databaseConnection, IRunnerData data, Dictionary<string, string?> options)
|
||||
{
|
||||
if (data.Backup == null)
|
||||
throw new ArgumentNullException(nameof(data.Backup));
|
||||
@@ -746,13 +770,13 @@ namespace Duplicati.Server
|
||||
{
|
||||
tempfolder = new TempFolder();
|
||||
var temppath = System.IO.Path.Combine(tempfolder, "task-setup.json");
|
||||
using (var tempfile = TempFile.WrapExistingFile(temppath))
|
||||
using (var tempfile = Library.Utility.TempFile.WrapExistingFile(temppath))
|
||||
{
|
||||
object? taskdata = null;
|
||||
if (all_tasks)
|
||||
taskdata = FIXMEGlobal.DataConnection.Backups.Where(x => !x.IsTemporary).Select(x => FIXMEGlobal.DataConnection.PrepareBackupForExport(FIXMEGlobal.DataConnection.GetBackup(x.ID)!));
|
||||
taskdata = databaseConnection.Backups.Where(x => !x.IsTemporary).Select(x => databaseConnection.PrepareBackupForExport(databaseConnection.GetBackup(x.ID)!));
|
||||
else
|
||||
taskdata = new[] { FIXMEGlobal.DataConnection.PrepareBackupForExport(data.Backup) };
|
||||
taskdata = new[] { databaseConnection.PrepareBackupForExport(data.Backup) };
|
||||
|
||||
using (var fs = System.IO.File.OpenWrite(tempfile))
|
||||
using (var sw = new System.IO.StreamWriter(fs, System.Text.Encoding.UTF8))
|
||||
@@ -773,20 +797,20 @@ namespace Duplicati.Server
|
||||
return tempfolder;
|
||||
}
|
||||
|
||||
private static void UpdateMetadataError(Duplicati.Server.Serialization.Interface.IBackup backup, Exception ex)
|
||||
private static void UpdateMetadataError(Connection databaseConnection, Serialization.Interface.IBackup backup, Exception ex)
|
||||
{
|
||||
backup.Metadata["LastErrorDate"] = Library.Utility.Utility.SerializeDateTime(DateTime.UtcNow);
|
||||
backup.Metadata["LastErrorDate"] = Utility.SerializeDateTime(DateTime.UtcNow);
|
||||
backup.Metadata["LastErrorMessage"] = ex.Message;
|
||||
|
||||
if (!backup.IsTemporary)
|
||||
FIXMEGlobal.DataConnection.SetMetadata(backup.Metadata, long.Parse(backup.ID), null);
|
||||
databaseConnection.SetMetadata(backup.Metadata, long.Parse(backup.ID), null);
|
||||
|
||||
string? messageid = null;
|
||||
if (ex is UserInformationException exception)
|
||||
messageid = exception.HelpID;
|
||||
|
||||
FIXMEGlobal.NotificationUpdateService.IncrementLastDataUpdateId();
|
||||
FIXMEGlobal.DataConnection.RegisterNotification(
|
||||
databaseConnection.RegisterNotification(
|
||||
NotificationType.Error,
|
||||
backup.IsTemporary ?
|
||||
"Error" : string.Format("Error while running {0}", backup.Name),
|
||||
@@ -804,31 +828,31 @@ namespace Duplicati.Server
|
||||
);
|
||||
}
|
||||
|
||||
private static void UpdateMetadataLastCompact(Duplicati.Server.Serialization.Interface.IBackup backup, Duplicati.Library.Interface.ICompactResults r)
|
||||
private static void UpdateMetadataLastCompact(IBackup backup, ICompactResults r)
|
||||
{
|
||||
if (r != null)
|
||||
{
|
||||
backup.Metadata["LastCompactDuration"] = r.Duration.ToString();
|
||||
backup.Metadata["LastCompactStarted"] = Library.Utility.Utility.SerializeDateTime(r.BeginTime.ToUniversalTime());
|
||||
backup.Metadata["LastCompactFinished"] = Library.Utility.Utility.SerializeDateTime(r.EndTime.ToUniversalTime());
|
||||
backup.Metadata["LastCompactStarted"] = Utility.SerializeDateTime(r.BeginTime.ToUniversalTime());
|
||||
backup.Metadata["LastCompactFinished"] = Utility.SerializeDateTime(r.EndTime.ToUniversalTime());
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateMetadataLastVacuum(Duplicati.Server.Serialization.Interface.IBackup backup, Duplicati.Library.Interface.IVacuumResults r)
|
||||
private static void UpdateMetadataLastVacuum(IBackup backup, IVacuumResults r)
|
||||
{
|
||||
if (r != null)
|
||||
{
|
||||
backup.Metadata["LastVacuumDuration"] = r.Duration.ToString();
|
||||
backup.Metadata["LastVacuumStarted"] = Library.Utility.Utility.SerializeDateTime(r.BeginTime.ToUniversalTime());
|
||||
backup.Metadata["LastVacuumFinished"] = Library.Utility.Utility.SerializeDateTime(r.EndTime.ToUniversalTime());
|
||||
backup.Metadata["LastVacuumStarted"] = Utility.SerializeDateTime(r.BeginTime.ToUniversalTime());
|
||||
backup.Metadata["LastVacuumFinished"] = Utility.SerializeDateTime(r.EndTime.ToUniversalTime());
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateMetadata(Duplicati.Server.Serialization.Interface.IBackup backup, Duplicati.Library.Interface.IParsedBackendStatistics r)
|
||||
private static void UpdateMetadataStatistics(IBackup backup, IParsedBackendStatistics r)
|
||||
{
|
||||
if (r != null)
|
||||
{
|
||||
backup.Metadata["LastBackupDate"] = Library.Utility.Utility.SerializeDateTime(r.LastBackupDate.ToUniversalTime());
|
||||
backup.Metadata["LastBackupDate"] = Utility.SerializeDateTime(r.LastBackupDate.ToUniversalTime());
|
||||
backup.Metadata["BackupListCount"] = r.BackupListCount.ToString();
|
||||
backup.Metadata["TotalQuotaSpace"] = r.TotalQuotaSpace.ToString();
|
||||
backup.Metadata["FreeQuotaSpace"] = r.FreeQuotaSpace.ToString();
|
||||
@@ -837,28 +861,28 @@ namespace Duplicati.Server
|
||||
backup.Metadata["TargetFilesSize"] = r.KnownFileSize.ToString();
|
||||
backup.Metadata["TargetFilesCount"] = r.KnownFileCount.ToString();
|
||||
backup.Metadata["TargetFilesetsCount"] = r.KnownFilesets.ToString();
|
||||
backup.Metadata["TargetSizeString"] = Duplicati.Library.Utility.Utility.FormatSizeString(r.KnownFileSize);
|
||||
backup.Metadata["TargetSizeString"] = Utility.FormatSizeString(r.KnownFileSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateMetadata(Duplicati.Server.Serialization.Interface.IBackup backup, Duplicati.Library.Interface.IBasicResults result)
|
||||
private static void UpdateMetadataBase(Connection databaseConnection, EventPollNotify eventPollNotify, Serialization.Interface.IBackup backup, IBasicResults result)
|
||||
{
|
||||
if (result is IRestoreResults r1)
|
||||
{
|
||||
backup.Metadata["LastRestoreDuration"] = r1.Duration.ToString();
|
||||
backup.Metadata["LastRestoreStarted"] = Library.Utility.Utility.SerializeDateTime(result.BeginTime.ToUniversalTime());
|
||||
backup.Metadata["LastRestoreFinished"] = Library.Utility.Utility.SerializeDateTime(result.EndTime.ToUniversalTime());
|
||||
backup.Metadata["LastRestoreStarted"] = Utility.SerializeDateTime(result.BeginTime.ToUniversalTime());
|
||||
backup.Metadata["LastRestoreFinished"] = Utility.SerializeDateTime(result.EndTime.ToUniversalTime());
|
||||
}
|
||||
|
||||
if (result is IParsedBackendStatistics r2 && !result.Interrupted)
|
||||
{
|
||||
UpdateMetadata(backup, r2);
|
||||
UpdateMetadataStatistics(backup, r2);
|
||||
}
|
||||
|
||||
if (result is IBackendStatsticsReporter r3 && !result.Interrupted)
|
||||
{
|
||||
if (r3.BackendStatistics is IParsedBackendStatistics statistics)
|
||||
UpdateMetadata(backup, statistics);
|
||||
UpdateMetadataStatistics(backup, statistics);
|
||||
}
|
||||
|
||||
if (result is ICompactResults r4 && !result.Interrupted)
|
||||
@@ -881,8 +905,8 @@ namespace Duplicati.Server
|
||||
backup.Metadata["SourceFilesSize"] = r.SizeOfExaminedFiles.ToString();
|
||||
backup.Metadata["SourceFilesCount"] = r.ExaminedFiles.ToString();
|
||||
backup.Metadata["SourceSizeString"] = Duplicati.Library.Utility.Utility.FormatSizeString(r.SizeOfExaminedFiles);
|
||||
backup.Metadata["LastBackupStarted"] = Library.Utility.Utility.SerializeDateTime(r.BeginTime.ToUniversalTime());
|
||||
backup.Metadata["LastBackupFinished"] = Library.Utility.Utility.SerializeDateTime(r.EndTime.ToUniversalTime());
|
||||
backup.Metadata["LastBackupStarted"] = Utility.SerializeDateTime(r.BeginTime.ToUniversalTime());
|
||||
backup.Metadata["LastBackupFinished"] = Utility.SerializeDateTime(r.EndTime.ToUniversalTime());
|
||||
backup.Metadata["LastBackupDuration"] = r.Duration.ToString();
|
||||
|
||||
if (r.CompactResults != null)
|
||||
@@ -912,7 +936,7 @@ namespace Duplicati.Server
|
||||
titleType = "Warning";
|
||||
}
|
||||
|
||||
FIXMEGlobal.DataConnection.RegisterNotification(
|
||||
databaseConnection.RegisterNotification(
|
||||
r.FilesWithError == 0 && !r.Errors.Any() ? NotificationType.Warning : NotificationType.Error,
|
||||
backup.IsTemporary ? "Warning" : $"{titleType} while running {backup.Name}",
|
||||
message,
|
||||
@@ -958,7 +982,7 @@ namespace Duplicati.Server
|
||||
else if (result.ParsedResult == ParsedResultType.Error && result.Errors.Count() == 1)
|
||||
message = $"Error: {result.Errors.Single()}";
|
||||
|
||||
FIXMEGlobal.DataConnection.RegisterNotification(
|
||||
databaseConnection.RegisterNotification(
|
||||
type,
|
||||
title,
|
||||
message,
|
||||
@@ -973,10 +997,10 @@ namespace Duplicati.Server
|
||||
}
|
||||
|
||||
if (!backup.IsTemporary)
|
||||
FIXMEGlobal.DataConnection.SetMetadata(backup.Metadata, long.Parse(backup.ID), null);
|
||||
databaseConnection.SetMetadata(backup.Metadata, long.Parse(backup.ID), null);
|
||||
|
||||
FIXMEGlobal.NotificationUpdateService.IncrementLastDataUpdateId();
|
||||
FIXMEGlobal.StatusEventNotifyer.SignalNewEvent();
|
||||
eventPollNotify.SignalNewEvent();
|
||||
}
|
||||
|
||||
private static bool TestIfOptionApplies()
|
||||
@@ -998,7 +1022,7 @@ namespace Duplicati.Server
|
||||
options["disable-module"] = string.Join(",", mods.Union(new string[] { module }).Distinct(StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
internal static Dictionary<string, string?> ApplyOptions(Serialization.Interface.IBackup backup, Dictionary<string, string?> options)
|
||||
internal static Dictionary<string, string?> ApplyOptions(Connection databaseConnection, Serialization.Interface.IBackup backup, Dictionary<string, string?> options)
|
||||
{
|
||||
options["backup-name"] = backup.Name;
|
||||
options["dbpath"] = backup.DBPath;
|
||||
@@ -1018,7 +1042,7 @@ namespace Duplicati.Server
|
||||
DisableModule("console-password-input", options);
|
||||
|
||||
// Patch in additional report urls
|
||||
var additionalReportUrl = FIXMEGlobal.DataConnection.ApplicationSettings.AdditionalReportUrl;
|
||||
var additionalReportUrl = databaseConnection.ApplicationSettings.AdditionalReportUrl;
|
||||
if (!string.IsNullOrWhiteSpace(additionalReportUrl))
|
||||
{
|
||||
options["send-http-json-urls"] = string.Join(";",
|
||||
@@ -1032,7 +1056,7 @@ namespace Duplicati.Server
|
||||
return options;
|
||||
}
|
||||
|
||||
private static IFilter? ApplyFilter(Serialization.Interface.IBackup backup, IFilter? filter)
|
||||
private static Library.Utility.IFilter? ApplyFilter(IBackup backup, Library.Utility.IFilter? filter)
|
||||
{
|
||||
var f2 = backup.Filters;
|
||||
if (f2 != null && f2.Length > 0)
|
||||
@@ -1044,7 +1068,7 @@ namespace Duplicati.Server
|
||||
? SpecialFolders.ExpandEnvironmentVariablesRegexp(n.Expression)
|
||||
: SpecialFolders.ExpandEnvironmentVariables(n.Expression)
|
||||
orderby n.Order
|
||||
select (IFilter)new FilterExpression(exp, n.Include))
|
||||
select (Library.Utility.IFilter)new FilterExpression(exp, n.Include))
|
||||
.Aggregate((a, b) => FilterExpression.Combine(a, b));
|
||||
|
||||
filter = FilterExpression.Combine(filter, nf);
|
||||
@@ -1053,17 +1077,17 @@ namespace Duplicati.Server
|
||||
return filter;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string?> GetCommonOptions()
|
||||
public static Dictionary<string, string?> GetCommonOptions(Connection databaseConnection)
|
||||
{
|
||||
return
|
||||
(from n in FIXMEGlobal.DataConnection.Settings
|
||||
(from n in databaseConnection.Settings
|
||||
where TestIfOptionApplies()
|
||||
select n).ToDictionary(k => k.Name.StartsWith("--", StringComparison.Ordinal) ? k.Name.Substring(2) : k.Name, k => (string?)k.Value);
|
||||
}
|
||||
|
||||
private static IFilter? GetCommonFilter()
|
||||
private static Library.Utility.IFilter? GetCommonFilter(Connection databaseConnection)
|
||||
{
|
||||
var filters = FIXMEGlobal.DataConnection.Filters;
|
||||
var filters = databaseConnection.Filters;
|
||||
if (filters == null || filters.Length == 0)
|
||||
return null;
|
||||
|
||||
@@ -1071,7 +1095,7 @@ namespace Duplicati.Server
|
||||
(from n in filters
|
||||
orderby n.Order
|
||||
let exp = Environment.ExpandEnvironmentVariables(n.Expression)
|
||||
select (IFilter)new FilterExpression(exp, n.Include))
|
||||
select (Library.Utility.IFilter)new FilterExpression(exp, n.Include))
|
||||
.Aggregate((a, b) => FilterExpression.Combine(a, b));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ using System.Text;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Duplicati.Library.Utility;
|
||||
using Duplicati.Library.RestAPI;
|
||||
using System.Threading.Tasks;
|
||||
using Duplicati.WebserverCore.Abstractions;
|
||||
using Duplicati.Server.Database;
|
||||
|
||||
// TODO: Rewrite this class.
|
||||
// It should just signal what new backups to run, and not mix with the worker thread.
|
||||
@@ -69,6 +69,10 @@ namespace Duplicati.Server
|
||||
/// The queue runner service
|
||||
/// </summary>
|
||||
private readonly IQueueRunnerService m_queueRunnerService;
|
||||
/// <summary>
|
||||
/// The data connection
|
||||
/// </summary>
|
||||
private readonly Connection m_dataConnection;
|
||||
|
||||
/// <summary>
|
||||
/// The currently scheduled items
|
||||
@@ -83,8 +87,11 @@ namespace Duplicati.Server
|
||||
/// <summary>
|
||||
/// Constructs a new scheduler
|
||||
/// </summary>
|
||||
public Scheduler(IQueueRunnerService queueRunnerService)
|
||||
/// <param name="connection">The data connection</param>
|
||||
/// <param name="queueRunnerService">The queue runner service</param>
|
||||
public Scheduler(Connection connection, IQueueRunnerService queueRunnerService)
|
||||
{
|
||||
m_dataConnection = connection;
|
||||
m_queueRunnerService = queueRunnerService;
|
||||
m_thread = new Thread(new ThreadStart(Runner));
|
||||
m_schedule = [];
|
||||
@@ -99,7 +106,7 @@ namespace Duplicati.Server
|
||||
public IList<Tuple<string, DateTime>> GetProposedSchedule()
|
||||
{
|
||||
return (
|
||||
from n in FIXMEGlobal.Scheduler.Schedule
|
||||
from n in this.Schedule
|
||||
let backupid = (from t in n.Value.Tags
|
||||
where t != null && t.StartsWith("ID=", StringComparison.Ordinal)
|
||||
select t.Substring("ID=".Length)).FirstOrDefault()
|
||||
@@ -220,7 +227,7 @@ namespace Duplicati.Server
|
||||
{
|
||||
t.Item1.Time = t.Item2;
|
||||
t.Item1.LastRun = t.Item3;
|
||||
FIXMEGlobal.DataConnection.AddOrUpdateSchedule(t.Item1);
|
||||
m_dataConnection.AddOrUpdateSchedule(t.Item1);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@@ -256,8 +263,8 @@ namespace Duplicati.Server
|
||||
// to avoid frequent db lookups
|
||||
|
||||
//Determine schedule list
|
||||
var timeZoneInfo = FIXMEGlobal.DataConnection.ApplicationSettings.Timezone;
|
||||
var lst = FIXMEGlobal.DataConnection.Schedules;
|
||||
var timeZoneInfo = m_dataConnection.ApplicationSettings.Timezone;
|
||||
var lst = m_dataConnection.Schedules;
|
||||
foreach (var sc in lst)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sc.Repeat))
|
||||
@@ -291,7 +298,7 @@ namespace Duplicati.Server
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FIXMEGlobal.DataConnection.LogError(sc.ID.ToString(), "Scheduler failed to find next date",
|
||||
m_dataConnection.LogError(sc.ID.ToString(), "Scheduler failed to find next date",
|
||||
ex);
|
||||
}
|
||||
|
||||
@@ -300,7 +307,7 @@ namespace Duplicati.Server
|
||||
{
|
||||
var jobsToRun = new List<Server.Runner.IRunnerData>();
|
||||
//TODO: Cache this to avoid frequent lookups
|
||||
foreach (var id in FIXMEGlobal.DataConnection.GetBackupIDsForTags(sc.Tags).Distinct()
|
||||
foreach (var id in m_dataConnection.GetBackupIDsForTags(sc.Tags).Distinct()
|
||||
.Select(x => x.ToString()))
|
||||
{
|
||||
//See if it is already queued
|
||||
@@ -315,11 +322,11 @@ namespace Duplicati.Server
|
||||
//If it is not already in queue, put it there
|
||||
if (!tmplst.Any(x => x == id))
|
||||
{
|
||||
var entry = FIXMEGlobal.DataConnection.GetBackup(id);
|
||||
var entry = m_dataConnection.GetBackup(id);
|
||||
if (entry != null)
|
||||
{
|
||||
var options = Server.Runner.GetCommonOptions();
|
||||
Server.Runner.ApplyOptions(entry, options);
|
||||
var options = Server.Runner.GetCommonOptions(m_dataConnection);
|
||||
Server.Runner.ApplyOptions(m_dataConnection, entry, options);
|
||||
if (new Library.Main.Options(options).DisableOnBattery &&
|
||||
(Library.Utility.Power.PowerSupply.GetSource() ==
|
||||
Library.Utility.Power.PowerSupply.Source.Battery))
|
||||
@@ -364,7 +371,7 @@ namespace Duplicati.Server
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FIXMEGlobal.DataConnection.LogError(sc.ID.ToString(),
|
||||
m_dataConnection.LogError(sc.ID.ToString(),
|
||||
"Scheduler failed to find next date", ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -53,10 +53,6 @@ public interface IQueuedTask
|
||||
/// </summary>
|
||||
Func<Exception?, Task>? OnFinished { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// That action that performs the task.
|
||||
/// </summary>
|
||||
Task Execute();
|
||||
/// <summary>
|
||||
/// Updates the throttle speeds for the task.
|
||||
/// </summary>
|
||||
|
||||
+14
-28
@@ -99,11 +99,6 @@ namespace Duplicati.Server
|
||||
/// </summary>
|
||||
public static Database.Connection DataConnection { get => FIXMEGlobal.DataConnection; set => FIXMEGlobal.DataConnection = value; }
|
||||
|
||||
/// <summary>
|
||||
/// This is the scheduling thread
|
||||
/// </summary>
|
||||
public static ISchedulerService Scheduler { get => FIXMEGlobal.Scheduler; }
|
||||
|
||||
/// <summary>
|
||||
/// The thread running the ping-pong handler
|
||||
/// </summary>
|
||||
@@ -137,20 +132,10 @@ namespace Duplicati.Server
|
||||
DuplicatiWebserver.Stop().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The update poll thread.
|
||||
/// </summary>
|
||||
public static UpdatePollThread UpdatePoller => FIXMEGlobal.UpdatePoller;
|
||||
|
||||
/// <summary>
|
||||
/// An event that is set once the server is ready to respond to requests
|
||||
/// </summary>
|
||||
public static readonly System.Threading.ManualResetEvent ServerStartedEvent = new System.Threading.ManualResetEvent(false);
|
||||
|
||||
/// <summary>
|
||||
/// The status event signaler, used to control long polling of status updates
|
||||
/// </summary>
|
||||
public static EventPollNotify StatusEventNotifyer => FIXMEGlobal.Provider.GetRequiredService<EventPollNotify>();
|
||||
public static readonly ManualResetEvent ServerStartedEvent = new ManualResetEvent(false);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate method for creating a copy of the current progress state
|
||||
@@ -244,6 +229,9 @@ namespace Duplicati.Server
|
||||
var crashed = false;
|
||||
var terminated = false;
|
||||
IQueueRunnerService queueRunner = null;
|
||||
UpdatePollThread updatePollThread = null;
|
||||
EventPollNotify eventPollNotify = null;
|
||||
ISchedulerService scheduler = null;
|
||||
try
|
||||
{
|
||||
DataConnection = GetDatabaseConnection(commandlineOptions, silentConsole);
|
||||
@@ -267,14 +255,17 @@ namespace Duplicati.Server
|
||||
|
||||
DuplicatiWebserver = StartWebServer(commandlineOptions, DataConnection).Await();
|
||||
|
||||
queueRunner = DuplicatiWebserver.Provider.GetRequiredService<IQueueRunnerService>();
|
||||
DataConnection.SetServiceProvider(DuplicatiWebserver.Provider);
|
||||
queueRunner = DuplicatiWebserver.Provider.GetRequiredService<IQueueRunnerService>();
|
||||
updatePollThread = DuplicatiWebserver.Provider.GetRequiredService<UpdatePollThread>();
|
||||
eventPollNotify = DuplicatiWebserver.Provider.GetRequiredService<EventPollNotify>();
|
||||
scheduler = DuplicatiWebserver.Provider.GetRequiredService<ISchedulerService>();
|
||||
|
||||
UpdatePoller.Init(Library.Utility.Utility.ParseBoolOption(commandlineOptions, DISABLE_UPDATE_CHECK_OPTION));
|
||||
updatePollThread.Init(Library.Utility.Utility.ParseBoolOption(commandlineOptions, DISABLE_UPDATE_CHECK_OPTION));
|
||||
|
||||
SetPurgeTempFilesTimer(commandlineOptions);
|
||||
|
||||
LiveControl.StateChanged = (e) => { LiveControl_StateChanged(queueRunner, DataConnection, StatusEventNotifyer, e); };
|
||||
LiveControl.StateChanged = (e) => { LiveControl_StateChanged(queueRunner, DataConnection, eventPollNotify, e); };
|
||||
|
||||
if (Library.Utility.Utility.ParseBoolOption(commandlineOptions, PING_PONG_KEEPALIVE_OPTION))
|
||||
{
|
||||
@@ -361,10 +352,10 @@ namespace Duplicati.Server
|
||||
Log.WriteInformationMessage(LOGTAG, "ServerStopping", Strings.Program.ServerStopping);
|
||||
|
||||
var steps = new Action[] {
|
||||
() => StatusEventNotifyer.SignalNewEvent(),
|
||||
() => { if (ShutdownModernWebserver != null) ShutdownModernWebserver(); },
|
||||
() => UpdatePoller?.Terminate(),
|
||||
() => Scheduler?.Terminate(true),
|
||||
() => eventPollNotify?.SignalNewEvent(),
|
||||
() => ShutdownModernWebserver(),
|
||||
() => updatePollThread?.Terminate(),
|
||||
() => scheduler?.Terminate(true),
|
||||
() => queueRunner?.Terminate(true),
|
||||
() => ApplicationInstance?.Dispose(),
|
||||
() => PurgeTempFilesTimer?.Dispose(),
|
||||
@@ -844,11 +835,6 @@ namespace Duplicati.Server
|
||||
Library.UsageReporter.Reporter.SetReportLevel(reportLevel, disableUsageReporter);
|
||||
}
|
||||
|
||||
private static void SignalNewEvent(object sender, EventArgs e)
|
||||
{
|
||||
StatusEventNotifyer.SignalNewEvent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This event handler updates the trayicon menu with the current state of the runner.
|
||||
/// </summary>
|
||||
|
||||
@@ -41,8 +41,8 @@ public class BackupGet : IEndpointV1
|
||||
=> ExecuteGet(connection, GetBackup(connection, id)))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapGet("/backup/{id}/files", ([FromServices] Connection connection, [FromRoute] string id, [FromQuery] string? filter, [FromQuery] string? time, [FromQuery(Name = "all-versions")] bool? allVersions, [FromQuery(Name = "prefix-only")] bool? prefixOnly, [FromQuery(Name = "folder-contents")] bool? folderContents)
|
||||
=> ExecuteGetFiles(GetBackup(connection, id), filter, time, allVersions ?? false, prefixOnly ?? false, folderContents ?? false, new Dictionary<string, string>()))
|
||||
group.MapGet("/backup/{id}/files", ([FromServices] Connection connection, [FromServices] EventPollNotify eventPollNotify, [FromRoute] string id, [FromQuery] string? filter, [FromQuery] string? time, [FromQuery(Name = "all-versions")] bool? allVersions, [FromQuery(Name = "prefix-only")] bool? prefixOnly, [FromQuery(Name = "folder-contents")] bool? folderContents)
|
||||
=> ExecuteGetFiles(connection, eventPollNotify, GetBackup(connection, id), filter, time, allVersions ?? false, prefixOnly ?? false, folderContents ?? false, new Dictionary<string, string>()))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapGet("/backup/{id}/log", ([FromServices] Connection connection, [FromRoute] string id, [FromQuery] long? offset, [FromQuery] long? pagesize)
|
||||
@@ -53,16 +53,16 @@ public class BackupGet : IEndpointV1
|
||||
=> ExecuteGetRemotelog(connection, GetBackup(connection, id), offset, pagesize ?? 100))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapGet("/backup/{id}/filesets", ([FromServices] Connection connection, [FromRoute] string id, [FromQuery(Name = "include-metadata")] bool? includeMetadata, [FromQuery(Name = "from-remote-only")] bool? fromRemoteOnly)
|
||||
=> ExecuteGetFilesets(GetBackup(connection, id), includeMetadata ?? false, fromRemoteOnly ?? false))
|
||||
group.MapGet("/backup/{id}/filesets", ([FromServices] Connection connection, [FromServices] EventPollNotify eventPollNotify, [FromRoute] string id, [FromQuery(Name = "include-metadata")] bool? includeMetadata, [FromQuery(Name = "from-remote-only")] bool? fromRemoteOnly)
|
||||
=> ExecuteGetFilesets(connection, eventPollNotify, GetBackup(connection, id), includeMetadata ?? false, fromRemoteOnly ?? false))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapGet("/backup/{id}/export-argsonly", ([FromServices] Connection connection, [FromRoute] string id, [FromQuery(Name = "export-passwords")] bool? exportPasswords, [FromQuery] string? passphrase)
|
||||
=> ExecuteGetExportArgsOnly(GetBackup(connection, id), exportPasswords ?? false))
|
||||
=> ExecuteGetExportArgsOnly(connection, GetBackup(connection, id), exportPasswords ?? false))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapGet("/backup/{id}/export-cmdline", ([FromServices] Connection connection, [FromRoute] string id, [FromQuery(Name = "export-passwords")] bool? exportPasswords, [FromQuery] string? passphrase)
|
||||
=> ExecuteGetExportCmdline(GetBackup(connection, id), exportPasswords ?? false))
|
||||
=> ExecuteGetExportCmdline(connection, GetBackup(connection, id), exportPasswords ?? false))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapGet("/backup/{id}/export", ([FromServices] Connection connection, [FromServices] IHttpContextAccessor httpContextAccessor, [FromServices] IJWTTokenProvider jWTTokenProvider, [FromRoute] string id, [FromQuery(Name = "export-passwords")] bool? exportPasswords, [FromQuery] string? passphrase, [FromQuery] string token, CancellationToken ct) =>
|
||||
@@ -142,7 +142,7 @@ public class BackupGet : IEndpointV1
|
||||
);
|
||||
}
|
||||
|
||||
private static Dictionary<string, object> SearchFiles(IBackup backup, string? filter, string? timestring, bool allVersions, bool prefixOnly, bool folderContents, Dictionary<string, string> extraValues)
|
||||
private static Dictionary<string, object> SearchFiles(Connection connection, EventPollNotify eventPollNotify, IBackup backup, string? filter, string? timestring, bool allVersions, bool prefixOnly, bool folderContents, Dictionary<string, string> extraValues)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(timestring) && !allVersions)
|
||||
throw new BadRequestException("Invalid or missing time");
|
||||
@@ -151,7 +151,7 @@ public class BackupGet : IEndpointV1
|
||||
if (!allVersions)
|
||||
time = Library.Utility.Timeparser.ParseTimeInterval(timestring, DateTime.Now);
|
||||
|
||||
var r = Runner.Run(Runner.CreateListTask(backup, filter == null ? null : [filter], prefixOnly, allVersions, folderContents, time), false) as Duplicati.Library.Interface.IListResults;
|
||||
var r = Runner.Run(connection, eventPollNotify, Runner.CreateListTask(backup, filter == null ? null : [filter], prefixOnly, allVersions, folderContents, time), false) as IListResults;
|
||||
if (r == null)
|
||||
throw new ServerErrorException("No result from list operation");
|
||||
|
||||
@@ -170,8 +170,8 @@ public class BackupGet : IEndpointV1
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<string, object> ExecuteGetFiles(IBackup bk, string? filter, string? timestring, bool allVersions, bool prefixOnly, bool folderContents, Dictionary<string, string> extraValues)
|
||||
=> SearchFiles(bk, filter, timestring, allVersions, prefixOnly, folderContents, extraValues);
|
||||
private static Dictionary<string, object> ExecuteGetFiles(Connection connection, EventPollNotify eventPollNotify, IBackup bk, string? filter, string? timestring, bool allVersions, bool prefixOnly, bool folderContents, Dictionary<string, string> extraValues)
|
||||
=> SearchFiles(connection, eventPollNotify, bk, filter, timestring, allVersions, prefixOnly, folderContents, extraValues);
|
||||
|
||||
private static List<Dictionary<string, object>> ExecuteGetLog(Connection connection, IBackup bk, long? offset, long pagesize)
|
||||
{
|
||||
@@ -202,7 +202,7 @@ public class BackupGet : IEndpointV1
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<IListResultFileset> ExecuteGetFilesets(IBackup bk, bool includeMetadata, bool fromRemoteOnly)
|
||||
private static IEnumerable<IListResultFileset> ExecuteGetFilesets(Connection connection, EventPollNotify eventPollNotify, IBackup bk, bool includeMetadata, bool fromRemoteOnly)
|
||||
{
|
||||
var extra = new Dictionary<string, string?>
|
||||
{
|
||||
@@ -218,7 +218,7 @@ public class BackupGet : IEndpointV1
|
||||
|
||||
try
|
||||
{
|
||||
var r = Runner.Run(Runner.CreateTask(DuplicatiOperation.List, bk, extra), false) as IListResults;
|
||||
var r = Runner.Run(connection, eventPollNotify, Runner.CreateTask(DuplicatiOperation.List, bk, extra), false) as IListResults;
|
||||
if (r == null)
|
||||
throw new ServerErrorException("No result from list operation");
|
||||
|
||||
@@ -239,20 +239,20 @@ public class BackupGet : IEndpointV1
|
||||
backup.SanitizeTargetUrl();
|
||||
}
|
||||
|
||||
private static Dto.ExportCommandlineDto ExecuteGetExportCmdline(IBackup backup, bool exportPasswords)
|
||||
private static Dto.ExportCommandlineDto ExecuteGetExportCmdline(Connection connection, IBackup backup, bool exportPasswords)
|
||||
{
|
||||
if (!exportPasswords)
|
||||
RemovePasswords(backup);
|
||||
|
||||
return new Dto.ExportCommandlineDto(Runner.GetCommandLine(Runner.CreateTask(DuplicatiOperation.Backup, backup)));
|
||||
return new Dto.ExportCommandlineDto(Runner.GetCommandLine(connection, Runner.CreateTask(DuplicatiOperation.Backup, backup)));
|
||||
}
|
||||
|
||||
private static Dto.ExportArgsOnlyDto ExecuteGetExportArgsOnly(IBackup backup, bool exportPasswords)
|
||||
private static Dto.ExportArgsOnlyDto ExecuteGetExportArgsOnly(Connection connection, IBackup backup, bool exportPasswords)
|
||||
{
|
||||
if (!exportPasswords)
|
||||
RemovePasswords(backup);
|
||||
|
||||
var parts = Runner.GetCommandLineParts(Runner.CreateTask(DuplicatiOperation.Backup, backup));
|
||||
var parts = Runner.GetCommandLineParts(connection, Runner.CreateTask(DuplicatiOperation.Backup, backup));
|
||||
return new Dto.ExportArgsOnlyDto(
|
||||
parts.First(),
|
||||
parts.Skip(1).Where(x => !x.StartsWith("--", StringComparison.Ordinal)),
|
||||
|
||||
@@ -36,9 +36,9 @@ public class BackupPutDelete : IEndpointV1
|
||||
=> ExecutePut(GetBackup(connection, id), connection, input))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapDelete("/backup/{id}", ([FromServices] Connection connection, [FromServices] IQueueRunnerService queueRunnerService, [FromServices] LiveControls liveControls, [FromServices] IHttpContextAccessor httpContextAccessor, [FromRoute] string id, [FromQuery(Name = "delete-remote-files")] bool? delete_remote_files, [FromQuery(Name = "delete-local-db")] bool? delete_local_db, [FromQuery] bool? force) =>
|
||||
group.MapDelete("/backup/{id}", ([FromServices] Connection connection, [FromServices] ISchedulerService schedulerService, [FromServices] IQueueRunnerService queueRunnerService, [FromServices] LiveControls liveControls, [FromServices] IHttpContextAccessor httpContextAccessor, [FromRoute] string id, [FromQuery(Name = "delete-remote-files")] bool? delete_remote_files, [FromQuery(Name = "delete-local-db")] bool? delete_local_db, [FromQuery] bool? force) =>
|
||||
{
|
||||
var res = ExecuteDelete(GetBackup(connection, id), queueRunnerService, liveControls, delete_remote_files ?? false, delete_local_db, force ?? false);
|
||||
var res = ExecuteDelete(GetBackup(connection, id), schedulerService, queueRunnerService, liveControls, delete_remote_files ?? false, delete_local_db, force ?? false);
|
||||
if (res.Status != "OK" && httpContextAccessor.HttpContext != null)
|
||||
httpContextAccessor.HttpContext.Response.StatusCode = 500;
|
||||
return res;
|
||||
@@ -130,7 +130,7 @@ public class BackupPutDelete : IEndpointV1
|
||||
}
|
||||
}
|
||||
|
||||
private static Dto.DeleteBackupOutputDto ExecuteDelete(IBackup backup, IQueueRunnerService queueRunnerService, LiveControls liveControls, bool delete_remote_files, bool? delete_local_db, bool force)
|
||||
private static Dto.DeleteBackupOutputDto ExecuteDelete(IBackup backup, ISchedulerService schedulerService, IQueueRunnerService queueRunnerService, LiveControls liveControls, bool delete_remote_files, bool? delete_local_db, bool force)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -179,7 +179,7 @@ public class BackupPutDelete : IEndpointV1
|
||||
if (delete_remote_files)
|
||||
extra["delete-remote-files"] = "true";
|
||||
|
||||
return new Dto.DeleteBackupOutputDto("OK", null, queueRunnerService.AddTask(Runner.CreateTask(DuplicatiOperation.Delete, backup, extra)));
|
||||
return new Dto.DeleteBackupOutputDto("OK", null, queueRunnerService.AddTask(Runner.CreateDeleteTask(backup, extra, (_) => schedulerService.Reschedule())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ using Duplicati.Library.Interface;
|
||||
using Duplicati.Library.Main;
|
||||
using Duplicati.Library.RestAPI;
|
||||
using Duplicati.Server;
|
||||
using Duplicati.Server.Database;
|
||||
using Duplicati.WebserverCore.Abstractions;
|
||||
using Duplicati.WebserverCore.Exceptions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -38,8 +39,8 @@ namespace Duplicati.WebserverCore.Endpoints.V1
|
||||
=> ExecuteDbPath(input.path))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapPost("/remoteoperation/test", ([FromQuery] bool? autocreate, [FromBody] RemoteOperationInput input, CancellationToken cancelToken)
|
||||
=> ExecuteTest(input.path, autocreate ?? false, cancelToken))
|
||||
group.MapPost("/remoteoperation/test", ([FromServices] Connection connection, [FromQuery] bool? autocreate, [FromBody] RemoteOperationInput input, CancellationToken cancelToken)
|
||||
=> ExecuteTest(connection, input.path, autocreate ?? false, cancelToken))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapPost("/remoteoperation/create", ([FromBody] RemoteOperationInput input, CancellationToken cancelToken)
|
||||
@@ -53,11 +54,11 @@ namespace Duplicati.WebserverCore.Endpoints.V1
|
||||
return new Dto.GetDbPathDto(!string.IsNullOrWhiteSpace(path), path);
|
||||
}
|
||||
|
||||
private static Dictionary<string, string?> ParseUrlOptions(Library.Utility.Uri uri)
|
||||
private static Dictionary<string, string?> ParseUrlOptions(Connection connection, Library.Utility.Uri uri)
|
||||
{
|
||||
var qp = uri.QueryParameters;
|
||||
|
||||
var opts = Runner.GetCommonOptions();
|
||||
var opts = Runner.GetCommonOptions(connection);
|
||||
foreach (var k in qp.Keys.Cast<string>())
|
||||
opts[k] = qp[k];
|
||||
|
||||
@@ -87,10 +88,10 @@ namespace Duplicati.WebserverCore.Endpoints.V1
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<TupleDisposeWrapper> GetBackend(string url, CancellationToken cancelToken)
|
||||
private static async Task<TupleDisposeWrapper> GetBackend(Connection connection, string url, CancellationToken cancelToken)
|
||||
{
|
||||
var uri = new Library.Utility.Uri(url);
|
||||
var opts = ParseUrlOptions(uri);
|
||||
var opts = ParseUrlOptions(connection, uri);
|
||||
|
||||
var tmp = new[] { uri };
|
||||
await SecretProviderHelper.ApplySecretProviderAsync([], tmp, opts, Library.Utility.TempFolder.SystemTempPath, FIXMEGlobal.SecretProvider, cancelToken);
|
||||
@@ -101,13 +102,13 @@ namespace Duplicati.WebserverCore.Endpoints.V1
|
||||
return new TupleDisposeWrapper(backend, modules);
|
||||
}
|
||||
|
||||
private static async Task ExecuteTest(string url, bool autoCreate, CancellationToken cancelToken)
|
||||
private static async Task ExecuteTest(Connection connection, string url, bool autoCreate, CancellationToken cancelToken)
|
||||
{
|
||||
TupleDisposeWrapper? wrapper = null;
|
||||
|
||||
try
|
||||
{
|
||||
wrapper = await GetBackend(url, cancelToken);
|
||||
wrapper = await GetBackend(connection, url, cancelToken);
|
||||
|
||||
using (var b = wrapper.Backend)
|
||||
{
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
using Duplicati.Library.RestAPI;
|
||||
using Duplicati.Server;
|
||||
using Duplicati.WebserverCore.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Duplicati.WebserverCore.Endpoints.V1;
|
||||
|
||||
@@ -27,9 +28,11 @@ public class Updates : IEndpointV1
|
||||
{
|
||||
public static void Map(RouteGroupBuilder group)
|
||||
{
|
||||
group.MapPost("/updates/check", Execute).RequireAuthorization();
|
||||
group.MapPost("/updates/check",
|
||||
([FromServices] UpdatePollThread updatePollThread) => Execute(updatePollThread))
|
||||
.RequireAuthorization();
|
||||
}
|
||||
|
||||
private static void Execute()
|
||||
=> FIXMEGlobal.UpdatePoller.CheckNow();
|
||||
private static void Execute(UpdatePollThread updatePollThread)
|
||||
=> updatePollThread.CheckNow();
|
||||
}
|
||||
@@ -22,6 +22,7 @@ using Duplicati.Library.Interface;
|
||||
using Duplicati.Library.Main;
|
||||
using Duplicati.Library.RestAPI;
|
||||
using Duplicati.Server;
|
||||
using Duplicati.Server.Database;
|
||||
using Duplicati.WebserverCore.Abstractions;
|
||||
using Duplicati.WebserverCore.Exceptions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -33,18 +34,20 @@ public record WebModules : IEndpointV1
|
||||
public static void Map(RouteGroupBuilder group)
|
||||
{
|
||||
group.MapGet("/webmodules", ExecuteGet).RequireAuthorization();
|
||||
group.MapPost("/webmodule/{modulekey}", ([FromRoute] string modulekey, [FromBody] Dictionary<string, string> options, CancellationToken cancellationToken) => ExecutePost(modulekey, options, cancellationToken)).RequireAuthorization();
|
||||
group.MapPost("/webmodule/{modulekey}", ([FromServices] Connection connection, [FromRoute] string modulekey, [FromBody] Dictionary<string, string> options, CancellationToken cancellationToken)
|
||||
=> ExecutePost(connection, modulekey, options, cancellationToken))
|
||||
.RequireAuthorization();
|
||||
}
|
||||
|
||||
private static IEnumerable<IWebModule> ExecuteGet()
|
||||
=> Library.DynamicLoader.WebLoader.Modules;
|
||||
|
||||
private static async Task<Dto.WebModuleOutputDto> ExecutePost(string modulekey, Dictionary<string, string> inputOptions, CancellationToken cancellationToken)
|
||||
private static async Task<Dto.WebModuleOutputDto> ExecutePost(Connection connection, string modulekey, Dictionary<string, string> inputOptions, CancellationToken cancellationToken)
|
||||
{
|
||||
var m = Library.DynamicLoader.WebLoader.Modules.FirstOrDefault(x => x.Key.Equals(modulekey, StringComparison.OrdinalIgnoreCase))
|
||||
?? throw new NotFoundException("No such module found");
|
||||
|
||||
var options = Runner.GetCommonOptions();
|
||||
var options = Runner.GetCommonOptions(connection);
|
||||
foreach (var k in inputOptions.Keys)
|
||||
options[k] = inputOptions[k];
|
||||
|
||||
|
||||
@@ -33,33 +33,33 @@ public class BackupListing : IEndpointV2
|
||||
{
|
||||
public static void Map(RouteGroupBuilder group)
|
||||
{
|
||||
group.MapPost("/backup/list-filesets", ([FromServices] Connection connection, [FromBody] Dto.V2.ListFilesetsRequestDto input)
|
||||
=> ExecuteGetFilesets(GetBackup(connection, input.BackupId)))
|
||||
group.MapPost("/backup/list-filesets", ([FromServices] Connection connection, [FromServices] EventPollNotify eventPollNotify, [FromBody] Dto.V2.ListFilesetsRequestDto input)
|
||||
=> ExecuteGetFilesets(connection, eventPollNotify, GetBackup(connection, input.BackupId)))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapPost("/backup/list-folder", ([FromServices] Connection connection, [FromBody] Dto.V2.ListFolderContentRequestDto input)
|
||||
=> ExecuteListFolder(GetBackup(connection, input.BackupId), input))
|
||||
group.MapPost("/backup/list-folder", ([FromServices] Connection connection, [FromServices] EventPollNotify eventPollNotify, [FromBody] Dto.V2.ListFolderContentRequestDto input)
|
||||
=> ExecuteListFolder(connection, eventPollNotify, GetBackup(connection, input.BackupId), input))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapPost("/backup/list-versions", ([FromServices] Connection connection, [FromBody] Dto.V2.ListFileVersionsRequestDto input)
|
||||
=> ExecuteListVersions(GetBackup(connection, input.BackupId), input))
|
||||
group.MapPost("/backup/list-versions", ([FromServices] Connection connection, [FromServices] EventPollNotify eventPollNotify, [FromBody] Dto.V2.ListFileVersionsRequestDto input)
|
||||
=> ExecuteListVersions(connection, eventPollNotify, GetBackup(connection, input.BackupId), input))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapPost("/backup/search", ([FromServices] Connection connection, [FromBody] Dto.V2.SearchEntriesRequestDto input)
|
||||
=> ExecuteSearch(GetBackup(connection, input.BackupId), input))
|
||||
group.MapPost("/backup/search", ([FromServices] Connection connection, [FromServices] EventPollNotify eventPollNotify, [FromBody] Dto.V2.SearchEntriesRequestDto input)
|
||||
=> ExecuteSearch(connection, eventPollNotify, GetBackup(connection, input.BackupId), input))
|
||||
.RequireAuthorization();
|
||||
}
|
||||
|
||||
private static IBackup GetBackup(Connection connection, string id)
|
||||
=> connection.GetBackup(id) ?? throw new NotFoundException("Backup not found");
|
||||
|
||||
private static Dto.V2.ListFolderContentResponseDto ExecuteListFolder(IBackup bk, Dto.V2.ListFolderContentRequestDto input)
|
||||
private static Dto.V2.ListFolderContentResponseDto ExecuteListFolder(Connection connection, EventPollNotify eventPollNotify, IBackup bk, Dto.V2.ListFolderContentRequestDto input)
|
||||
{
|
||||
var time = string.IsNullOrWhiteSpace(input.Time)
|
||||
? new DateTime(0)
|
||||
: Library.Utility.Timeparser.ParseTimeInterval(input.Time, DateTime.Now);
|
||||
|
||||
var r = Runner.Run(Runner.CreateListFolderContents(bk, input.Paths, time, input.PageSize ?? 1000, input.Page ?? 0), false) as IListFolderResults;
|
||||
var r = Runner.Run(connection, eventPollNotify, Runner.CreateListFolderContents(bk, input.Paths, time, input.PageSize ?? 1000, input.Page ?? 0), false) as IListFolderResults;
|
||||
if (r == null)
|
||||
throw new ServerErrorException("No result from list operation");
|
||||
|
||||
@@ -78,16 +78,16 @@ public class BackupListing : IEndpointV2
|
||||
r.Entries.TotalCount);
|
||||
}
|
||||
|
||||
private static Dto.V2.ListFilesetsResponseDto ExecuteGetFilesets(IBackup bk)
|
||||
private static Dto.V2.ListFilesetsResponseDto ExecuteGetFilesets(Connection connection, EventPollNotify eventPollNotify, IBackup bk)
|
||||
{
|
||||
var extra = new Dictionary<string, string>();
|
||||
var extra = new Dictionary<string, string?>();
|
||||
|
||||
// Retries will hang the http request
|
||||
extra["number-of-retries"] = "0";
|
||||
|
||||
try
|
||||
{
|
||||
var r = Runner.Run(Runner.CreateListFilesetsTask(bk, extra), false) as IListFilesetResults;
|
||||
var r = Runner.Run(connection, eventPollNotify, Runner.CreateListFilesetsTask(bk, extra), false) as IListFilesetResults;
|
||||
if (r == null)
|
||||
throw new ServerErrorException("No result from list operation");
|
||||
|
||||
@@ -112,9 +112,9 @@ public class BackupListing : IEndpointV2
|
||||
}
|
||||
}
|
||||
|
||||
private static Dto.V2.ListFileVersionsOutputDto ExecuteListVersions(IBackup bk, Dto.V2.ListFileVersionsRequestDto input)
|
||||
private static Dto.V2.ListFileVersionsOutputDto ExecuteListVersions(Connection connection, EventPollNotify eventPollNotify, IBackup bk, Dto.V2.ListFileVersionsRequestDto input)
|
||||
{
|
||||
var r = Runner.Run(Runner.ListFileVersionsTask(bk, input.Paths, input.PageSize ?? 1000, input.Page ?? 0), false) as IListFileVersionsResults;
|
||||
var r = Runner.Run(connection, eventPollNotify, Runner.ListFileVersionsTask(bk, input.Paths, input.PageSize ?? 1000, input.Page ?? 0), false) as IListFileVersionsResults;
|
||||
if (r == null)
|
||||
throw new ServerErrorException("No result from list operation");
|
||||
|
||||
@@ -135,13 +135,13 @@ public class BackupListing : IEndpointV2
|
||||
r.FileVersions.TotalCount);
|
||||
}
|
||||
|
||||
private static Dto.V2.SearchEntriesResponseDto ExecuteSearch(IBackup bk, Dto.V2.SearchEntriesRequestDto input)
|
||||
private static Dto.V2.SearchEntriesResponseDto ExecuteSearch(Connection connection, EventPollNotify eventPollNotify, IBackup bk, Dto.V2.SearchEntriesRequestDto input)
|
||||
{
|
||||
var time = string.IsNullOrWhiteSpace(input.Time)
|
||||
? new DateTime(0)
|
||||
: Library.Utility.Timeparser.ParseTimeInterval(input.Time, DateTime.Now);
|
||||
|
||||
var r = Runner.Run(Runner.CreateSearchEntriesTask(bk, input.Filters, input.Paths, time, input.PageSize ?? 1000, input.Page ?? 0), false) as ISearchFilesResults;
|
||||
var r = Runner.Run(connection, eventPollNotify, Runner.CreateSearchEntriesTask(bk, input.Filters, input.Paths, time, input.PageSize ?? 1000, input.Page ?? 0), false) as ISearchFilesResults;
|
||||
if (r == null)
|
||||
throw new ServerErrorException("No result from list operation");
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public class DestinationVerify : IEndpointV2
|
||||
public static void Map(RouteGroupBuilder group)
|
||||
{
|
||||
group.MapPost("/destination/test", ([FromServices] Connection connection, [FromBody] Dto.V2.DestinationTestRequestDto input, CancellationToken cancelToken)
|
||||
=> ExecuteTest(input, cancelToken))
|
||||
=> ExecuteTest(connection, input, cancelToken))
|
||||
.RequireAuthorization();
|
||||
}
|
||||
|
||||
@@ -51,11 +51,11 @@ public class DestinationVerify : IEndpointV2
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string?> ParseUrlOptions(Library.Utility.Uri uri)
|
||||
private static Dictionary<string, string?> ParseUrlOptions(Connection connection, Library.Utility.Uri uri)
|
||||
{
|
||||
var qp = uri.QueryParameters;
|
||||
|
||||
var opts = Runner.GetCommonOptions();
|
||||
var opts = Runner.GetCommonOptions(connection);
|
||||
foreach (var k in qp.Keys.Cast<string>())
|
||||
opts[k] = qp[k];
|
||||
|
||||
@@ -75,10 +75,10 @@ public class DestinationVerify : IEndpointV2
|
||||
return modules;
|
||||
}
|
||||
|
||||
private static async Task<TupleDisposeWrapper> GetBackend(string url, CancellationToken cancelToken)
|
||||
private static async Task<TupleDisposeWrapper> GetBackend(Connection connection, string url, CancellationToken cancelToken)
|
||||
{
|
||||
var uri = new Library.Utility.Uri(url);
|
||||
var opts = ParseUrlOptions(uri);
|
||||
var opts = ParseUrlOptions(connection, uri);
|
||||
|
||||
var tmp = new[] { uri };
|
||||
await SecretProviderHelper.ApplySecretProviderAsync([], tmp, opts, Library.Utility.TempFolder.SystemTempPath, FIXMEGlobal.SecretProvider, cancelToken);
|
||||
@@ -90,13 +90,13 @@ public class DestinationVerify : IEndpointV2
|
||||
}
|
||||
|
||||
|
||||
private static async Task<DestinationTestResponseDto> ExecuteTest(DestinationTestRequestDto input, CancellationToken cancelToken)
|
||||
private static async Task<DestinationTestResponseDto> ExecuteTest(Connection connection, DestinationTestRequestDto input, CancellationToken cancelToken)
|
||||
{
|
||||
TupleDisposeWrapper? wrapper = null;
|
||||
|
||||
try
|
||||
{
|
||||
wrapper = await GetBackend(input.DestinationUrl, cancelToken);
|
||||
wrapper = await GetBackend(connection, input.DestinationUrl, cancelToken);
|
||||
|
||||
using (var b = wrapper.Backend)
|
||||
{
|
||||
|
||||
@@ -136,7 +136,7 @@ public class QueueRunnerService(Connection connection, EventPollNotify eventPoll
|
||||
if (task.OnStarting != null)
|
||||
await task.OnStarting().ConfigureAwait(false);
|
||||
|
||||
await task.Execute();
|
||||
Runner.Run(connection, eventPollNotify, task, false);
|
||||
|
||||
// If the task is completed, don't call OnFinished again
|
||||
completed = true;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
using Duplicati.Library.RestAPI;
|
||||
using Duplicati.Server;
|
||||
using Duplicati.Server.Database;
|
||||
using Duplicati.Server.Serialization.Interface;
|
||||
using Duplicati.WebserverCore.Abstractions;
|
||||
|
||||
@@ -29,9 +30,9 @@ namespace WebserverCore.Services;
|
||||
public class SchedulerService : ISchedulerService
|
||||
{
|
||||
private readonly Scheduler scheduler;
|
||||
public SchedulerService(EventPollNotify eventPollNotify, INotificationUpdateService notificationUpdateService, IQueueRunnerService queueRunnerService)
|
||||
public SchedulerService(Connection connection, EventPollNotify eventPollNotify, INotificationUpdateService notificationUpdateService, IQueueRunnerService queueRunnerService)
|
||||
{
|
||||
this.scheduler = new Scheduler(queueRunnerService);
|
||||
this.scheduler = new Scheduler(connection, queueRunnerService);
|
||||
var lastScheduleId = notificationUpdateService.LastDataUpdateId;
|
||||
eventPollNotify.NewEvent += (sender, e) =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user