RemoteContentProvider get items WIP

This commit is contained in:
2023-08-28 09:29:52 +02:00
parent 79971fe0f4
commit bb44ca0308
49 changed files with 765 additions and 118 deletions

View File

@@ -28,6 +28,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
private readonly IContainerSizeScanProvider _containerSizeScanProvider; private readonly IContainerSizeScanProvider _containerSizeScanProvider;
private readonly IProgramsService _programsService; private readonly IProgramsService _programsService;
private readonly IContentProviderRegistry _contentProviderRegistry; private readonly IContentProviderRegistry _contentProviderRegistry;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ToolUserCommandHandlerService> _logger; private readonly ILogger<ToolUserCommandHandlerService> _logger;
private IDeclarativeProperty<IContainer?>? _currentLocation; private IDeclarativeProperty<IContainer?>? _currentLocation;
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem; private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
@@ -45,6 +46,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
IContainerSizeScanProvider containerSizeScanProvider, IContainerSizeScanProvider containerSizeScanProvider,
IProgramsService programsService, IProgramsService programsService,
IContentProviderRegistry contentProviderRegistry, IContentProviderRegistry contentProviderRegistry,
IServiceProvider serviceProvider,
ILogger<ToolUserCommandHandlerService> logger) : base(appState) ILogger<ToolUserCommandHandlerService> logger) : base(appState)
{ {
_systemClipboardService = systemClipboardService; _systemClipboardService = systemClipboardService;
@@ -57,6 +59,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
_containerSizeScanProvider = containerSizeScanProvider; _containerSizeScanProvider = containerSizeScanProvider;
_programsService = programsService; _programsService = programsService;
_contentProviderRegistry = contentProviderRegistry; _contentProviderRegistry = contentProviderRegistry;
_serviceProvider = serviceProvider;
_logger = logger; _logger = logger;
SaveCurrentLocation(l => _currentLocation = l); SaveCurrentLocation(l => _currentLocation = l);
SaveCurrentSelectedItem(i => _currentSelectedItem = i); SaveCurrentSelectedItem(i => _currentSelectedItem = i);
@@ -92,7 +95,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
Func<Task<IRemoteConnection>>? connection = null; Func<Task<IRemoteConnection>>? connection = null;
if (path.StartsWith("http")) if (path.StartsWith("http"))
{ {
connection = async () => await SignalRConnection.GetOrCreateForAsync(path); connection = async () => await SignalRConnection.GetOrCreateForAsync(path, providerName.Value);
} }
if (connection is null) if (connection is null)
@@ -109,15 +112,17 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
var remoteContentProvider = new RemoteContentProvider( var remoteContentProvider = new RemoteContentProvider(
_timelessContentProvider, _timelessContentProvider,
_serviceProvider,
connection, connection,
"local", "local",
providerName.Value); providerName.Value);
_contentProviderRegistry.AddContentProvider(remoteContentProvider); _contentProviderRegistry.AddContentProvider(remoteContentProvider);
await _userCommandHandlerService.HandleCommandAsync( await _userCommandHandlerService.HandleCommandAsync(
new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, remoteContentProvider))); new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, remoteContentProvider)));
await remoteContentProvider.InitializeChildren();
} }
private Task Edit() private Task Edit()

View File

