Admin ItemDeleter, Logging
This commit is contained in:
@@ -22,6 +22,8 @@ public record FullName(string Path)
|
|||||||
public string GetName()
|
public string GetName()
|
||||||
=> Path.Split(Constants.SeparatorChar).Last();
|
=> Path.Split(Constants.SeparatorChar).Last();
|
||||||
|
|
||||||
public FullName GetChild(string childName)
|
public FullName GetChild(string childName)
|
||||||
=> new FullName(Path + Constants.SeparatorChar + childName);
|
=> new(Path + Constants.SeparatorChar + childName);
|
||||||
|
|
||||||
|
public override string ToString() => Path;
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
namespace FileTime.Core.Models;
|
namespace FileTime.Core.Models;
|
||||||
|
|
||||||
public record NativePath(string Path);
|
public record NativePath(string Path)
|
||||||
|
{
|
||||||
|
public override string ToString() => Path;
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DynamicData" Version="7.14.2" />
|
<PackageReference Include="DynamicData" Version="7.14.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using FileTime.Core.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Providers.LocalAdmin;
|
using FileTime.Providers.LocalAdmin;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace FileTime.Providers.Local;
|
namespace FileTime.Providers.Local;
|
||||||
|
|
||||||
@@ -8,50 +9,75 @@ public class LocalItemCreator : ItemCreatorBase<ILocalContentProvider>
|
|||||||
{
|
{
|
||||||
private readonly IAdminContentAccessorFactory _adminContentAccessorFactory;
|
private readonly IAdminContentAccessorFactory _adminContentAccessorFactory;
|
||||||
private readonly IAdminContentProvider _adminContentProvider;
|
private readonly IAdminContentProvider _adminContentProvider;
|
||||||
|
private readonly ILogger<LocalItemCreator> _logger;
|
||||||
|
|
||||||
public LocalItemCreator(
|
public LocalItemCreator(
|
||||||
IAdminContentAccessorFactory adminContentAccessorFactory,
|
IAdminContentAccessorFactory adminContentAccessorFactory,
|
||||||
IAdminContentProvider adminContentProvider)
|
IAdminContentProvider adminContentProvider,
|
||||||
|
ILogger<LocalItemCreator> logger)
|
||||||
{
|
{
|
||||||
_adminContentAccessorFactory = adminContentAccessorFactory;
|
_adminContentAccessorFactory = adminContentAccessorFactory;
|
||||||
_adminContentProvider = adminContentProvider;
|
_adminContentProvider = adminContentProvider;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task CreateContainerAsync(ILocalContentProvider contentProvider, FullName fullName)
|
public override async Task CreateContainerAsync(ILocalContentProvider contentProvider, FullName fullName)
|
||||||
{
|
{
|
||||||
|
_logger.LogTrace("Start creating container {FullName}", fullName);
|
||||||
var path = contentProvider.GetNativePath(fullName).Path;
|
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
|
try
|
||||||
{
|
{
|
||||||
|
_logger.LogTrace("Trying to create container with path {Path}", path);
|
||||||
Directory.CreateDirectory(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();
|
var adminItemCreator = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync();
|
||||||
await adminContentAccessor.CreateContainerAsync(_adminContentProvider, fullName);
|
await adminItemCreator.CreateContainerAsync(_adminContentProvider, fullName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task CreateElementAsync(ILocalContentProvider contentProvider, FullName fullName)
|
public override async Task CreateElementAsync(ILocalContentProvider contentProvider, FullName fullName)
|
||||||
{
|
{
|
||||||
|
_logger.LogTrace("Start creating element {FullName}", fullName);
|
||||||
var path = contentProvider.GetNativePath(fullName).Path;
|
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
|
try
|
||||||
{
|
{
|
||||||
|
_logger.LogTrace("Trying to create element with path {Path}", path);
|
||||||
await using (File.Create(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();
|
var adminItemCreator = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync();
|
||||||
await adminContentAccessor.CreateElementAsync(_adminContentProvider, fullName);
|
await adminItemCreator.CreateElementAsync(_adminContentProvider, fullName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,27 +1,65 @@
|
|||||||
using FileTime.Core.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Providers.LocalAdmin;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace FileTime.Providers.Local;
|
namespace FileTime.Providers.Local;
|
||||||
|
|
||||||
public class LocalItemDeleter : IItemDeleter<ILocalContentProvider>
|
public class LocalItemDeleter : IItemDeleter<ILocalContentProvider>
|
||||||
{
|
{
|
||||||
public Task DeleteAsync(ILocalContentProvider contentProvider, FullName fullName)
|
private readonly IAdminContentAccessorFactory _adminContentAccessorFactory;
|
||||||
|
private readonly IAdminContentProvider _adminContentProvider;
|
||||||
|
private readonly ILogger<LocalItemDeleter> _logger;
|
||||||
|
|
||||||
|
public LocalItemDeleter(
|
||||||
|
IAdminContentAccessorFactory adminContentAccessorFactory,
|
||||||
|
IAdminContentProvider adminContentProvider,
|
||||||
|
ILogger<LocalItemDeleter> 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;
|
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);
|
_logger.LogDebug(e, "Failed to delete item with path {Path}", nativePath);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new FileNotFoundException(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,4 +6,5 @@ public interface IAdminContentAccessorFactory
|
|||||||
{
|
{
|
||||||
bool IsAdminModeSupported { get; }
|
bool IsAdminModeSupported { get; }
|
||||||
Task<IRemoteItemCreator> CreateAdminItemCreatorAsync();
|
Task<IRemoteItemCreator> CreateAdminItemCreatorAsync();
|
||||||
|
Task<IRemoteItemDeleter> CreateAdminItemDeleterAsync();
|
||||||
}
|
}
|
||||||
@@ -33,4 +33,18 @@ public class AdminContentAccessorFactory : IAdminContentAccessorFactory
|
|||||||
.GetRequiredService<IRemoteItemCreator>();
|
.GetRequiredService<IRemoteItemCreator>();
|
||||||
return adminItemCreator;
|
return adminItemCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IRemoteItemDeleter> CreateAdminItemDeleterAsync()
|
||||||
|
{
|
||||||
|
await _adminElevationManager.CreateAdminInstanceIfNecessaryAsync();
|
||||||
|
var connection = await _adminElevationManager.CreateConnectionAsync();
|
||||||
|
|
||||||
|
Debug.Assert(connection != null);
|
||||||
|
|
||||||
|
var adminItemDeleter = _serviceProvider.GetInitableResolver(
|
||||||
|
connection,
|
||||||
|
_adminElevationManager.ProviderName)
|
||||||
|
.GetRequiredService<IRemoteItemDeleter>();
|
||||||
|
return adminItemDeleter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Server.Common;
|
||||||
|
using InitableService;
|
||||||
|
|
||||||
|
namespace FileTime.Providers.Remote;
|
||||||
|
|
||||||
|
public interface IRemoteItemDeleter : IItemDeleter<IRemoteContentProvider>, IInitable<IRemoteConnection, string>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
17
src/Providers/FileTime.Providers.Remote/RemoteItemDeleter.cs
Normal file
17
src/Providers/FileTime.Providers.Remote/RemoteItemDeleter.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ public static class Startup
|
|||||||
{
|
{
|
||||||
serviceCollection.TryAddSingleton<IRemoteContentProvider, RemoteContentProvider>();
|
serviceCollection.TryAddSingleton<IRemoteContentProvider, RemoteContentProvider>();
|
||||||
serviceCollection.TryAddTransient<IRemoteItemCreator, RemoteItemCreator>();
|
serviceCollection.TryAddTransient<IRemoteItemCreator, RemoteItemCreator>();
|
||||||
|
serviceCollection.TryAddTransient<IRemoteItemDeleter, RemoteItemDeleter>();
|
||||||
return serviceCollection;
|
return serviceCollection;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ public interface IRemoteConnection
|
|||||||
Task Exit();
|
Task Exit();
|
||||||
Task CreateContainerAsync(string contentProviderId, FullName fullName);
|
Task CreateContainerAsync(string contentProviderId, FullName fullName);
|
||||||
Task CreateElementAsync(string contentProviderId, FullName fullName);
|
Task CreateElementAsync(string contentProviderId, FullName fullName);
|
||||||
|
Task DeleteItemAsync(string contentProviderId, FullName fullName);
|
||||||
}
|
}
|
||||||
@@ -5,4 +5,5 @@ public interface ISignalRHub
|
|||||||
Task Exit();
|
Task Exit();
|
||||||
Task CreateContainerAsync(string contentProviderId, string fullName);
|
Task CreateContainerAsync(string contentProviderId, string fullName);
|
||||||
Task CreateElementAsync(string contentProviderId, string fullName);
|
Task CreateElementAsync(string contentProviderId, string fullName);
|
||||||
|
Task DeleteItemAsync(string contentProviderId, string fullName);
|
||||||
}
|
}
|
||||||
@@ -39,4 +39,10 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
|
|||||||
var client = CreateClient();
|
var client = CreateClient();
|
||||||
await client.CreateElementAsync(contentProviderId, fullName.Path);
|
await client.CreateElementAsync(contentProviderId, fullName.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteItemAsync(string contentProviderId, FullName fullName)
|
||||||
|
{
|
||||||
|
var client = CreateClient();
|
||||||
|
await client.DeleteItemAsync(contentProviderId, fullName.Path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -42,4 +42,11 @@ public class ConnectionHub : Hub<ISignalRClient>, ISignalRHub
|
|||||||
var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider);
|
var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider);
|
||||||
await itemCreator.CreateElementAsync(contentProvider, new FullName(fullName));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using FileTime.Providers.Local;
|
|||||||
using FileTime.Server.App;
|
using FileTime.Server.App;
|
||||||
using FileTime.Server.Common;
|
using FileTime.Server.Common;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
@@ -52,7 +53,8 @@ IContainer CreateRootDiContainer(IConfigurationRoot configuration)
|
|||||||
var serviceCollection = DependencyInjection
|
var serviceCollection = DependencyInjection
|
||||||
.RegisterDefaultServices(configuration)
|
.RegisterDefaultServices(configuration)
|
||||||
.AddLocalProviderServices()
|
.AddLocalProviderServices()
|
||||||
.AddServerServices();
|
.AddServerServices()
|
||||||
|
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog());
|
||||||
|
|
||||||
serviceCollection.TryAddSingleton<IApplicationStopper>(
|
serviceCollection.TryAddSingleton<IApplicationStopper>(
|
||||||
new ApplicationStopper(() => applicationCancellation.Cancel())
|
new ApplicationStopper(() => applicationCancellation.Cancel())
|
||||||
|
|||||||
Reference in New Issue
Block a user