This adds a Windows Shell Extension that shows overlay icons on folders that are included in Duplicati backups, similar to how cloud storage solutions like OneDrive and Google Drive indicate sync status. The feature includes: - New API endpoint /api/v1/folderstatus to query backup status of folders - Windows Shell Icon Overlay handlers for backed up, warning, error and syncing states - Client methods in DuplicatiServerClient for the new endpoint - Registration script for the COM shell extension Closes #6450
912 lines
46 KiB
C#
912 lines
46 KiB
C#
// Copyright (C) 2025, The Duplicati Team
|
|
// https://duplicati.com, hello@duplicati.com
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// 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 System.Net;
|
|
using System.Net.Http.Headers;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using Duplicati.WebserverCore.Dto;
|
|
using Duplicati.WebserverCore.Dto.V2;
|
|
using Duplicati.WebserverCore.Endpoints.V1.Backup;
|
|
|
|
namespace Duplicati.WebserverCore.Client;
|
|
|
|
/// <summary>
|
|
/// A client for interacting with the Duplicati server API, supporting both v1 and v2 endpoints.
|
|
/// </summary>
|
|
public class DuplicatiServerClient : IDisposable
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
private readonly string _baseUrl;
|
|
private readonly JsonSerializerOptions _jsonOptions;
|
|
private bool _disposed;
|
|
private readonly bool _selfOwnedHttpClient;
|
|
private readonly SemaphoreSlim _tokenRefreshSemaphore = new(1, 1);
|
|
private readonly ServerCredentialType _credentialType;
|
|
private readonly string _credential;
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the client is authenticated.
|
|
/// </summary>
|
|
public bool IsAuthenticated => !string.IsNullOrEmpty(_httpClient.DefaultRequestHeaders.Authorization?.Parameter);
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="DuplicatiServerClient"/> class.
|
|
/// </summary>
|
|
/// <param name="baseUrl">The base URL of the Duplicati server.</param>
|
|
/// <param name="credentialType">The type of credential being provided (Password or Token).</param>
|
|
/// <param name="credential">The server password or access token.</param>
|
|
/// <param name="httpClient">Optional HttpClient instance. If not provided, a new one will be created.</param>
|
|
public DuplicatiServerClient(string baseUrl, ServerCredentialType credentialType, string credential, HttpClient? httpClient = null)
|
|
{
|
|
_selfOwnedHttpClient = httpClient is not null;
|
|
_baseUrl = baseUrl.TrimEnd('/');
|
|
_httpClient = httpClient ?? new HttpClient();
|
|
_jsonOptions = new JsonSerializerOptions
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
WriteIndented = true,
|
|
AllowTrailingCommas = true
|
|
};
|
|
_credential = credential;
|
|
_credentialType = credentialType;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Authenticates the client with the Duplicati server using the provided credentials and acquires a bearer token.
|
|
///
|
|
/// If a call is made to another method before calling Authenticate, it will automatically call this method to ensure the client is authenticated.
|
|
/// The idea of having a separate Authenticate method is to allow for explicit authentication to avoid getting a 401 result on the server log.
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
public async Task Authenticate(CancellationToken cancellationToken = default)
|
|
{
|
|
await RefreshTokenAsync(cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Acquires a bearer token using password-based authentication.
|
|
/// </summary>
|
|
/// <param name="password">The server password.</param>
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
private async Task AcquireBearerViaPasswordAuthAsync(string password, CancellationToken cancellationToken)
|
|
{
|
|
var loginResult = await LoginV1Async(new LoginInputDto(password, true), cancellationToken).ConfigureAwait(false);
|
|
SetTokenAuthentication(loginResult.AccessToken);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Acquires a bearer token using token-based authentication.
|
|
/// </summary>
|
|
/// <param name="token">The signin token.</param>
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
private async Task AcquireBearerViaTokenAuthAsync(string token, CancellationToken cancellationToken)
|
|
{
|
|
var signinResult = await SigninV1Async(new SigninInputDto(token, true), cancellationToken).ConfigureAwait(false);
|
|
if (!string.IsNullOrEmpty(signinResult.AccessToken))
|
|
SetTokenAuthentication(signinResult.AccessToken);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets token-based authentication.
|
|
/// </summary>
|
|
/// <param name="token">The authentication token.</param>
|
|
public void SetTokenAuthentication(string token)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(token))
|
|
throw new ArgumentException("Token cannot be null or empty");
|
|
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Makes an HTTP GET request to the specified endpoint.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the response data.</typeparam>
|
|
/// <param name="endpoint">The API endpoint.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <param name="retryOn401">Whether to retry on 401 unauthorized responses.</param>
|
|
/// <returns>The response data.</returns>
|
|
private async Task<T> GetAsync<T>(string endpoint, CancellationToken cancellationToken = default, bool retryOn401 = true)
|
|
{
|
|
return await ExecuteWithRetryAsync<T>(async () =>
|
|
{
|
|
using var response = await _httpClient.GetAsync($"{_baseUrl}{endpoint}", cancellationToken).ConfigureAwait(false);
|
|
return await ProcessResponseAsync<T>(response, cancellationToken).ConfigureAwait(false);
|
|
}, retryOn401, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Makes an HTTP POST request to the specified endpoint.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the response data.</typeparam>
|
|
/// <param name="endpoint">The API endpoint.</param>
|
|
/// <param name="data">The request data.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <param name="retryOn401">Whether to retry on 401 unauthorized responses.</param>
|
|
/// <returns>The response data.</returns>
|
|
private async Task<T> PostAsync<T>(string endpoint, object? data, CancellationToken cancellationToken = default, bool retryOn401 = true)
|
|
{
|
|
return await ExecuteWithRetryAsync<T>(async () =>
|
|
{
|
|
var json = data != null ? JsonSerializer.Serialize(data, _jsonOptions) : string.Empty;
|
|
using var content = new StringContent(json, Encoding.UTF8, "application/json");
|
|
using var response = await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content, cancellationToken).ConfigureAwait(false);
|
|
return await ProcessResponseAsync<T>(response, cancellationToken).ConfigureAwait(false);
|
|
}, retryOn401, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Makes an HTTP PUT request to the specified endpoint.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the response data.</typeparam>
|
|
/// <param name="endpoint">The API endpoint.</param>
|
|
/// <param name="data">The request data.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <param name="retryOn401">Whether to retry on 401 unauthorized responses.</param>
|
|
/// <returns>The response data.</returns>
|
|
private async Task<T> PutAsync<T>(string endpoint, object? data, CancellationToken cancellationToken = default, bool retryOn401 = true)
|
|
{
|
|
return await ExecuteWithRetryAsync<T>(async () =>
|
|
{
|
|
var json = data != null ? JsonSerializer.Serialize(data, _jsonOptions) : string.Empty;
|
|
using var content = new StringContent(json, Encoding.UTF8, "application/json");
|
|
using var response = await _httpClient.PutAsync($"{_baseUrl}{endpoint}", content, cancellationToken).ConfigureAwait(false);
|
|
return await ProcessResponseAsync<T>(response, cancellationToken).ConfigureAwait(false);
|
|
}, retryOn401, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Makes an HTTP DELETE request to the specified endpoint.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the response data.</typeparam>
|
|
/// <param name="endpoint">The API endpoint.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <param name="retryOn401">Whether to retry on 401 unauthorized responses.</param>
|
|
/// <returns>The response data.</returns>
|
|
private async Task<T> DeleteAsync<T>(string endpoint, CancellationToken cancellationToken = default, bool retryOn401 = true)
|
|
{
|
|
return await ExecuteWithRetryAsync<T>(async () =>
|
|
{
|
|
using var response = await _httpClient.DeleteAsync($"{_baseUrl}{endpoint}", cancellationToken).ConfigureAwait(false);
|
|
return await ProcessResponseAsync<T>(response, cancellationToken).ConfigureAwait(false);
|
|
}, retryOn401, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Processes an HTTP response and handles potential errors.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the response data.</typeparam>
|
|
/// <param name="response">The HTTP response message.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The response data.</returns>
|
|
private async Task<T> ProcessResponseAsync<T>(HttpResponseMessage response, CancellationToken cancellationToken = default)
|
|
{
|
|
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
|
{
|
|
throw new UnauthorizedAccessException("Unauthorized access - token may be expired");
|
|
}
|
|
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
if (typeof(T) == typeof(Stream))
|
|
{
|
|
// Response is a stream, read the response into it
|
|
var ms = new MemoryStream();
|
|
var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
|
await stream.CopyToAsync(ms, cancellationToken).ConfigureAwait(false);
|
|
ms.Position = 0; // rewind
|
|
return (T)(object)ms;
|
|
}
|
|
else
|
|
{
|
|
var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
|
|
return JsonSerializer.Deserialize<T>(content)!;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes an HTTP request with retry logic for 401 unauthorized responses.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the response data.</typeparam>
|
|
/// <param name="operation">The HTTP operation to execute.</param>
|
|
/// <param name="retryOn401">Whether to retry on 401 unauthorized responses.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The response data.</returns>
|
|
private async Task<T> ExecuteWithRetryAsync<T>(Func<Task<T>> operation, bool retryOn401, CancellationToken cancellationToken = default)
|
|
{
|
|
const int maxRetries = 3;
|
|
var attempts = 0;
|
|
|
|
while (attempts <= maxRetries)
|
|
{
|
|
try
|
|
{
|
|
return await operation().ConfigureAwait(false);
|
|
}
|
|
catch (UnauthorizedAccessException) when (retryOn401 && attempts < maxRetries)
|
|
{
|
|
attempts++;
|
|
await RefreshTokenAsync(cancellationToken).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
// This should never be reached due to the loop logic, but included for completeness
|
|
throw new UnauthorizedAccessException("Maximum retry attempts reached for token refresh");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refreshes the authentication token based on the credential type.
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
private async Task RefreshTokenAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
await _tokenRefreshSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
try
|
|
{
|
|
switch (_credentialType)
|
|
{
|
|
case ServerCredentialType.Password:
|
|
await AcquireBearerViaPasswordAuthAsync(_credential, cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
case ServerCredentialType.Token:
|
|
await AcquireBearerViaTokenAuthAsync(_credential, cancellationToken).ConfigureAwait(false);
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException($"Unsupported credential type: {_credentialType}");
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_tokenRefreshSemaphore.Release();
|
|
}
|
|
}
|
|
|
|
// V1 Authentication Methods
|
|
|
|
/// <summary>
|
|
/// Performs password-based login to the Duplicati server (V1).
|
|
/// </summary>
|
|
/// <param name="input">The login input containing password and remember me flag.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The access token output.</returns>
|
|
public async Task<AccessTokenOutputDto> LoginV1Async(LoginInputDto input, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<AccessTokenOutputDto>("/api/v1/auth/login", input, cancellationToken, false).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs token-based signin to the Duplicati server (V1).
|
|
/// </summary>
|
|
/// <param name="input">The signin input containing signin token and remember me flag.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The access token output.</returns>
|
|
public async Task<AccessTokenOutputDto> SigninV1Async(SigninInputDto input, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<AccessTokenOutputDto>("/api/v1/auth/signin", input, cancellationToken, false).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refreshes the access token using the refresh token (V1).
|
|
/// </summary>
|
|
/// <param name="nonce">The nonce to include in the refresh request. Optional, can be null.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The new access token output.</returns>
|
|
public async Task<AccessTokenOutputDto> RefreshTokenV1Async(string? nonce, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<AccessTokenOutputDto>("/api/v1/auth/refresh", string.IsNullOrWhiteSpace(nonce) ? null : new { Nonce = nonce }, cancellationToken, false).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Issues a signin token for authentication (V1).
|
|
/// </summary>
|
|
/// <param name="input">The signin token input.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The signin token output.</returns>
|
|
public async Task<SigninTokenOutputDto> IssueSigninTokenV1Async(IssueSigninTokenInputDto input, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<SigninTokenOutputDto>("/api/v1/auth/issuesignintoken", input, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Issues a single-operation token for a specific operation (V1).
|
|
/// </summary>
|
|
/// <param name="operation">The operation name.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The single operation token output.</returns>
|
|
public async Task<SingleOperationTokenOutputDto> IssueTokenV1Async(string operation, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<SingleOperationTokenOutputDto>($"/api/v1/auth/issuetoken/{operation}", null, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Issues a forever token for long-term authentication (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The single operation token output.</returns>
|
|
public async Task<SingleOperationTokenOutputDto> IssueForeverTokenV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<SingleOperationTokenOutputDto>("/api/v1/auth/issue-forever-token", null, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Logs out and invalidates the refresh token (V1).
|
|
/// </summary>
|
|
/// <param name="nonce">The nonce to include in the logout request.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>A task representing the asynchronous operation.</returns>
|
|
public async Task LogoutV1Async(string? nonce, CancellationToken cancellationToken = default)
|
|
{
|
|
await PostAsync<object>("/api/v1/auth/refresh/logout", string.IsNullOrWhiteSpace(nonce) ? null : new { Nonce = nonce }, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V1 Backup Management Methods
|
|
|
|
/// <summary>
|
|
/// Lists all backups configured on the server (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The list of backups.</returns>
|
|
public async Task<BackupAndScheduleOutputDto[]> ListBackupsV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<BackupAndScheduleOutputDto[]>("/api/v1/backups", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new backup configuration (V1).
|
|
/// </summary>
|
|
/// <param name="backup">The backup configuration to create.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The created backup configuration.</returns>
|
|
public async Task<BackupDto> CreateBackupV1Async(BackupDto backup, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<BackupDto>("/api/v1/backups", backup, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Imports a backup configuration (V1).
|
|
/// </summary>
|
|
/// <param name="input">The import backup input.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The import backup output.</returns>
|
|
public async Task<ImportBackupOutputDto> ImportBackupV1Async(ImportBackupInputDto input, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<ImportBackupOutputDto>("/api/v1/backups/import", input, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets details of a specific backup (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The backup details.</returns>
|
|
public async Task<BackupGet.GetBackupResultDto> GetBackupV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<BackupGet.GetBackupResultDto>($"/api/v1/backup/{backupId}", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates a backup configuration (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="backup">The updated backup configuration.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The updated backup configuration.</returns>
|
|
public async Task<BackupDto> UpdateBackupV1Async(string backupId, BackupDto backup, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PutAsync<BackupDto>($"/api/v1/backup/{backupId}", backup, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a backup configuration (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The delete backup output.</returns>
|
|
public async Task<DeleteBackupOutputDto> DeleteBackupV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await DeleteAsync<DeleteBackupOutputDto>($"/api/v1/backup/{backupId}", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V1 Backup Operations
|
|
|
|
/// <summary>
|
|
/// Starts a backup operation (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> StartBackupV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/start", null, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs a backup operation (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> RunBackupV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/run", null, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restores files from a backup (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="input">The restore input parameters.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> RestoreBackupV1Async(string backupId, RestoreInputDto input, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/restore", input, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies a backup's integrity (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> VerifyBackupV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/verify", null, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Repairs a backup (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="input">The repair input parameters.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> RepairBackupV1Async(string backupId, RepairInputDto input, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/repair", input, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Repairs and updates a backup (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> RepairUpdateBackupV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/repairupdate", null, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compacts a backup (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> CompactBackupV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/compact", null, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Vacuums a backup database (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> VacuumBackupV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/vacuum", null, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V1 Backup Data Access
|
|
|
|
/// <summary>
|
|
/// Lists files in a backup (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The list of files.</returns>
|
|
public async Task<TreeNodeDto[]> ListFilesV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<TreeNodeDto[]>($"/api/v1/backup/{backupId}/files", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lists backup filesets (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The list of filesets.</returns>
|
|
public async Task<object[]> ListFilesetsV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<object[]>($"/api/v1/backup/{backupId}/filesets", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the backup log (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The backup log entries.</returns>
|
|
public async Task<LogEntry[]> GetBackupLogV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<LogEntry[]>($"/api/v1/backup/{backupId}/log", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the remote operation log (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The remote log entries.</returns>
|
|
public async Task<LogEntry[]> GetRemoteLogV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<LogEntry[]>($"/api/v1/backup/{backupId}/remotelog", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V1 Database Management
|
|
|
|
/// <summary>
|
|
/// Deletes a backup database (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> DeleteDatabaseV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/deletedb", null, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves a backup database (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="input">The database path input.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> MoveDatabaseV1Async(string backupId, UpdateDbPathInputDto input, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/movedb", input, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the database path (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="input">The database path input.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task started information.</returns>
|
|
public async Task<TaskStartedDto> UpdateDatabaseV1Async(string backupId, UpdateDbPathInputDto input, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<TaskStartedDto>($"/api/v1/backup/{backupId}/updatedb", input, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V1 Export Operations
|
|
|
|
/// <summary>
|
|
/// Exports a backup configuration (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The backup configuration.</returns>
|
|
public async Task<Stream> ExportBackupV1Async(string backupId, bool exportPasswords, string passPhrase, string exportToken, CancellationToken cancellationToken = default)
|
|
{
|
|
|
|
// [FromRoute] string id, [FromQuery(Name = "export-passwords")] bool? exportPasswords, [FromQuery] string? passphrase, [FromQuery] string token
|
|
return await GetAsync<Stream>($"/api/v1/backup/{backupId}/export?exportpasswords={exportPasswords}&passphrase={Uri.EscapeDataString(passPhrase)}&token={Uri.EscapeDataString(exportToken)}", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exports backup as command line (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The command line export.</returns>
|
|
public async Task<ExportCommandlineDto> ExportCommandlineV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<ExportCommandlineDto>($"/api/v1/backup/{backupId}/export-cmdline", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Exports backup arguments only (V1).
|
|
/// </summary>
|
|
/// <param name="backupId">The backup identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The arguments export.</returns>
|
|
public async Task<ExportArgsOnlyDto> ExportArgsOnlyV1Async(string backupId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<ExportArgsOnlyDto>($"/api/v1/backup/{backupId}/export-argsonly", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V1 Server Management
|
|
|
|
/// <summary>
|
|
/// Gets the server state (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The server status.</returns>
|
|
public async Task<ServerStatusDto> GetServerStateV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<ServerStatusDto>("/api/v1/serverstate", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pauses the server (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>A task representing the asynchronous operation.</returns>
|
|
public async Task PauseServerV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
await PostAsync<object>("/api/v1/serverstate/pause", null, cancellationToken);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resumes the server (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>A task representing the asynchronous operation.</returns>
|
|
public async Task ResumeServerV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
await PostAsync<object>("/api/v1/serverstate/resume", null, cancellationToken);
|
|
}
|
|
|
|
// V1 Task Management
|
|
|
|
/// <summary>
|
|
/// Lists all active tasks (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The list of active tasks.</returns>
|
|
public async Task<object[]> ListTasksV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<object[]>("/api/v1/tasks", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets details of a specific task (V1).
|
|
/// </summary>
|
|
/// <param name="taskId">The task identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The task details.</returns>
|
|
public async Task<GetTaskStateDto> GetTaskV1Async(string taskId, CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<GetTaskStateDto>($"/api/v1/task/{taskId}", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops a running task (V1).
|
|
/// </summary>
|
|
/// <param name="taskId">The task identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>A task representing the asynchronous operation.</returns>
|
|
public async Task StopTaskV1Async(string taskId, CancellationToken cancellationToken = default)
|
|
{
|
|
await PostAsync<object>($"/api/v1/task/{taskId}/stop", null, cancellationToken);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aborts a running task (V1).
|
|
/// </summary>
|
|
/// <param name="taskId">The task identifier.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>A task representing the asynchronous operation.</returns>
|
|
public async Task AbortTaskV1Async(string taskId, CancellationToken cancellationToken = default)
|
|
{
|
|
await PostAsync<object>($"/api/v1/task/{taskId}/abort", null, cancellationToken);
|
|
}
|
|
|
|
// V1 System Information
|
|
|
|
/// <summary>
|
|
/// Gets system information (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The system information.</returns>
|
|
public async Task<SystemInfoDto> GetSystemInfoV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<SystemInfoDto>("/api/v1/systeminfo", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the changelog (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The changelog entries.</returns>
|
|
public async Task<ChangelogDto[]> GetChangelogV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<ChangelogDto[]>("/api/v1/changelog", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets license information (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The license information.</returns>
|
|
public async Task<LicenseDto[]> GetLicensesV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<LicenseDto[]>("/api/v1/licenses", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets acknowledgements (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The acknowledgements.</returns>
|
|
public async Task<AcknowlegdementDto[]> GetAcknowledgementsV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<AcknowlegdementDto[]>("/api/v1/acknowledgements", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V1 Settings Management
|
|
|
|
/// <summary>
|
|
/// Gets server settings (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The server settings.</returns>
|
|
public async Task<SettingDto[]> GetServerSettingsV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<SettingDto[]>("/api/v1/serversetting", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates server settings (V1).
|
|
/// </summary>
|
|
/// <param name="settings">The settings to update.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The updated settings.</returns>
|
|
public async Task<SettingDto[]> UpdateServerSettingsV1Async(SettingDto[] settings, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PutAsync<SettingDto[]>("/api/v1/serversetting", settings, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V1 Filesystem Operations
|
|
|
|
/// <summary>
|
|
/// Browses the filesystem (V1).
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The filesystem entries.</returns>
|
|
public async Task<TreeNodeDto[]> BrowseFilesystemV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<TreeNodeDto[]>("/api/v1/filesystem", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs filesystem operations (V1).
|
|
/// </summary>
|
|
/// <param name="data">The filesystem operation data.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The operation result.</returns>
|
|
public async Task<object> PerformFilesystemOperationV1Async(object data, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<object>("/api/v1/filesystem", data, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V2 API Methods
|
|
|
|
/// <summary>
|
|
/// Lists filesets with pagination (V2).
|
|
/// </summary>
|
|
/// <param name="request">The list filesets request.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The paged filesets response.</returns>
|
|
public async Task<PagedResponseEnvelope<ListFilesetsResponseDto>> ListFilesetsV2Async(ListFilesetsRequestDto request, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<PagedResponseEnvelope<ListFilesetsResponseDto>>("/api/v2/backup/list-filesets", request, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lists folder contents with pagination (V2).
|
|
/// </summary>
|
|
/// <param name="request">The list folder content request.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The paged folder content response.</returns>
|
|
public async Task<PagedResponseEnvelope<ListFolderContentResponseDto>> ListFolderContentV2Async(ListFolderContentRequestDto request, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<PagedResponseEnvelope<ListFolderContentResponseDto>>("/api/v2/backup/list-folder", request, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lists file versions with pagination (V2).
|
|
/// </summary>
|
|
/// <param name="request">The list file versions request.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The paged file versions response.</returns>
|
|
public async Task<PagedResponseEnvelope<ListFileVersionsOutputDto>> ListFileVersionsV2Async(ListFileVersionsRequestDto request, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<PagedResponseEnvelope<ListFileVersionsOutputDto>>("/api/v2/backup/list-versions", request, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Searches entries with filters (V2).
|
|
/// </summary>
|
|
/// <param name="request">The search entries request.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The paged search results response.</returns>
|
|
public async Task<PagedResponseEnvelope<SearchEntriesResponseDto>> SearchEntriesV2Async(SearchEntriesRequestDto request, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<PagedResponseEnvelope<SearchEntriesResponseDto>>("/api/v2/backup/search", request, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests destination connectivity (V2).
|
|
/// </summary>
|
|
/// <param name="request">The destination test request.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The destination test response.</returns>
|
|
public async Task<ResponseEnvelope<DestinationTestResponseDto>> TestDestinationV2Async(DestinationTestRequestDto request, CancellationToken cancellationToken = default)
|
|
{
|
|
return await PostAsync<ResponseEnvelope<DestinationTestResponseDto>>("/api/v2/destination/test", request, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
// V1 Folder Status Methods
|
|
|
|
/// <summary>
|
|
/// Gets the backup status for all source folders across all backups (V1).
|
|
/// Used by the Windows Shell Extension to show overlay icons on backed up folders.
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The list of folder statuses.</returns>
|
|
public async Task<FolderStatusDto[]> GetFolderStatusesV1Async(CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<FolderStatusDto[]>("/api/v1/folderstatus", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the backup status for a specific folder path (V1).
|
|
/// Used by the Windows Shell Extension to show overlay icons on backed up folders.
|
|
/// </summary>
|
|
/// <param name="path">The folder path to check.</param>
|
|
/// <param name="cancellationToken">The cancellation token. Optional, defaults to <see cref="CancellationToken.None"/>.</param>
|
|
/// <returns>The folder status.</returns>
|
|
public async Task<FolderStatusDto> GetFolderStatusV1Async(string path, CancellationToken cancellationToken = default)
|
|
{
|
|
return await GetAsync<FolderStatusDto>($"/api/v1/folderstatus/{Uri.EscapeDataString(path)}", cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases all resources used by the <see cref="DuplicatiServerClient"/>.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases the unmanaged resources used by the <see cref="DuplicatiServerClient"/> and optionally releases the managed resources.
|
|
/// </summary>
|
|
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (_disposed) return;
|
|
if (disposing)
|
|
{
|
|
if (_selfOwnedHttpClient)
|
|
_httpClient?.Dispose();
|
|
_tokenRefreshSemaphore?.Dispose();
|
|
}
|
|
_disposed = true;
|
|
}
|
|
}
|