@@ -13,7 +13,7 @@ namespace FileTime.App.Core;
public static class Startup public static class Startup
{ {
public static IServiceCollection AddCoreAppServices(this IServiceCollection serviceCollection, IConfigurationRoot configuration) public static IServiceCollection AddAppCoreDependencies(this IServiceCollection serviceCollection, IConfigurationRoot configuration)
{ {
serviceCollection.TryAddTransient<ITabViewModel, TabViewModel>(); serviceCollection.TryAddTransient<ITabViewModel, TabViewModel>();
serviceCollection.TryAddTransient<IContainerViewModel, ContainerViewModel>(); serviceCollection.TryAddTransient<IContainerViewModel, ContainerViewModel>();

View File

@@ -2,14 +2,7 @@ using FileTime.App.Core;
using FileTime.App.Core.Models; using FileTime.App.Core.Models;
using FileTime.App.Core.Services; using FileTime.App.Core.Services;
using FileTime.App.Core.Services.Persistence; using FileTime.App.Core.Services.Persistence;
using FileTime.Core.Command; using FileTime.Core;
using FileTime.Core.Command.CreateContainer;
using FileTime.Core.Command.CreateElement;
using FileTime.Core.Command.Delete;
using FileTime.Core.CommandHandlers;
using FileTime.Core.ContentAccess;
using FileTime.Core.Services;
using FileTime.Core.Timeline;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using FileTime.Providers.LocalAdmin; using FileTime.Providers.LocalAdmin;
using FileTime.Providers.Remote; using FileTime.Providers.Remote;
@@ -25,36 +18,16 @@ public static class DependencyInjection
{ {
serviceCollection ??= new ServiceCollection(); serviceCollection ??= new ServiceCollection();
serviceCollection.TryAddSingleton<ICommandScheduler, CommandScheduler>();
serviceCollection.TryAddSingleton<ITimelessContentProvider, TimelessContentProvider>();
serviceCollection.TryAddSingleton<ICommandRunner, CommandRunner>();
serviceCollection.TryAddSingleton<IContentAccessorFactory, ContentAccessorFactory>();
serviceCollection.TryAddSingleton<IContentProviderRegistry, ContentProviderRegistry>();
serviceCollection.TryAddSingleton<IRootContentProvider, RootContentProvider>();
//TODO: check local/remote context
serviceCollection.TryAddSingleton<ILocalCommandExecutor, LocalCommandExecutor>();
serviceCollection.TryAddSingleton<ICommandSchedulerNotifier, LocalCommandSchedulerNotifier>();
serviceCollection.TryAddSingleton<IApplicationSettings, ApplicationSettings>(); serviceCollection.TryAddSingleton<IApplicationSettings, ApplicationSettings>();
serviceCollection.TryAddSingleton<ITabPersistenceService, TabPersistenceService>(); serviceCollection.TryAddSingleton<ITabPersistenceService, TabPersistenceService>();
serviceCollection.TryAddTransient<ITab, Tab>();
serviceCollection.TryAddSingleton<ITabEvents, TabEvents>();
serviceCollection.AddSingleton<IExitHandler, ITabPersistenceService>(sp => sp.GetRequiredService<ITabPersistenceService>()); serviceCollection.AddSingleton<IExitHandler, ITabPersistenceService>(sp => sp.GetRequiredService<ITabPersistenceService>());
serviceCollection.AddSingleton<IStartupHandler, ITabPersistenceService>(sp => sp.GetRequiredService<ITabPersistenceService>()); serviceCollection.AddSingleton<IStartupHandler, ITabPersistenceService>(sp => sp.GetRequiredService<ITabPersistenceService>());
return serviceCollection return serviceCollection
.AddCoreAppServices(configuration) .AddCoreDependencies()
.AddAppCoreDependencies(configuration)
.AddLocalProviderServices() .AddLocalProviderServices()
.AddLocalAdminProviderServices(configuration) .AddLocalAdminProviderServices(configuration)
.AddRemoteProviderServices() .AddRemoteProviderServices();
.RegisterCommands()
.AddDefaultCommandHandlers();
} }
private static IServiceCollection RegisterCommands(this IServiceCollection serviceCollection)
=> serviceCollection
.AddCommands()
.AddTransient<CreateContainerCommand>()
.AddTransient<CreateElementCommand>()
.AddTransient<DeleteCommand>();
} }

View File

@@ -15,6 +15,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Core\FileTime.Core.CommandHandlers\FileTime.Core.CommandHandlers.csproj" /> <ProjectReference Include="..\..\Core\FileTime.Core.CommandHandlers\FileTime.Core.CommandHandlers.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Extensions.DependencyInjection\FileTime.Core.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Timeline\FileTime.Core.Timeline.csproj" /> <ProjectReference Include="..\..\Core\FileTime.Core.Timeline\FileTime.Core.Timeline.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.LocalAdmin\FileTime.Providers.LocalAdmin.csproj" /> <ProjectReference Include="..\..\Providers\FileTime.Providers.LocalAdmin\FileTime.Providers.LocalAdmin.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" /> <ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" />

View File

@@ -6,7 +6,7 @@ using FileTime.App.Search;
using FileTime.ConsoleUI.App; using FileTime.ConsoleUI.App;
using FileTime.ConsoleUI.App.Services; using FileTime.ConsoleUI.App.Services;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using FileTime.Server.Common; using FileTime.Server;
using FileTime.Tools.Compression; using FileTime.Tools.Compression;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;

View File

@@ -17,6 +17,7 @@
<ProjectReference Include="..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" /> <ProjectReference Include="..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" />
<ProjectReference Include="..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" /> <ProjectReference Include="..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" />
<ProjectReference Include="..\..\Library\TerminalUI.DependencyInjection\TerminalUI.DependencyInjection.csproj" /> <ProjectReference Include="..\..\Library\TerminalUI.DependencyInjection\TerminalUI.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" /> <ProjectReference Include="..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" />
<ProjectReference Include="..\FileTime.ConsoleUI.App\FileTime.ConsoleUI.App.csproj" /> <ProjectReference Include="..\FileTime.ConsoleUI.App\FileTime.ConsoleUI.App.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -25,4 +25,5 @@
<ProjectReference Include="..\..\Library\InitableService\InitableService.csproj" /> <ProjectReference Include="..\..\Library\InitableService\InitableService.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -2,8 +2,8 @@ namespace FileTime.Core.Timeline;
public class PointInTime public class PointInTime
{ {
public static readonly PointInTime Eternal = new PointInTime(); public static readonly PointInTime Eternal = new();
public static readonly PointInTime Present = new PointInTime(); public static readonly PointInTime Present = new();
private readonly List<Difference> _differences; private readonly List<Difference> _differences;
@@ -46,5 +46,5 @@ public class PointInTime
return merged; return merged;
} }
public static PointInTime CreateEmpty() => new PointInTime(); public static PointInTime CreateEmpty() => new();
} }

View File

@@ -1,13 +0,0 @@
using FileTime.Core.Command.Copy;
using FileTime.Core.Command.Move;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Core.Command;
public static class Startup
{
public static IServiceCollection AddCommands(this IServiceCollection serviceCollection) =>
serviceCollection
.AddSingleton<CopyCommandFactory>()
.AddSingleton<MoveCommandFactory>();
}

View File

@@ -1,13 +0,0 @@
using FileTime.Core.Command;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Core.CommandHandlers;
public static class Startup
{
public static IServiceCollection AddDefaultCommandHandlers(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddSingleton<ICommandHandler, StreamCopyCommandHandler>();
}
}

View File

@@ -28,6 +28,7 @@ public class ContentProviderRegistry : IContentProviderRegistry
private void InitializeContentProviderListIfNeeded() private void InitializeContentProviderListIfNeeded()
{ {
if (_initialized) return;
lock (_lock) lock (_lock)
{ {
if (!_initialized) if (!_initialized)

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>FileTime.Core</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
<ProjectReference Include="..\FileTime.Core.CommandHandlers\FileTime.Core.CommandHandlers.csproj" />
<ProjectReference Include="..\FileTime.Core.Command\FileTime.Core.Command.csproj" />
<ProjectReference Include="..\FileTime.Core.ContentAccess\FileTime.Core.ContentAccess.csproj" />
<ProjectReference Include="..\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj" />
<ProjectReference Include="..\FileTime.Core.Services\FileTime.Core.Services.csproj" />
<ProjectReference Include="..\FileTime.Core.Timeline\FileTime.Core.Timeline.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,88 @@
using FileTime.Core.Command;
using FileTime.Core.Command.Copy;
using FileTime.Core.Command.CreateContainer;
using FileTime.Core.Command.CreateElement;
using FileTime.Core.Command.Delete;
using FileTime.Core.Command.Move;
using FileTime.Core.CommandHandlers;
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Serialization.Container;
using FileTime.Core.Services;
using FileTime.Core.Timeline;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace FileTime.Core;
public static class Startup
{
public static IServiceCollection AddCoreDependencies(this IServiceCollection serviceCollection)
=> serviceCollection
.AddCoreServices()
.AddTimelineServices()
.AddDefaultCommandHandlers()
.AddCommands()
.AddCommandFactories()
.AddCommandServices()
.AddContentAccessServices()
.AddSerialization();
private static IServiceCollection AddContentAccessServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<IContentAccessorFactory, ContentAccessorFactory>();
serviceCollection.TryAddSingleton<IContentProviderRegistry, ContentProviderRegistry>();
serviceCollection.TryAddSingleton<IRootContentProvider, RootContentProvider>();
return serviceCollection;
}
private static IServiceCollection AddCommandServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<ICommandRunner, CommandRunner>();
return serviceCollection;
}
private static IServiceCollection AddCoreServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddTransient<ITab, Tab>();
serviceCollection.TryAddSingleton<ITabEvents, TabEvents>();
return serviceCollection;
}
private static IServiceCollection AddTimelineServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<ICommandScheduler, CommandScheduler>();
serviceCollection.TryAddSingleton<ITimelessContentProvider, TimelessContentProvider>();
//TODO: check local/remote context
serviceCollection.TryAddSingleton<ILocalCommandExecutor, LocalCommandExecutor>();
serviceCollection.TryAddSingleton<ICommandSchedulerNotifier, LocalCommandSchedulerNotifier>();
return serviceCollection;
}
private static IServiceCollection AddCommands(this IServiceCollection serviceCollection)
=> serviceCollection
.AddTransient<CreateContainerCommand>()
.AddTransient<CreateElementCommand>()
.AddTransient<DeleteCommand>();
private static IServiceCollection AddCommandFactories(this IServiceCollection serviceCollection) =>
serviceCollection
.AddSingleton<CopyCommandFactory>()
.AddSingleton<MoveCommandFactory>();
private static IServiceCollection AddSerialization(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<ISerializer<IContainer>, ContainerSerializer>();
return serviceCollection;
}
private static IServiceCollection AddDefaultCommandHandlers(this IServiceCollection serviceCollection)
=> serviceCollection
.AddSingleton<ICommandHandler, StreamCopyCommandHandler>();
}

