diff --git a/src/Core/FileTime.Core.Abstraction/Models/FullName.cs b/src/Core/FileTime.Core.Abstraction/Models/FullName.cs
index 71a6fd5..11d0f09 100644
--- a/src/Core/FileTime.Core.Abstraction/Models/FullName.cs
+++ b/src/Core/FileTime.Core.Abstraction/Models/FullName.cs
@@ -22,6 +22,8 @@ public record FullName(string Path)
public string GetName()
=> Path.Split(Constants.SeparatorChar).Last();
- public FullName GetChild(string childName)
- => new FullName(Path + Constants.SeparatorChar + childName);
+ public FullName GetChild(string childName)
+ => new(Path + Constants.SeparatorChar + childName);
+
+ public override string ToString() => Path;
}
\ No newline at end of file
diff --git a/src/Core/FileTime.Core.Abstraction/Models/NativePath.cs b/src/Core/FileTime.Core.Abstraction/Models/NativePath.cs
index 3a1b7e7..d8c9310 100644
--- a/src/Core/FileTime.Core.Abstraction/Models/NativePath.cs
+++ b/src/Core/FileTime.Core.Abstraction/Models/NativePath.cs
@@ -1,3 +1,6 @@
namespace FileTime.Core.Models;
-public record NativePath(string Path);
\ No newline at end of file
+public record NativePath(string Path)
+{
+ public override string ToString() => Path;
+}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.Local/FileTime.Providers.Local.csproj b/src/Providers/FileTime.Providers.Local/FileTime.Providers.Local.csproj
index cec897a..6f8e695 100644
--- a/src/Providers/FileTime.Providers.Local/FileTime.Providers.Local.csproj
+++ b/src/Providers/FileTime.Providers.Local/FileTime.Providers.Local.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src/Providers/FileTime.Providers.Local/LocalItemCreator.cs b/src/Providers/FileTime.Providers.Local/LocalItemCreator.cs
index 21e4f6d..78dd70f 100644
--- a/src/Providers/FileTime.Providers.Local/LocalItemCreator.cs
+++ b/src/Providers/FileTime.Providers.Local/LocalItemCreator.cs
@@ -1,6 +1,7 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
using FileTime.Providers.LocalAdmin;
+using Microsoft.Extensions.Logging;
namespace FileTime.Providers.Local;
@@ -8,50 +9,75 @@ public class LocalItemCreator : ItemCreatorBase
{
private readonly IAdminContentAccessorFactory _adminContentAccessorFactory;
private readonly IAdminContentProvider _adminContentProvider;
+ private readonly ILogger _logger;
public LocalItemCreator(
IAdminContentAccessorFactory adminContentAccessorFactory,
- IAdminContentProvider adminContentProvider)
+ IAdminContentProvider adminContentProvider,
+ ILogger logger)
{
_adminContentAccessorFactory = adminContentAccessorFactory;
_adminContentProvider = adminContentProvider;
+ _logger = logger;
}
public override async Task CreateContainerAsync(ILocalContentProvider contentProvider, FullName fullName)
{
+ _logger.LogTrace("Start creating container {FullName}", fullName);
var path = contentProvider.GetNativePath(fullName).Path;
- if (Directory.Exists(path)) return;
+ if (Directory.Exists(path))
+ {
+ _logger.LogTrace("Container with path {Path} already exists", path);
+ return;
+ }
try
{
+ _logger.LogTrace("Trying to create container with path {Path}", path);
Directory.CreateDirectory(path);
}
- catch (UnauthorizedAccessException)
+ catch (UnauthorizedAccessException e)
{
- if (!_adminContentAccessorFactory.IsAdminModeSupported) throw;
+ _logger.LogDebug(e, "Failed to create container with path {Path}", path);
+ if (!_adminContentAccessorFactory.IsAdminModeSupported)
+ {
+ _logger.LogTrace("Admin mode is disabled, not trying to create {Path} as admin", path);
+ throw;
+ }
- var adminContentAccessor = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync();
- await adminContentAccessor.CreateContainerAsync(_adminContentProvider, fullName);
+ var adminItemCreator = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync();
+ await adminItemCreator.CreateContainerAsync(_adminContentProvider, fullName);
}
}
public override async Task CreateElementAsync(ILocalContentProvider contentProvider, FullName fullName)
{
+ _logger.LogTrace("Start creating element {FullName}", fullName);
var path = contentProvider.GetNativePath(fullName).Path;
- if (File.Exists(path)) return;
-
+ if (File.Exists(path))
+ {
+ _logger.LogTrace("Element with path {Path} already exists", path);
+ return;
+ }
+
try
{
+ _logger.LogTrace("Trying to create element with path {Path}", path);
await using (File.Create(path))
{
}
}
- catch (UnauthorizedAccessException)
+ catch (UnauthorizedAccessException e)
{
- if (!_adminContentAccessorFactory.IsAdminModeSupported) throw;
+ _logger.LogDebug(e, "Failed to create element with path {Path}", path);
+ if (!_adminContentAccessorFactory.IsAdminModeSupported)
+ {
+ _logger.LogTrace("Admin mode is disabled, not trying to create {Path} as admin", path);
+ throw;
+ }
- var adminContentAccessor = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync();
- await adminContentAccessor.CreateElementAsync(_adminContentProvider, fullName);
+ var adminItemCreator = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync();
+ await adminItemCreator.CreateElementAsync(_adminContentProvider, fullName);
}
}
}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.Local/LocalItemDeleter.cs b/src/Providers/FileTime.Providers.Local/LocalItemDeleter.cs
index d2e18a7..9bbd39a 100644
--- a/src/Providers/FileTime.Providers.Local/LocalItemDeleter.cs
+++ b/src/Providers/FileTime.Providers.Local/LocalItemDeleter.cs
@@ -1,27 +1,65 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
+using FileTime.Providers.LocalAdmin;
+using Microsoft.Extensions.Logging;
namespace FileTime.Providers.Local;
public class LocalItemDeleter : IItemDeleter
{
- public Task DeleteAsync(ILocalContentProvider contentProvider, FullName fullName)
+ private readonly IAdminContentAccessorFactory _adminContentAccessorFactory;
+ private readonly IAdminContentProvider _adminContentProvider;
+ private readonly ILogger _logger;
+
+ public LocalItemDeleter(
+ IAdminContentAccessorFactory adminContentAccessorFactory,
+ IAdminContentProvider adminContentProvider,
+ ILogger logger)
{
+ _adminContentAccessorFactory = adminContentAccessorFactory;
+ _adminContentProvider = adminContentProvider;
+ _logger = logger;
+ }
+
+ public async Task DeleteAsync(ILocalContentProvider contentProvider, FullName fullName)
+ {
+ _logger.LogTrace("Start deleting item {FullName}", fullName);
var nativePath = contentProvider.GetNativePath(fullName).Path;
- if (File.Exists(nativePath))
+ try
{
- File.Delete(nativePath);
+ if (File.Exists(nativePath))
+ {
+ _logger.LogTrace("File exists with path {Path}", nativePath);
+ File.Delete(nativePath);
+ }
+ else if (Directory.Exists(nativePath))
+ {
+ _logger.LogTrace("Directory exists with path {Path}", nativePath);
+ Directory.Delete(nativePath, true);
+ }
+ else
+ {
+ _logger.LogTrace("No file or directory exists with path {Path}", nativePath);
+ throw new FileNotFoundException(nativePath);
+ }
}
- else if (Directory.Exists(nativePath))
+ catch (Exception e)
{
- Directory.Delete(nativePath, true);
- }
- else
- {
- throw new FileNotFoundException(nativePath);
- }
+ _logger.LogDebug(e, "Failed to delete item with path {Path}", nativePath);
- return Task.CompletedTask;
+ if (!_adminContentAccessorFactory.IsAdminModeSupported
+ || e is not UnauthorizedAccessException and not IOException)
+ {
+ _logger.LogTrace(
+ "Admin mode is disabled or exception is not an access denied one, not trying to create {Path} as admin",
+ nativePath
+ );
+ throw;
+ }
+
+ var adminItemDeleter = await _adminContentAccessorFactory.CreateAdminItemDeleterAsync();
+ await adminItemDeleter.DeleteAsync(_adminContentProvider, fullName);
+ }
}
}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/IAdminContentAccessorFactory.cs b/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/IAdminContentAccessorFactory.cs
index b124332..ec975a9 100644
--- a/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/IAdminContentAccessorFactory.cs
+++ b/src/Providers/FileTime.Providers.LocalAdmin.Abstractions/IAdminContentAccessorFactory.cs
@@ -6,4 +6,5 @@ public interface IAdminContentAccessorFactory
{
bool IsAdminModeSupported { get; }
Task CreateAdminItemCreatorAsync();
+ Task CreateAdminItemDeleterAsync();
}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.LocalAdmin/AdminContentAccessorFactory.cs b/src/Providers/FileTime.Providers.LocalAdmin/AdminContentAccessorFactory.cs
index 7d7c32c..52ad992 100644
--- a/src/Providers/FileTime.Providers.LocalAdmin/AdminContentAccessorFactory.cs
+++ b/src/Providers/FileTime.Providers.LocalAdmin/AdminContentAccessorFactory.cs
@@ -33,4 +33,18 @@ public class AdminContentAccessorFactory : IAdminContentAccessorFactory
.GetRequiredService();
return adminItemCreator;
}
+
+ public async Task CreateAdminItemDeleterAsync()
+ {
+ await _adminElevationManager.CreateAdminInstanceIfNecessaryAsync();
+ var connection = await _adminElevationManager.CreateConnectionAsync();
+
+ Debug.Assert(connection != null);
+
+ var adminItemDeleter = _serviceProvider.GetInitableResolver(
+ connection,
+ _adminElevationManager.ProviderName)
+ .GetRequiredService();
+ return adminItemDeleter;
+ }
}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteItemDeleter.cs b/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteItemDeleter.cs
new file mode 100644
index 0000000..f6518bd
--- /dev/null
+++ b/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteItemDeleter.cs
@@ -0,0 +1,10 @@
+using FileTime.Core.ContentAccess;
+using FileTime.Server.Common;
+using InitableService;
+
+namespace FileTime.Providers.Remote;
+
+public interface IRemoteItemDeleter : IItemDeleter, IInitable
+{
+
+}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.Remote/RemoteItemDeleter.cs b/src/Providers/FileTime.Providers.Remote/RemoteItemDeleter.cs
new file mode 100644
index 0000000..cd91e4c
--- /dev/null
+++ b/src/Providers/FileTime.Providers.Remote/RemoteItemDeleter.cs
@@ -0,0 +1,17 @@
+using FileTime.Core.Models;
+using FileTime.Server.Common;
+
+namespace FileTime.Providers.Remote;
+
+public class RemoteItemDeleter : IRemoteItemDeleter
+{
+ private IRemoteConnection _remoteConnection = null!;
+ private string _remoteContentProviderId = null!;
+ public void Init(IRemoteConnection remoteConnection, string remoteContentProviderId)
+ {
+ _remoteConnection = remoteConnection;
+ _remoteContentProviderId = remoteContentProviderId;
+ }
+ public async Task DeleteAsync(IRemoteContentProvider contentProvider, FullName fullName)
+ => await _remoteConnection.DeleteItemAsync(_remoteContentProviderId, fullName);
+}
\ No newline at end of file
diff --git a/src/Providers/FileTime.Providers.Remote/Startup.cs b/src/Providers/FileTime.Providers.Remote/Startup.cs
index 59a975e..fe2cf4d 100644
--- a/src/Providers/FileTime.Providers.Remote/Startup.cs
+++ b/src/Providers/FileTime.Providers.Remote/Startup.cs
@@ -10,6 +10,7 @@ public static class Startup
{
serviceCollection.TryAddSingleton();
serviceCollection.TryAddTransient();
+ serviceCollection.TryAddTransient();
return serviceCollection;
}
diff --git a/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs b/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs
index ee3e79e..52388cc 100644
--- a/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs
+++ b/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs
@@ -7,4 +7,5 @@ public interface IRemoteConnection
Task Exit();
Task CreateContainerAsync(string contentProviderId, FullName fullName);
Task CreateElementAsync(string contentProviderId, FullName fullName);
+ Task DeleteItemAsync(string contentProviderId, FullName fullName);
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs b/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs
index 7487070..94f90ee 100644
--- a/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs
+++ b/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs
@@ -5,4 +5,5 @@ public interface ISignalRHub
Task Exit();
Task CreateContainerAsync(string contentProviderId, string fullName);
Task CreateElementAsync(string contentProviderId, string fullName);
+ Task DeleteItemAsync(string contentProviderId, string fullName);
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs b/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs
index 0993da5..b47f447 100644
--- a/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs
+++ b/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs
@@ -39,4 +39,10 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable
var client = CreateClient();
await client.CreateElementAsync(contentProviderId, fullName.Path);
}
+
+ public async Task DeleteItemAsync(string contentProviderId, FullName fullName)
+ {
+ var client = CreateClient();
+ await client.DeleteItemAsync(contentProviderId, fullName.Path);
+ }
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server.Web/ConnectionHub.cs b/src/Server/FileTime.Server.Web/ConnectionHub.cs
index 99b8032..2ad2613 100644
--- a/src/Server/FileTime.Server.Web/ConnectionHub.cs
+++ b/src/Server/FileTime.Server.Web/ConnectionHub.cs
@@ -42,4 +42,11 @@ public class ConnectionHub : Hub, ISignalRHub
var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider);
await itemCreator.CreateElementAsync(contentProvider, new FullName(fullName));
}
+
+ public async Task DeleteItemAsync(string contentProviderId, string fullName)
+ {
+ var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId);
+ var itemDeleter = _contentAccessorFactory.GetItemDeleter(contentProvider);
+ await itemDeleter.DeleteAsync(contentProvider, new FullName(fullName));
+ }
}
\ No newline at end of file
diff --git a/src/Server/FileTime.Server/Program.cs b/src/Server/FileTime.Server/Program.cs
index f64e150..07c4fee 100644
--- a/src/Server/FileTime.Server/Program.cs
+++ b/src/Server/FileTime.Server/Program.cs
@@ -5,6 +5,7 @@ using FileTime.Providers.Local;
using FileTime.Server.App;
using FileTime.Server.Common;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Serilog;
@@ -52,7 +53,8 @@ IContainer CreateRootDiContainer(IConfigurationRoot configuration)
var serviceCollection = DependencyInjection
.RegisterDefaultServices(configuration)
.AddLocalProviderServices()
- .AddServerServices();
+ .AddServerServices()
+ .AddLogging(loggingBuilder => loggingBuilder.AddSerilog());
serviceCollection.TryAddSingleton(
new ApplicationStopper(() => applicationCancellation.Cancel())