Fixed UI and added some verbose logging

This commit is contained in:
Kenneth Skovhede
2025-03-27 21:32:25 +01:00
parent e566787d60
commit 4e2f40ce35
6 changed files with 65 additions and 11 deletions
+13 -4
View File
@@ -20,6 +20,7 @@
// DEALINGS IN THE SOFTWARE.
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
@@ -269,13 +270,13 @@ public class FilenClient : IDisposable
{
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
var result = JsonSerializer.Deserialize<FilenResponseEnvelope<T>>(json);
//var result = await response.Content.ReadFromJsonAsync<FilenResponse<T>>(cancellationToken).ConfigureAwait(false);
// var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
// var result = JsonSerializer.Deserialize<FilenResponseEnvelope<T>>(json);
var result = await response.Content.ReadFromJsonAsync<FilenResponseEnvelope<T>>(cancellationToken).ConfigureAwait(false);
if (result is null)
throw new Exception("Failed to read response");
if (!result.Status || !string.IsNullOrWhiteSpace(result.Error))
throw new Exception($"Server returned error: {result.Error ?? result.Message ?? "Unknown"}");
throw new UserInformationException($"{result.Error ?? result.Message ?? "Unknown"}", "FilenAPIError");
return result.Data;
}
@@ -456,6 +457,8 @@ public class FilenClient : IDisposable
bytesRead += read;
}
Logging.Log.WriteVerboseMessage(LOGTAG, "UploadingChunk", "Uploading chunk {0}, size {1}", chunk, bytesRead);
// Stream is exhausted
if (bytesRead == 0) break;
@@ -482,6 +485,8 @@ public class FilenClient : IDisposable
chunk++;
}
Logging.Log.WriteVerboseMessage(LOGTAG, "UploadingChunk", "Finished uploading {0} chunks", chunk);
return chunk;
}
@@ -554,6 +559,10 @@ public class FilenClient : IDisposable
var totalSize = file.Size;
while (downloaded < totalSize)
{
if (chunk >= file.Chunks)
throw new Exception($"Attempted to download more chunks than available, expected {file.Chunks}, got {chunk}. File: {file.Name}, total size: {totalSize}, downloaded: {downloaded}");
Logging.Log.WriteVerboseMessage(LOGTAG, "DownloadingChunk", "Downloading chunk {0}, size {1}", chunk, totalSize - downloaded);
var url = $"{EgestUrls[Random.Shared.Next(0, EgestUrls.Count)]}/{file.Region}/{file.Bucket}/{file.Uuid}/{chunk}";
using var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _authResult.ApiKey);
@@ -233,13 +233,13 @@ public static class FilenCrypto
}
else if (version == "003")
{
Logging.Log.WriteWarningMessage(LOGTAG, "EncV3NotTested", null, "Encryption version 3 is not tested.");
Logging.Log.WriteVerboseMessage(LOGTAG, "EncV3NotTested", "Encryption version 3 encoutered for metadata, but this is not tested.");
if (key.Key.Length != 64)
throw new ArgumentException($"Invalid key length {key.Key.Length}. Expected 64 (hex).", nameof(key));
var keyBuffer = Convert.FromHexString(key.Key);
var ivBuffer = Convert.FromHexString(metadata.Substring(3, 24)); // Sliced from 3, length 24
var encrypted = Convert.FromBase64String(metadata.Substring(27));
var ivBuffer = Convert.FromHexString(metadata.Substring(3, 12)); // Sliced from 3, length 24
var encrypted = Convert.FromBase64String(metadata.Substring(15));
return Encoding.UTF8.GetString(AesGcmDecrypt(keyBuffer, ivBuffer, encrypted));
}
+2 -2
View File
@@ -23,8 +23,8 @@ namespace Duplicati.Library.Backend.Strings
{
internal static class FilenBackend
{
public static string Description => LC.L(@"This backend can read and write data to Filen.io using its REST protocol. Allowed format is ""filen://folder/subfolder"".");
public static string DisplayName => LC.L(@"Local folder or drive");
public static string Description => LC.L(@"This backend can read and write data to Filen.io using its REST protocol. Supported format is ""filen://folder/subfolder"".");
public static string DisplayName => LC.L(@"Filen.io");
public static string TwoFactorShort => LC.L(@"Optional 2-factor code");
public static string TwoFactorLong => LC.L(@"The 2-factor code to use for authentication, leave empty if the account is not MFA protected. Not that a new code must be provided by the user for each authentication attempt.");
public static string MoveToTrashShort => LC.L(@"Move to trash");
@@ -35,8 +35,9 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
EditUriBackendConfig.templates['aliyunoss'] = 'templates/backends/aliyunoss.html';
EditUriBackendConfig.templates['e2'] = 'templates/backends/e2.html';
EditUriBackendConfig.templates['pcloud'] = 'templates/backends/pcloud.html';
EditUriBackendConfig.templates['smb'] = 'templates/backends/smb.html';
EditUriBackendConfig.templates['smb'] = 'templates/backends/smb.html';
EditUriBackendConfig.templates['cifs'] = 'templates/backends/smb.html';
EditUriBackendConfig.templates['filen'] = 'templates/backends/filen.html';
EditUriBackendConfig.testers['s3'] = function(scope, callback) {
@@ -637,6 +638,15 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
EditUriBackendConfig.parsers['cifs'] = EditUriBackendConfig.parsers['smb'];
EditUriBackendConfig.parsers['filen'] = function (scope, module, server, path, port, options) {
console.log("filen parser", scope, module, server, path, port, options);
var p1 = server ?? '';
var p2 = path ?? '';
var combined = p1;
if (p1.length > 0 && p2.length > 0)
combined += '/' + p2;
scope.Path = combined;
};
// Builders take the scope and produce the uri output
EditUriBackendConfig.builders['s3'] = function (scope) {
@@ -1007,6 +1017,22 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
EditUriBackendConfig.builders['cifs'] = EditUriBackendConfig.builders['smb'];
EditUriBackendConfig.builders['filen'] = function (scope) {
var opts = {};
EditUriBackendConfig.merge_in_advanced_options(scope, opts, true);
// Remove trailing and leading slashes
if (scope.Path)
scope.Path = scope.Path.replace(/^[/\\]+/, '').replace(/[/\\]+$/, '');
else
scope.Path = '';
return AppUtils.format('{0}://{1}{2}',
scope.Backend.Key,
scope.Path,
AppUtils.encodeDictAsUrl(opts)
);
}
EditUriBackendConfig.validaters['smb'] = function (scope, continuation) {
if (EditUriBackendConfig.require_server(scope)
&& EditUriBackendConfig.require_username(scope)
@@ -1016,6 +1042,11 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
EditUriBackendConfig.validaters['smb'] = EditUriBackendConfig.validaters['cifs'];
EditUriBackendConfig.validaters['filen'] = function (scope, continuation) {
if (EditUriBackendConfig.require_username_and_password(scope))
EditUriBackendConfig.recommend_path(scope, continuation);
};
EditUriBackendConfig.validaters['file'] = function (scope, continuation) {
if (EditUriBackendConfig.require_path(scope))
continuation();
@@ -78,7 +78,8 @@ backupApp.service('SystemInfo', function($rootScope, $timeout, $cookies, AppServ
'rclone': null,
'cos': null,
'aliyunoss': null,
'pcloud': null
'pcloud': null,
'filen': null,
}
};
@@ -0,0 +1,13 @@
<div class="input text">
<label for="generic_path" translate>Path on server</label>
<input type="text" name="generic_path" id="generic_path" ng-model="$parent.Path" placeholder="{{'Destination path' | translate}}" />
</div>
<div class="input text">
<label for="generic_username" translate>Username</label>
<input type="text" name="generic_username" id="generic_username" ng-model="$parent.Username" placeholder="{{'Authentication username' | translate}}" />
</div>
<div class="input password">
<label for="generic_password" translate>Password</label>
<input autocomplete="new-password" type="password" name="generic_password" id="generic_password" ng-model="$parent.Password" placeholder="{{'Authentication password' | translate}}" />
</div>