View File

@@ -0,0 +1,14 @@
using FileTime.Core.Models;
namespace FileTime.Core.Serialization;
public class AbsolutePathSerializer
{
public static SerializedAbsolutePath Serialize(AbsolutePath absolutePath)
=> new()
{
PointInTime = absolutePath.PointInTime,
Path = absolutePath.Path.Path,
Type = absolutePath.Type
};
}

View File

@@ -0,0 +1,5 @@
using FileTime.Core.ContentAccess;
namespace FileTime.Core.Serialization.Container;
public record ContainerDeserializationContext(IContentProvider ContentProvider);

View File

@@ -0,0 +1,10 @@
using System.Collections.ObjectModel;
using FileTime.Core.Models;
namespace FileTime.Core.Serialization.Container;
public record ContainerDeserializationResult(
Models.Container Container,
ObservableCollection<Exception> Exceptions,
ExtensionCollection Extensions,
ObservableCollection<AbsolutePath> Items);

View File

@@ -0,0 +1,67 @@
using System.Collections.ObjectModel;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Serialization.Container;
public class ContainerDeserializer
{
private readonly ITimelessContentProvider _timelessContentProvider;
public ContainerDeserializer(ITimelessContentProvider timelessContentProvider)
{
_timelessContentProvider = timelessContentProvider;
}
public ContainerDeserializationResult Deserialize(
SerializedContainer source,
ContainerDeserializationContext context)
{
ObservableCollection<Exception> exceptions = new();
ExtensionCollection extensions = new();
ObservableCollection<AbsolutePath> items = new();
var mappedItems = source.Items
.Select(x => new AbsolutePath(
_timelessContentProvider,
x.PointInTime,
new FullName(x.Path),
x.Type
)
);
foreach (var item in
mappedItems)
{
items.Add(item);
}
var container = new Models.Container(
source.Name,
source.DisplayName,
new FullName(source.FullName),
new NativePath(source.NativePath),
new AbsolutePath(_timelessContentProvider, PointInTime.Present, new FullName(source.Parent), AbsolutePathType.Container),
source.IsHidden,
source.IsExists,
source.CreatedAt,
source.ModifiedAt,
source.CanDelete,
source.CanRename,
source.Attributes,
context.ContentProvider,
source.AllowRecursiveDeletion,
PointInTime.Present,
exceptions,
new ReadOnlyExtensionCollection(extensions),
items
);
return new ContainerDeserializationResult(
container,
exceptions,
extensions,
items);
}
}

View File

@@ -0,0 +1,32 @@
using FileTime.Core.Models;
namespace FileTime.Core.Serialization.Container;
public class ContainerSerializer : ISerializer<IContainer>
{
Task<ISerialized> ISerializer<IContainer>.SerializeAsync(int id, IContainer item) => Task.FromResult(Serialize(id, item));
private ISerialized Serialize(int id, IContainer container)
{
var items = container.Items.Select(AbsolutePathSerializer.Serialize).ToArray();
var serialized = new SerializedContainer
{
Id = id,
Name = container.Name,
DisplayName = container.DisplayName,
FullName = container.FullName!.Path,
NativePath = container.NativePath!.Path,
Parent = container.Parent!.Path.Path,
IsHidden = container.IsHidden,
IsExists = container.IsExists,
CreatedAt = container.CreatedAt,
ModifiedAt = container.ModifiedAt,
CanDelete = container.CanDelete,
CanRename = container.CanRename,
Attributes = container.Attributes,
AllowRecursiveDeletion = container.AllowRecursiveDeletion,
Items = items
};
return serialized;
}
}

View File

@@ -0,0 +1,29 @@
using System.Runtime.Serialization;
using FileTime.Core.Enums;
using MessagePack;
namespace FileTime.Core.Serialization.Container;
[DataContract]
[MessagePackObject]
public class SerializedContainer : ISerialized
{
[Key(0)] [DataMember(Order = 0)] public required int Id { get; set; }
[Key(1)] [DataMember(Order = 1)] public required string Name { get; set; }
[Key(2)] [DataMember(Order = 2)] public required string DisplayName { get; set; }
[Key(3)] [DataMember(Order = 3)] public required string FullName { get; set; }
[Key(4)] [DataMember(Order = 4)] public required string NativePath { get; set; }
[Key(5)] [DataMember(Order = 5)] public required string Parent { get; set; }
[Key(6)] [DataMember(Order = 6)] public required bool IsHidden { get; set; }
[Key(7)] [DataMember(Order = 7)] public required bool IsExists { get; set; }
[Key(8)] [DataMember(Order = 8)] public required DateTime? CreatedAt { get; set; }
[Key(9)] [DataMember(Order = 9)] public required DateTime? ModifiedAt { get; set; }
[Key(10)] [DataMember(Order = 10)] public required SupportsDelete CanDelete { get; set; }
[Key(11)] [DataMember(Order = 11)] public required bool CanRename { get; set; }
[Key(12)] [DataMember(Order = 12)] public required string? Attributes { get; set; }
[Key(13)] [DataMember(Order = 13)] public required bool AllowRecursiveDeletion { get; set; }
[Key(14)] [DataMember(Order = 14)] public required SerializedAbsolutePath[] Items { get; set; }
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
<ProjectReference Include="..\FileTime.Core.Models\FileTime.Core.Models.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MessagePack" Version="2.5.124" />
<PackageReference Include="MessagePackAnalyzer" Version="2.5.124">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,9 @@
using FileTime.Core.Serialization.Container;
namespace FileTime.Core.Serialization;
[MessagePack.Union(0, typeof(SerializedContainer))]
public interface ISerialized
{
int Id { get; }
}

View File

@@ -0,0 +1,14 @@
using FileTime.Core.Models;
namespace FileTime.Core.Serialization;
public interface ISerializer
{
Task<ISerialized> SerializeAsync(int id, object item);
}
public interface ISerializer<T> where T : IItem
{
async Task<ISerialized> SerializeAsync(int id, object item) => await SerializeAsync(id, (T) item);
Task<ISerialized> SerializeAsync(int id, T item);
}

View File

@@ -0,0 +1,15 @@
using System.Runtime.Serialization;
using FileTime.Core.Enums;
using FileTime.Core.Timeline;
using MessagePack;
namespace FileTime.Core.Serialization;
[MessagePackObject]
[DataContract]
public class SerializedAbsolutePath
{
[Key(0)] [DataMember(Order = 0)] public required PointInTime PointInTime { get; set; }
[Key(1)] [DataMember(Order = 1)] public required string Path { get; set; }
[Key(2)] [DataMember(Order = 2)] public required AbsolutePathType Type { get; set; }
}

View File

@@ -0,0 +1,11 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Serialization.Timeline;
public class PointInTimeSerializer
{
public static SerializedPointInTime Serialize(PointInTime pointInTime)
{
return new();
}
}

View File

@@ -0,0 +1,9 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Serialization.Timeline;
public class SerializedDifference
{
public required SerializedAbsolutePath AbsolutePath { get; set; }
public required DifferenceActionType Action { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace FileTime.Core.Serialization.Timeline;
public class SerializedPointInTime
{
}

View File

@@ -188,7 +188,17 @@ public class Tab : ITab
private static IItem MapItem(AbsolutePath item) private static IItem MapItem(AbsolutePath item)
{ {
var t = Task.Run(async () => await item.ResolveAsync(true)); var t = Task.Run(async () =>
{
try
{
return await item.ResolveAsync(true);
}
catch
{
return null;
}
});
t.Wait(); t.Wait();
return t.Result; return t.Result;
} }

View File

@@ -139,6 +139,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Providers.Smb", "P
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Providers.Smb.Abstractions", "Providers\FileTime.Providers.Smb.Abstractions\FileTime.Providers.Smb.Abstractions.csproj", "{595F736D-4711-4AD3-8D4F-13DD85FB35E7}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Providers.Smb.Abstractions", "Providers\FileTime.Providers.Smb.Abstractions\FileTime.Providers.Smb.Abstractions.csproj", "{595F736D-4711-4AD3-8D4F-13DD85FB35E7}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Core.Serialization", "Core\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj", "{0AAC0BDF-35F2-434C-882E-047415D55682}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Core.Extensions.DependencyInjection", "Core\FileTime.Core.Extensions.DependencyInjection\FileTime.Core.Extensions.DependencyInjection.csproj", "{0A29616B-5413-4DA9-96EC-B45D6AF632C9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Server.Tracker", "Server\FileTime.Server.Tracker\FileTime.Server.Tracker.csproj", "{BD382403-86D8-4E99-B279-370FC22F059C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Server.Extensions.DependencyInjection", "Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj", "{25AA9F04-EEEE-49C4-870B-CDFF71717687}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -381,6 +389,22 @@ Global
{595F736D-4711-4AD3-8D4F-13DD85FB35E7}.Debug|Any CPU.Build.0 = Debug|Any CPU {595F736D-4711-4AD3-8D4F-13DD85FB35E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{595F736D-4711-4AD3-8D4F-13DD85FB35E7}.Release|Any CPU.ActiveCfg = Release|Any CPU {595F736D-4711-4AD3-8D4F-13DD85FB35E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{595F736D-4711-4AD3-8D4F-13DD85FB35E7}.Release|Any CPU.Build.0 = Release|Any CPU {595F736D-4711-4AD3-8D4F-13DD85FB35E7}.Release|Any CPU.Build.0 = Release|Any CPU
{0AAC0BDF-35F2-434C-882E-047415D55682}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0AAC0BDF-35F2-434C-882E-047415D55682}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0AAC0BDF-35F2-434C-882E-047415D55682}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0AAC0BDF-35F2-434C-882E-047415D55682}.Release|Any CPU.Build.0 = Release|Any CPU
{0A29616B-5413-4DA9-96EC-B45D6AF632C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A29616B-5413-4DA9-96EC-B45D6AF632C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A29616B-5413-4DA9-96EC-B45D6AF632C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A29616B-5413-4DA9-96EC-B45D6AF632C9}.Release|Any CPU.Build.0 = Release|Any CPU
{BD382403-86D8-4E99-B279-370FC22F059C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD382403-86D8-4E99-B279-370FC22F059C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD382403-86D8-4E99-B279-370FC22F059C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD382403-86D8-4E99-B279-370FC22F059C}.Release|Any CPU.Build.0 = Release|Any CPU
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25AA9F04-EEEE-49C4-870B-CDFF71717687}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -446,6 +470,10 @@ Global
{CA48A181-256E-4546-B164-6768B240BFFC} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3} {CA48A181-256E-4546-B164-6768B240BFFC} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3}
{CC8AA3FC-7A5B-4558-AB35-DFF59F2AEF47} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3} {CC8AA3FC-7A5B-4558-AB35-DFF59F2AEF47} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3}
{595F736D-4711-4AD3-8D4F-13DD85FB35E7} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3} {595F736D-4711-4AD3-8D4F-13DD85FB35E7} = {2FC40FE1-4446-44AB-BF77-00F94D995FA3}
{0AAC0BDF-35F2-434C-882E-047415D55682} = {3324D046-1E05-46B5-B1BA-82910D56B332}
{0A29616B-5413-4DA9-96EC-B45D6AF632C9} = {3324D046-1E05-46B5-B1BA-82910D56B332}
{BD382403-86D8-4E99-B279-370FC22F059C} = {778AAF38-20FF-438C-A9C3-60850C8B5A27}
{25AA9F04-EEEE-49C4-870B-CDFF71717687} = {778AAF38-20FF-438C-A9C3-60850C8B5A27}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF} SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}

View File

@@ -9,6 +9,7 @@ using FileTime.GuiApp.App;
using FileTime.GuiApp.App.Font; using FileTime.GuiApp.App.Font;
using FileTime.GuiApp.App.ViewModels; using FileTime.GuiApp.App.ViewModels;
using FileTime.GuiApp.App.Views; using FileTime.GuiApp.App.Views;
using FileTime.Server;
using FileTime.Server.Common; using FileTime.Server.Common;
using FileTime.Tools.Compression; using FileTime.Tools.Compression;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -31,7 +32,7 @@ public class Application : Avalonia.Application
.AddCompression() .AddCompression()
.ConfigureFont(configuration) .ConfigureFont(configuration)
.RegisterLogging() .RegisterLogging()
.RegisterServices() .RegisterGuiServices()
.AddSettings() .AddSettings()
.AddViewModels() .AddViewModels()
.BuildServiceProvider(); .BuildServiceProvider();

View File

@@ -46,6 +46,7 @@
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.Search\FileTime.App.Search.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.Search\FileTime.App.Search.csproj" />
<ProjectReference Include="..\..\..\Server\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" /> <ProjectReference Include="..\..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" />
<ProjectReference Include="..\FileTime.GuiApp.CustomImpl\FileTime.GuiApp.CustomImpl.csproj" /> <ProjectReference Include="..\FileTime.GuiApp.CustomImpl\FileTime.GuiApp.CustomImpl.csproj" />
<ProjectReference Include="..\FileTime.GuiApp.Font\FileTime.GuiApp.Font.csproj" /> <ProjectReference Include="..\FileTime.GuiApp.Font\FileTime.GuiApp.Font.csproj" />

View File

@@ -55,7 +55,7 @@ public static class Startup
return serviceCollection; return serviceCollection;
} }
internal static IServiceCollection RegisterServices(this IServiceCollection serviceCollection) internal static IServiceCollection RegisterGuiServices(this IServiceCollection serviceCollection)
{ {
serviceCollection.TryAddSingleton<IRxSchedulerService, AvaloniaRxSchedulerService>(); serviceCollection.TryAddSingleton<IRxSchedulerService, AvaloniaRxSchedulerService>();
serviceCollection.TryAddSingleton<IKeyInputHandlerService, KeyInputHandlerService>(); serviceCollection.TryAddSingleton<IKeyInputHandlerService, KeyInputHandlerService>();

View File

@@ -14,6 +14,7 @@ namespace FileTime.Providers.LocalAdmin;
public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChanged, IExitHandler public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChanged, IExitHandler
{ {
private const string AdminContentProviderName = "localAdminRemote";
private class ConnectionInfo private class ConnectionInfo
{ {
public string? SignalRBaseUrl { get; init; } public string? SignalRBaseUrl { get; init; }
@@ -144,9 +145,10 @@ public class AdminElevationManager : IAdminElevationManager, INotifyPropertyChan
_remoteContentProvider = new RemoteContentProvider( _remoteContentProvider = new RemoteContentProvider(
_timelessContentProvider, _timelessContentProvider,
async () => await SignalRConnection.GetOrCreateForAsync(_connectionInfo.SignalRBaseUrl), _serviceProvider,
async () => await SignalRConnection.GetOrCreateForAsync(_connectionInfo.SignalRBaseUrl, AdminContentProviderName),
"local", "local",
"localAdminRemote" AdminContentProviderName
); );
return Task.FromResult((IRemoteContentProvider)_remoteContentProvider); return Task.FromResult((IRemoteContentProvider)_remoteContentProvider);

View File

@@ -7,4 +7,5 @@ public interface IRemoteContentProvider : IContentProvider
{ {
Task<IRemoteConnection> GetRemoteConnectionAsync(); Task<IRemoteConnection> GetRemoteConnectionAsync();
string RemoteProviderName { get; } string RemoteProviderName { get; }
Task InitializeChildren();
} }

View File

@@ -1,47 +1,101 @@
using FileTime.Core.ContentAccess; using FileTime.Core.ContentAccess;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Serialization.Container;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
using FileTime.Server.Common; using FileTime.Server.Common;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Providers.Remote; namespace FileTime.Providers.Remote;
public sealed class RemoteContentProvider : ContentProviderBase, IRemoteContentProvider public sealed class RemoteContentProvider : ContentProviderBase, IRemoteContentProvider
{ {
public string RemoteProviderName { get; } private readonly IServiceProvider _serviceProvider;
private readonly Func<Task<IRemoteConnection>> _remoteConnectionProvider; private readonly Func<Task<IRemoteConnection>> _remoteConnectionProvider;
private readonly SemaphoreSlim _initializeSemaphore = new(1, 1);
private bool _initialized;
public string RemoteProviderName { get; }
public RemoteContentProvider( public RemoteContentProvider(
ITimelessContentProvider timelessContentProvider, ITimelessContentProvider timelessContentProvider,
IServiceProvider serviceProvider,
Func<Task<IRemoteConnection>> remoteConnectionProvider, Func<Task<IRemoteConnection>> remoteConnectionProvider,
string remoteName, string remoteName,
string name) string name)
: base(name, timelessContentProvider) : base(name, timelessContentProvider)
{ {
RemoteProviderName = remoteName; RemoteProviderName = remoteName;
_serviceProvider = serviceProvider;
_remoteConnectionProvider = remoteConnectionProvider; _remoteConnectionProvider = remoteConnectionProvider;
} }
public async Task<IRemoteConnection> GetRemoteConnectionAsync() public async Task<IRemoteConnection> GetRemoteConnectionAsync()
=> await _remoteConnectionProvider(); => await _remoteConnectionProvider();
public async Task InitializeChildren()
{
await _initializeSemaphore.WaitAsync();
try
{
if (_initialized) return;
//TODO: loading indicator
var connection = await GetRemoteConnectionAsync();
var children = await connection.GetChildren(RemoteProviderName, RemoteProviderName);
_initialized = true;
}
finally
{
_initializeSemaphore.Release();
}
}
//TODO implement //TODO implement
public override Task<IItem> GetItemByNativePathAsync( public override async Task<IItem> GetItemByNativePathAsync(
NativePath nativePath, NativePath nativePath,
PointInTime pointInTime, PointInTime pointInTime,
bool forceResolve = false, bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default) => ItemInitializationSettings itemInitializationSettings = default)
throw new NotImplementedException(); {
if (nativePath.Path == string.Empty)
{
return this;
}
var connection = await GetRemoteConnectionAsync();
var serialized = await connection.GetItemByNativePathAsync(
RemoteProviderName,
nativePath,
pointInTime,
forceResolve,
forceResolvePathType,
itemInitializationSettings
);
if (serialized is SerializedContainer serializedContainer)
{
var containerDeserializer = _serviceProvider.GetRequiredService<ContainerDeserializer>();
var container = containerDeserializer.Deserialize(
serializedContainer,
new ContainerDeserializationContext(this)
);
return container.Container;
}
throw new NotSupportedException();
}
//TODO: make it async
public override async ValueTask<NativePath> GetNativePathAsync(FullName fullName) public override async ValueTask<NativePath> GetNativePathAsync(FullName fullName)
{ {
var remoteFullname = new FullName(ConvertLocalFullNameToRemote(fullName)); var remoteFullname = new FullName(ConvertLocalFullNameToRemote(fullName));
var connection = await GetRemoteConnectionAsync(); var connection = await GetRemoteConnectionAsync();
var remoteNativePath = await connection.GetNativePathAsync(remoteFullname); var remoteNativePath = await connection.GetNativePathAsync(RemoteProviderName, remoteFullname);
return new NativePath(remoteNativePath!.Path); return new NativePath(remoteNativePath.Path);
} }
public override FullName GetFullName(NativePath nativePath) => throw new NotImplementedException(); public override FullName GetFullName(NativePath nativePath) => throw new NotImplementedException();

View File

@@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" /> <ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,4 +1,7 @@
using FileTime.Core.Models; using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Timeline;
namespace FileTime.Server.Common; namespace FileTime.Server.Common;
@@ -13,5 +16,17 @@ public interface IRemoteConnection
Task WriteBytesAsync(string transactionId, byte[] data, int? index, CancellationToken cancellationToken = default); Task WriteBytesAsync(string transactionId, byte[] data, int? index, CancellationToken cancellationToken = default);
Task FlushWriterAsync(string transactionId, CancellationToken cancellationToken = default); Task FlushWriterAsync(string transactionId, CancellationToken cancellationToken = default);
Task CloseWriterAsync(string transactionId); Task CloseWriterAsync(string transactionId);
Task<NativePath?> GetNativePathAsync(FullName fullName); Task<NativePath> GetNativePathAsync(string contentProviderId, FullName fullName);
Task<ISerialized> GetItemByNativePathAsync(
string contentProviderId,
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve,
AbsolutePathType forceResolvePathType,
ItemInitializationSettings itemInitializationSettings);
Task<SerializedAbsolutePath[]> GetChildren(
string contentProviderId,
string fullName);
} }

View File

@@ -0,0 +1,9 @@
using FileTime.Core.Models;
namespace FileTime.Server.Common.ItemTracker;
public interface IItemTrackerRegistry
{
int Register(IItem item);
event Action<int>? ItemRemoved;
}

View File

@@ -2,5 +2,5 @@
public interface ISignalRClient public interface ISignalRClient
{ {
Task RemoveTrackedItem(int itemId);
} }

View File

@@ -1,7 +1,13 @@
namespace FileTime.Server.Common.Connections.SignalR; using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Timeline;
namespace FileTime.Server.Common.Connections.SignalR;
public interface ISignalRHub public interface ISignalRHub
{ {
Task SetClientIdentifier(string providerName);
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);
@@ -15,5 +21,16 @@ public interface ISignalRHub
//TODO: CancellationToken https://github.com/nenoNaninu/TypedSignalR.Client/issues/120 //TODO: CancellationToken https://github.com/nenoNaninu/TypedSignalR.Client/issues/120
Task WriteBytesAsync(string transactionId, string data, int index); Task WriteBytesAsync(string transactionId, string data, int index);
Task CloseWriterAsync(string transactionId); Task CloseWriterAsync(string transactionId);
Task<string?> GetNativePathAsync(string fullNamePath); Task<string> GetNativePathAsync(string contentProviderId, string fullNamePath);
Task<ISerialized> GetItemByNativePathAsync(
string contentProviderId,
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve,
AbsolutePathType forceResolvePathType,
ItemInitializationSettings itemInitializationSettings);
Task<SerializedAbsolutePath[]> GetChildren(
string contentProviderId,
string fullName);
} }

View File

@@ -1,4 +1,7 @@
using FileTime.Core.Models; using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Timeline;
using InitableService; using InitableService;
using Microsoft.AspNetCore.SignalR.Client; using Microsoft.AspNetCore.SignalR.Client;
using Serilog; using Serilog;
@@ -6,7 +9,7 @@ using TypedSignalR.Client;
namespace FileTime.Server.Common.Connections.SignalR; namespace FileTime.Server.Common.Connections.SignalR;
public class SignalRConnection : IRemoteConnection, IAsyncInitable<string> public class SignalRConnection : IRemoteConnection, IAsyncInitable<string, string>
{ {
private static readonly Dictionary<string, SignalRConnection> Connections = new(); private static readonly Dictionary<string, SignalRConnection> Connections = new();
private static readonly object ConnectionsLock = new(); private static readonly object ConnectionsLock = new();
@@ -15,7 +18,7 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
private HubConnection _connection = null!; private HubConnection _connection = null!;
private ISignalRHub _client = null!; private ISignalRHub _client = null!;
public async Task InitAsync(string baseUrl) public async Task InitAsync(string baseUrl, string providerName)
{ {
_baseUrl = baseUrl; _baseUrl = baseUrl;
@@ -26,9 +29,10 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
_connection = connectionBuilder.Build(); _connection = connectionBuilder.Build();
await _connection.StartAsync(); await _connection.StartAsync();
_client = _connection.CreateHubProxy<ISignalRHub>(); _client = _connection.CreateHubProxy<ISignalRHub>();
await _client.SetClientIdentifier(providerName);
} }
public static async Task<SignalRConnection> GetOrCreateForAsync(string baseUrl) public static async Task<SignalRConnection> GetOrCreateForAsync(string baseUrl, string providerName)
{ {
SignalRConnection? connection; SignalRConnection? connection;
lock (ConnectionsLock) lock (ConnectionsLock)
@@ -47,7 +51,7 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
Connections.Add(baseUrl, connection); Connections.Add(baseUrl, connection);
} }
await connection.InitAsync(baseUrl); await connection.InitAsync(baseUrl, providerName);
return connection; return connection;
} }
@@ -78,9 +82,32 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
public async Task CloseWriterAsync(string transactionId) public async Task CloseWriterAsync(string transactionId)
=> await _client.CloseWriterAsync(transactionId); => await _client.CloseWriterAsync(transactionId);
public async Task<NativePath?> GetNativePathAsync(FullName fullName) public async Task<NativePath> GetNativePathAsync(string contentProviderId, FullName fullName)
{ {
var path = await _client.GetNativePathAsync(fullName.Path); var path = await _client.GetNativePathAsync(contentProviderId, fullName.Path);
return path is null ? null : new NativePath(path); return new NativePath(path);
} }
public async Task<ISerialized> GetItemByNativePathAsync(
string contentProviderId,
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve,
AbsolutePathType forceResolvePathType,
ItemInitializationSettings itemInitializationSettings)
{
var item = await _client.GetItemByNativePathAsync(
contentProviderId,
nativePath,
pointInTime,
forceResolve,
forceResolvePathType,
itemInitializationSettings
);
return item;
}
public async Task<SerializedAbsolutePath[]> GetChildren(string contentProviderId, string fullName)
=> await _client.GetChildren(contentProviderId, fullName);
} }

View File

@@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Core\FileTime.Core.Serialization\FileTime.Core.Serialization.csproj" />
<ProjectReference Include="..\FileTime.Server.Common.Abstractions\FileTime.Server.Common.Abstractions.csproj" /> <ProjectReference Include="..\FileTime.Server.Common.Abstractions\FileTime.Server.Common.Abstractions.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -1,17 +0,0 @@
using FileTime.Server.Common.Connections.SignalR;
using FileTime.Server.Common.ContentAccess;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace FileTime.Server.Common;
public static class Startup
{
public static IServiceCollection AddServerCoreServices(this IServiceCollection services)
{
services.AddTransient<SignalRConnection>();
services.TryAddSingleton<IApplicationStopper, ApplicationStopper>();
services.AddSingleton<IContentAccessManager, ContentAccessManager>();
return services;
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>FileTime.Server</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Server.Common\FileTime.Server.Common.csproj" />
<ProjectReference Include="..\FileTime.Server.Tracker\FileTime.Server.Tracker.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@
using FileTime.Server.Common;
using FileTime.Server.Common.Connections.SignalR;
using FileTime.Server.Common.ContentAccess;
using FileTime.Server.Common.ItemTracker;
using FileTime.Server.Tracker.ItemTracker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace FileTime.Server;
public static class Startup
{
public static IServiceCollection AddServerCoreServices(this IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<SignalRConnection>();
serviceCollection.TryAddSingleton<IApplicationStopper, ApplicationStopper>();
serviceCollection.TryAddSingleton<IContentAccessManager, ContentAccessManager>();
return serviceCollection.AddRemoteTrackerServices();
}
private static IServiceCollection AddRemoteTrackerServices(this IServiceCollection serviceCollection)
{
serviceCollection.TryAddSingleton<IItemTrackerRegistry, ItemTrackerRegistry>();
return serviceCollection;
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Server.Common.Abstractions\FileTime.Server.Common.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,45 @@
using FileTime.Core.Models;
using FileTime.Server.Common.ItemTracker;
namespace FileTime.Server.Tracker.ItemTracker;
public class ItemTrackerRegistry : IItemTrackerRegistry
{
private readonly object _lock = new();
private readonly Dictionary<int, WeakReference<IItem>> _items = new();
private int _globalId = 1;
public event Action<int>? ItemRemoved;
public int Register(IItem item)
{
lock (_lock)
{
while (_items.ContainsKey(_globalId)) _globalId++;
_items[_globalId] = new WeakReference<IItem>(item);
return _globalId;
}
}
private void Clean()
{
lock (_lock)
{
var keys = _items.Keys.ToArray();
var keysToRemove = new List<int>();
foreach (var key in keys)
{
if (!_items[key].TryGetTarget(out _))
{
keysToRemove.Add(key);
}
}
foreach (var key in keysToRemove)
{
_items.Remove(key);
ItemRemoved?.Invoke(key);
}
}
}
}

View File

@@ -1,10 +1,14 @@
using FileTime.Core.ContentAccess; using FileTime.Core.ContentAccess;
using FileTime.Core.Enums;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Serialization;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
using FileTime.Server.Common; using FileTime.Server.Common;
using FileTime.Server.Common.Connections.SignalR; using FileTime.Server.Common.Connections.SignalR;
using FileTime.Server.Common.ContentAccess; using FileTime.Server.Common.ContentAccess;
using FileTime.Server.Common.ItemTracker;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Server.Web; namespace FileTime.Server.Web;
@@ -14,20 +18,46 @@ public class ConnectionHub : Hub<ISignalRClient>, ISignalRHub
private readonly IContentAccessorFactory _contentAccessorFactory; private readonly IContentAccessorFactory _contentAccessorFactory;
private readonly IApplicationStopper _applicationStopper; private readonly IApplicationStopper _applicationStopper;
private readonly IContentAccessManager _contentAccessManager; private readonly IContentAccessManager _contentAccessManager;
private readonly ITimelessContentProvider _timelessContentProvider; private readonly IItemTrackerRegistry _itemTrackerRegistry;
private readonly IServiceProvider _serviceProvider;
private readonly Dictionary<int, string> _trackedItemIds = new();
//TODO clean this sometimes
private readonly Dictionary<string, string> _clientIdToConnectionId = new();
public ConnectionHub( public ConnectionHub(
IContentProviderRegistry contentProviderRegistry, IContentProviderRegistry contentProviderRegistry,
IContentAccessorFactory contentAccessorFactory, IContentAccessorFactory contentAccessorFactory,
IApplicationStopper applicationStopper, IApplicationStopper applicationStopper,
IContentAccessManager contentAccessManager, IContentAccessManager contentAccessManager,
ITimelessContentProvider timelessContentProvider) IItemTrackerRegistry itemTrackerRegistry,
IServiceProvider serviceProvider)
{ {
_contentProviderRegistry = contentProviderRegistry; _contentProviderRegistry = contentProviderRegistry;
_contentAccessorFactory = contentAccessorFactory; _contentAccessorFactory = contentAccessorFactory;
_applicationStopper = applicationStopper; _applicationStopper = applicationStopper;
_contentAccessManager = contentAccessManager; _contentAccessManager = contentAccessManager;
_timelessContentProvider = timelessContentProvider; _itemTrackerRegistry = itemTrackerRegistry;
_serviceProvider = serviceProvider;
_itemTrackerRegistry.ItemRemoved += ItemTrackerRegistryOnItemRemoved;
}
private void ItemTrackerRegistryOnItemRemoved(int id)
{
if (_trackedItemIds.ContainsKey(id)) return;
var clientId = _trackedItemIds[id];
var connectionId = _clientIdToConnectionId[clientId];
Clients.Client(connectionId).RemoveTrackedItem(id);
}
public Task SetClientIdentifier(string providerName)
{
_clientIdToConnectionId[providerName] = Context.ConnectionId;
return Task.CompletedTask;
} }
public Task Exit() public Task Exit()
@@ -39,35 +69,35 @@ public class ConnectionHub : Hub<ISignalRClient>, ISignalRHub
public async Task CreateContainerAsync(string contentProviderId, string fullName) public async Task CreateContainerAsync(string contentProviderId, string fullName)
{ {
//TODO handle no content provider with id //TODO handle no content provider with id
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId); var contentProvider = GetContentProvider(contentProviderId);
var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider); var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider);
await itemCreator.CreateContainerAsync(contentProvider, new FullName(fullName)); await itemCreator.CreateContainerAsync(contentProvider, new FullName(fullName));
} }
public async Task CreateElementAsync(string contentProviderId, string fullName) public async Task CreateElementAsync(string contentProviderId, string fullName)
{ {
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId); var contentProvider = GetContentProvider(contentProviderId);
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) public async Task DeleteItemAsync(string contentProviderId, string fullName)
{ {
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId); var contentProvider = GetContentProvider(contentProviderId);
var itemDeleter = _contentAccessorFactory.GetItemDeleter(contentProvider); var itemDeleter = _contentAccessorFactory.GetItemDeleter(contentProvider);
await itemDeleter.DeleteAsync(contentProvider, new FullName(fullName)); await itemDeleter.DeleteAsync(contentProvider, new FullName(fullName));
} }
public async Task MoveItemAsync(string contentProviderId, string fullName, string newPath) public async Task MoveItemAsync(string contentProviderId, string fullName, string newPath)
{ {
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId); var contentProvider = GetContentProvider(contentProviderId);
var itemDeleter = _contentAccessorFactory.GetItemMover(contentProvider); var itemDeleter = _contentAccessorFactory.GetItemMover(contentProvider);
await itemDeleter.RenameAsync(contentProvider, new FullName(fullName), new FullName(newPath)); await itemDeleter.RenameAsync(contentProvider, new FullName(fullName), new FullName(newPath));
} }
public async Task InitializeRemoteWriter(string contentProviderId, string transactionId, string nativePath) public async Task InitializeRemoteWriter(string contentProviderId, string transactionId, string nativePath)
{ {
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId); var contentProvider = GetContentProvider(contentProviderId);
var item = await contentProvider.GetItemByNativePathAsync(new NativePath(nativePath), PointInTime.Present); var item = await contentProvider.GetItemByNativePathAsync(new NativePath(nativePath), PointInTime.Present);
if (item is not IElement element) if (item is not IElement element)
throw new FileNotFoundException("Item is not an element", nativePath); throw new FileNotFoundException("Item is not an element", nativePath);
@@ -90,6 +120,55 @@ public class ConnectionHub : Hub<ISignalRClient>, ISignalRHub
return Task.CompletedTask; return Task.CompletedTask;
} }
public async Task<string?> GetNativePathAsync(string fullNamePath) public async Task<string> GetNativePathAsync(string contentProviderId, string fullNamePath)
=> (await _timelessContentProvider.GetNativePathByFullNameAsync(new FullName(fullNamePath)))?.Path; {
var contentProvider = GetContentProvider(contentProviderId);
return (await contentProvider.GetNativePathAsync(new FullName(fullNamePath))).Path;
}
public async Task<ISerialized> GetItemByNativePathAsync(
string contentProviderId,
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve,
AbsolutePathType forceResolvePathType,
ItemInitializationSettings itemInitializationSettings)
{
var contentProvider = GetContentProvider(contentProviderId);
var item = await contentProvider.GetItemByNativePathAsync(
nativePath,
pointInTime,
forceResolve,
forceResolvePathType,
itemInitializationSettings
);
var id = _itemTrackerRegistry.Register(item);
var serializerType = typeof(ISerializer<>).MakeGenericType(item.GetType());
var serializer = (ISerializer) _serviceProvider.GetRequiredService(serializerType);
var serializedObject = await serializer.SerializeAsync(id, item);
return serializedObject;
}
public async Task<SerializedAbsolutePath[]> GetChildren(
string contentProviderId,
string fullName)
{
var contentProvider = GetContentProvider(contentProviderId);
var item = await contentProvider.GetItemByFullNameAsync(
new FullName(fullName),
PointInTime.Present);
if (item is IContainer container)
return container.Items.Select(AbsolutePathSerializer.Serialize).ToArray();
throw new NotSupportedException();
}
private IContentProvider GetContentProvider(string contentProviderId)
=> _contentProviderRegistry
.ContentProviders
.First(p => p.Name == contentProviderId);
} }

View File

@@ -11,6 +11,7 @@
<ProjectReference Include="..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" /> <ProjectReference Include="..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" /> <ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" />
<ProjectReference Include="..\FileTime.Server.App\FileTime.Server.App.csproj" /> <ProjectReference Include="..\FileTime.Server.App\FileTime.Server.App.csproj" />
<ProjectReference Include="..\FileTime.Server.Extensions.DependencyInjection\FileTime.Server.Extensions.DependencyInjection.csproj" />
<ProjectReference Include="..\FileTime.Server.Web\FileTime.Server.Web.csproj" /> <ProjectReference Include="..\FileTime.Server.Web\FileTime.Server.Web.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -2,6 +2,7 @@
using Autofac.Extensions.DependencyInjection; using Autofac.Extensions.DependencyInjection;
using FileTime.App.DependencyInjection; using FileTime.App.DependencyInjection;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using FileTime.Server;
using FileTime.Server.App; using FileTime.Server.App;
using FileTime.Server.Common; using FileTime.Server.Common;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;