mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 13:26:30 +00:00
rework to Azaion.Suite
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<Application x:Class="Azaion.Suite.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -0,0 +1,114 @@
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using Azaion.Annotator;
|
||||
using Azaion.Annotator.DTO;
|
||||
using Azaion.Annotator.Extensions;
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.DTO.Config;
|
||||
using Azaion.Common.Extensions;
|
||||
using Azaion.Suite.Services;
|
||||
using Azaion.Suite.Services.DTO;
|
||||
using Azaion.Dataset;
|
||||
using LibVLCSharp.Shared;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Serilog;
|
||||
|
||||
namespace Azaion.Suite;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
private readonly IHost _host;
|
||||
private readonly ILogger<App> _logger;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public App()
|
||||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.Enrich.FromLogContext()
|
||||
.MinimumLevel.Information()
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File(
|
||||
path: "Logs/log.txt",
|
||||
rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
|
||||
_host = Host.CreateDefaultBuilder()
|
||||
.ConfigureAppConfiguration((context, config) => config
|
||||
.AddCommandLine(Environment.GetCommandLineArgs())
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true))
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
services.AddSingleton<Loader>();
|
||||
services.AddSingleton<IHardwareService, HardwareService>();
|
||||
services.AddSingleton<IResourceLoader, ResourceLoader>();
|
||||
|
||||
services.Configure<ApiConfig>(context.Configuration.GetSection(nameof(ApiConfig)));
|
||||
services.AddSingleton<IConfigUpdater, ConfigUpdater>();
|
||||
|
||||
services.AddSingleton<Annotator.Annotator>();
|
||||
services.AddSingleton<DatasetExplorer>();
|
||||
services.AddSingleton<HelpWindow>();
|
||||
services.AddSingleton<IAIDetector, YOLODetector>();
|
||||
services.AddMediatR(c => c.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
|
||||
services.AddSingleton<LibVLC>(_ => new LibVLC());
|
||||
services.AddSingleton<FormState>();
|
||||
services.AddSingleton<MediaPlayer>(sp =>
|
||||
{
|
||||
var libVLC = sp.GetRequiredService<LibVLC>();
|
||||
return new MediaPlayer(libVLC);
|
||||
});
|
||||
services.AddSingleton<AnnotatorEventHandler>();
|
||||
services.AddSingleton<VLCFrameExtractor>();
|
||||
|
||||
services.AddHttpClient<AzaionApiClient>((sp, client) =>
|
||||
{
|
||||
var apiConfig = sp.GetRequiredService<IOptions<ApiConfig>>().Value;
|
||||
client.BaseAddress = new Uri(apiConfig.Url);
|
||||
client.Timeout = TimeSpan.FromSeconds(apiConfig.TimeoutSeconds);
|
||||
});
|
||||
|
||||
services.AddSingleton<DatasetExplorer>();
|
||||
services.AddSingleton<IGalleryManager, GalleryManager>();
|
||||
})
|
||||
.Build();
|
||||
_mediator = _host.Services.GetRequiredService<IMediator>();
|
||||
_logger = _host.Services.GetRequiredService<ILogger<App>>();
|
||||
DispatcherUnhandledException += OnDispatcherUnhandledException;
|
||||
}
|
||||
|
||||
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
_logger.LogError(e.Exception, e.Exception.Message);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
protected override async void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
EventManager.RegisterClassHandler(typeof(UIElement), UIElement.KeyDownEvent, new RoutedEventHandler(GlobalClick));
|
||||
await _host.StartAsync();
|
||||
_host.Services.GetRequiredService<Loader>().Show();
|
||||
|
||||
base.OnStartup(e);
|
||||
}
|
||||
|
||||
private void GlobalClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var args = (KeyEventArgs)e;
|
||||
var keyEvent = new KeyEvent(sender, args, (sender as FrameworkElement).GetParentWindow());
|
||||
_ = ThrottleExt.Throttle(() => _mediator.Publish(keyEvent), TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
protected override async void OnExit(ExitEventArgs e)
|
||||
{
|
||||
await _host.StopAsync();
|
||||
_host.Dispose();
|
||||
base.OnExit(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
@@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ApplicationIcon>..\logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MediatR" Version="12.4.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.4" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="appsettings.json" />
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Azaion.Annotator\Azaion.Annotator.csproj" />
|
||||
<ProjectReference Include="..\Azaion.Common\Azaion.Common.csproj" />
|
||||
<ProjectReference Include="..\Azaion.Dataset\Azaion.Dataset.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using Azaion.Suite.Services.DTO;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Azaion.Suite;
|
||||
|
||||
public class DynamicAssemblyLoader(IOptions<LocalFilesConfig> localFilesConfig) : AssemblyLoadContext
|
||||
{
|
||||
private static readonly Dictionary<string?, Assembly> LoadedAssemblies = new();
|
||||
|
||||
static DynamicAssemblyLoader()
|
||||
{
|
||||
LoadedAssemblies = Default.Assemblies.ToDictionary(a => a.GetName().Name, a => a);
|
||||
}
|
||||
|
||||
protected override Assembly Load(AssemblyName assemblyName)
|
||||
{
|
||||
var assembly = LoadedAssemblies.GetValueOrDefault(assemblyName.Name);
|
||||
if (assembly != null)
|
||||
return assembly;
|
||||
|
||||
var currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
|
||||
var asm = Assembly.LoadFile(Path.Combine(currentLocation, localFilesConfig.Value.DllPath, $"{assemblyName.Name!}.dll"));
|
||||
return asm;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Azaion.Suite;
|
||||
|
||||
public class HardwareInfo
|
||||
{
|
||||
public string CPU { get; set; } = null!;
|
||||
public string GPU { get; set; } = null!;
|
||||
public string Memory { get; set; } = null!;
|
||||
|
||||
public string Hash { get; set; } = null!;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<Window x:Class="Azaion.Suite.Loader"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
WindowStyle="None"
|
||||
AllowsTransparency="True"
|
||||
Background="Transparent"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Title="Azaion Annotator Security"
|
||||
Height="280" Width="350">
|
||||
<Border Width="350"
|
||||
Height="280"
|
||||
Background="DarkGray"
|
||||
CornerRadius="15"
|
||||
MouseMove="MainMouseMove">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="15"
|
||||
Direction ="-90"
|
||||
RenderingBias ="Quality"
|
||||
ShadowDepth ="2"
|
||||
Color ="Gray" />
|
||||
</Border.Effect>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Margin="20">
|
||||
<Canvas>
|
||||
<Button Padding="5" ToolTip="Закрити" Background="DarkGray" BorderBrush="DarkGray" Canvas.Left="290" Cursor="Hand"
|
||||
Name="CloseBtn"
|
||||
Click="CloseClick">
|
||||
<Path Stretch="Fill" Fill="LightGray" Data="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166
|
||||
4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166
|
||||
19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289
|
||||
17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" />
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border Background="{TemplateBinding Background}" BorderThickness="1">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</Canvas>
|
||||
<TextBlock Text="Вхід"
|
||||
FontSize="25"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
FontWeight="Bold"
|
||||
/>
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="Email"
|
||||
Grid.Row="0"
|
||||
Margin="0, 20, 0, 5"
|
||||
HorizontalAlignment="Left"/>
|
||||
<TextBox
|
||||
Name="TbEmail"
|
||||
Grid.Row="1"
|
||||
Padding="0,5"
|
||||
Width="300"
|
||||
FontSize="16"
|
||||
Background="DarkGray"
|
||||
BorderBrush="DimGray"
|
||||
BorderThickness="0,0,0,1"
|
||||
HorizontalAlignment="Left"
|
||||
Text="admin@azaion.com"/>
|
||||
<TextBlock Text="Пароль"
|
||||
Grid.Row="2"
|
||||
Margin="0, 20, 0, 5"
|
||||
HorizontalAlignment="Left"/>
|
||||
<PasswordBox Grid.Row="3"
|
||||
Name="TbPassword"
|
||||
Password="Az@1on1000Odm$n"
|
||||
FontSize="16"
|
||||
Background="DarkGray"
|
||||
BorderBrush="DimGray"
|
||||
Padding="0,5"
|
||||
Width="300"
|
||||
BorderThickness="0,0,0,1"
|
||||
HorizontalAlignment="Left"/>
|
||||
</Grid>
|
||||
<Button x:Name="LoginBtn"
|
||||
Content="Вхід"
|
||||
Foreground="White"
|
||||
Background="DimGray"
|
||||
Margin="0,25"
|
||||
Height="35"
|
||||
Width="280"
|
||||
Cursor="Hand"
|
||||
Click="RunClick">
|
||||
<Button.Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border x:Name="LoginBorder" Background="{TemplateBinding Background}"
|
||||
CornerRadius="16">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="LightGray" TargetName="LoginBorder" />
|
||||
<Setter Property="TextBlock.Foreground" Value="Black" TargetName="LoginBorder" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Window>
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using Azaion.Suite.Services.DTO;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Azaion.Suite;
|
||||
|
||||
public partial class Loader : Window
|
||||
{
|
||||
private readonly IResourceLoader _resourceLoader;
|
||||
private readonly IOptions<LocalFilesConfig> _localFilesConfig;
|
||||
|
||||
public Loader(IResourceLoader resourceLoader, IOptions<LocalFilesConfig> localFilesConfig)
|
||||
{
|
||||
_resourceLoader = resourceLoader;
|
||||
_localFilesConfig = localFilesConfig;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void RunClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
await _resourceLoader.LoadAnnotator(TbEmail.Text, TbPassword.Password, stream);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
var loader = new AssemblyLoadContext("DynamicContext", isCollectible: true);
|
||||
var annotatorAssembly = loader.LoadFromStream(stream);
|
||||
|
||||
var appType = annotatorAssembly.GetType("Azaion.Annotator.App");
|
||||
var appInstance = Activator.CreateInstance(appType);
|
||||
var runMethod = appType.GetMethod("Run", BindingFlags.Public | BindingFlags.Instance);
|
||||
if (runMethod != null)
|
||||
{
|
||||
runMethod.Invoke(appInstance, null);
|
||||
}
|
||||
|
||||
// var entryPoint = annotatorAssembly.EntryPoint;
|
||||
// if (entryPoint == null)
|
||||
// return;
|
||||
//
|
||||
// var o = annotatorAssembly.CreateInstance(entryPoint.Name);
|
||||
// entryPoint.Invoke(o, null);
|
||||
}
|
||||
|
||||
private void CloseClick(object sender, RoutedEventArgs e) => Close();
|
||||
|
||||
private void MainMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource is Button || e.OriginalSource is TextBox)
|
||||
return;
|
||||
|
||||
if (e.LeftButton == MouseButtonState.Pressed)
|
||||
DragMove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<Window x:Class="Azaion.Suite.MainSuite"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Azaion.Suite"
|
||||
mc:Ignorable="d"
|
||||
Title="MainSuite" Height="450" Width="800">
|
||||
<Grid>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using Azaion.Annotator.Extensions;
|
||||
using Azaion.Common.DTO.Config;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Azaion.Suite;
|
||||
|
||||
public partial class MainSuite : Window
|
||||
{
|
||||
private readonly AppConfig _appConfig;
|
||||
private readonly IConfigUpdater _configUpdater;
|
||||
|
||||
public MainSuite(IOptions<AppConfig> appConfig, IConfigUpdater configUpdater)
|
||||
{
|
||||
_configUpdater = configUpdater;
|
||||
_appConfig = appConfig.Value;
|
||||
InitializeComponent();
|
||||
Loaded += OnLoaded;
|
||||
Closed += OnFormClosed;
|
||||
|
||||
SizeChanged += async (_, _) => await SaveUserSettings();
|
||||
LocationChanged += async (_, _) => await SaveUserSettings();
|
||||
StateChanged += async (_, _) => await SaveUserSettings();
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!Directory.Exists(_appConfig.DirectoriesConfig.LabelsDirectory))
|
||||
Directory.CreateDirectory(_appConfig.DirectoriesConfig.LabelsDirectory);
|
||||
if (!Directory.Exists(_appConfig.DirectoriesConfig.ImagesDirectory))
|
||||
Directory.CreateDirectory(_appConfig.DirectoriesConfig.ImagesDirectory);
|
||||
if (!Directory.Exists(_appConfig.DirectoriesConfig.ResultsDirectory))
|
||||
Directory.CreateDirectory(_appConfig.DirectoriesConfig.ResultsDirectory);
|
||||
|
||||
|
||||
Left = _appConfig.WindowConfig.WindowLocation.X;
|
||||
Top = _appConfig.WindowConfig.WindowLocation.Y;
|
||||
Width = _appConfig.WindowConfig.WindowSize.Width;
|
||||
Height = _appConfig.WindowConfig.WindowSize.Height;
|
||||
|
||||
if (_appConfig.WindowConfig.FullScreen)
|
||||
WindowState = WindowState.Maximized;
|
||||
}
|
||||
|
||||
private async Task SaveUserSettings()
|
||||
{
|
||||
await ThrottleExt.Throttle(() =>
|
||||
{
|
||||
_configUpdater.Save(_appConfig);
|
||||
return Task.CompletedTask;
|
||||
}, TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
private void OnFormClosed(object? sender, EventArgs e)
|
||||
{
|
||||
_configUpdater.Save(_appConfig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Azaion.Suite.Services;
|
||||
using Azaion.Suite.Services.DTO;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Azaion.Suite;
|
||||
|
||||
public interface IResourceLoader
|
||||
{
|
||||
Task LoadAnnotator(string email, string password, Stream outStream, CancellationToken cancellationToken = default);
|
||||
Assembly LoadAssembly(string name, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public class ResourceLoader(AzaionApiClient azaionApi, IHardwareService hardwareService, IOptions<LocalFilesConfig> localFilesConfig) : IResourceLoader
|
||||
{
|
||||
public async Task LoadAnnotator(string email, string password, Stream outStream, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var hardwareInfo = await hardwareService.GetHardware();
|
||||
azaionApi.Login(email, password);
|
||||
var key = Security.MakeEncryptionKey(email, password, hardwareInfo.Hash);
|
||||
|
||||
var encryptedStream = await azaionApi.GetResource(password, hardwareInfo, ResourceEnum.AnnotatorDll);
|
||||
|
||||
await encryptedStream.DecryptTo(outStream, key, cancellationToken);
|
||||
//return Assembly.Load(stream.ToArray());
|
||||
}
|
||||
|
||||
public Assembly LoadAssembly(string name, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var dllValues = name.Split(",");
|
||||
var dllName = $"{dllValues[0]}.dll";
|
||||
var currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
|
||||
var asm = Assembly.LoadFile(Path.Combine(currentLocation, localFilesConfig.Value.DllPath, dllName));
|
||||
return asm;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using Azaion.Suite.Services.DTO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Azaion.Suite.Services;
|
||||
|
||||
public class AzaionApiClient(HttpClient httpClient)
|
||||
{
|
||||
const string JSON_MEDIA = "application/json";
|
||||
|
||||
private string Email { get; set; } = null!;
|
||||
private SecureString Password { get; set; } = new();
|
||||
private string JwtToken { get; set; } = null!;
|
||||
|
||||
public void Login(string email, string password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password))
|
||||
throw new Exception("Email or password is empty!");
|
||||
|
||||
Email = email;
|
||||
Password = password.ToSecureString();
|
||||
}
|
||||
|
||||
public async Task<Stream> GetResource(string password, HardwareInfo hardware, ResourceEnum resourceEnum)
|
||||
{
|
||||
var response = await Send(httpClient, new HttpRequestMessage(HttpMethod.Post, "/resources/get")
|
||||
{
|
||||
Content = new StringContent(JsonConvert.SerializeObject(new { password, hardware, resourceEnum }), Encoding.UTF8, JSON_MEDIA)
|
||||
});
|
||||
return await response.Content.ReadAsStreamAsync();
|
||||
}
|
||||
|
||||
private async Task<string> Authorize()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Email) || Password.Length == 0)
|
||||
throw new Exception("Email or password is empty! Please do Login first!");
|
||||
|
||||
var payload = new
|
||||
{
|
||||
email = Email,
|
||||
password = Password.ToRealString()
|
||||
};
|
||||
var response = await httpClient.PostAsync(
|
||||
"login",
|
||||
new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, JSON_MEDIA));
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new Exception($"Login failed: {response.StatusCode}");
|
||||
|
||||
var responseData = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var result = JsonConvert.DeserializeObject<LoginResponse>(responseData);
|
||||
|
||||
if (string.IsNullOrEmpty(result?.Token))
|
||||
throw new Exception("JWT Token not found in response");
|
||||
|
||||
return result.Token;
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> Send(HttpClient client, HttpRequestMessage request)
|
||||
{
|
||||
if (string.IsNullOrEmpty(JwtToken))
|
||||
JwtToken = await Authorize();
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", JwtToken);
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
JwtToken = await Authorize();
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", JwtToken);
|
||||
response = await client.SendAsync(request);
|
||||
}
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
return response;
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"Failed: {response.StatusCode}! Result: {result}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Azaion.Suite.Services.DTO;
|
||||
|
||||
public enum ResourceEnum
|
||||
{
|
||||
None = 0,
|
||||
AnnotatorDll = 10,
|
||||
AIModelRKNN = 20,
|
||||
AIModelONNX = 30,
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Azaion.Suite.Services;
|
||||
|
||||
public interface IHardwareService
|
||||
{
|
||||
Task<HardwareInfo> GetHardware();
|
||||
}
|
||||
|
||||
public class HardwareService : IHardwareService
|
||||
{
|
||||
private const string WIN32_GET_HARDWARE_COMMAND =
|
||||
"wmic OS get TotalVisibleMemorySize /Value && " +
|
||||
"wmic CPU get Name /Value && " +
|
||||
"wmic path Win32_VideoController get Name /Value";
|
||||
|
||||
private const string UNIX_GET_HARDWARE_COMMAND =
|
||||
"/bin/bash -c \"free -g | grep Mem: | awk '{print $2}' && " +
|
||||
"lscpu | grep 'Model name:' | cut -d':' -f2 && " +
|
||||
"lspci | grep VGA | cut -d':' -f3\"";
|
||||
|
||||
public async Task<HardwareInfo> GetHardware()
|
||||
{
|
||||
try
|
||||
{
|
||||
var output = await RunCommand(Environment.OSVersion.Platform == PlatformID.Win32NT
|
||||
? WIN32_GET_HARDWARE_COMMAND
|
||||
: UNIX_GET_HARDWARE_COMMAND);
|
||||
|
||||
var lines = output
|
||||
.Replace("TotalVisibleMemorySize=", "")
|
||||
.Replace("Name=", "")
|
||||
.Replace(" ", " ")
|
||||
.Trim()
|
||||
.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var memoryStr = "Unknown RAM";
|
||||
if (lines.Length > 0)
|
||||
{
|
||||
memoryStr = lines[0];
|
||||
if (int.TryParse(memoryStr, out var memKb))
|
||||
memoryStr = $"{Math.Round(memKb / 1024.0 / 1024.0)} Gb";
|
||||
}
|
||||
|
||||
var hardwareInfo = new HardwareInfo
|
||||
{
|
||||
Memory = memoryStr,
|
||||
CPU = lines.Length > 1 && string.IsNullOrEmpty(lines[1])
|
||||
? "Unknown RAM"
|
||||
: lines[1],
|
||||
GPU = lines.Length > 2 && string.IsNullOrEmpty(lines[2])
|
||||
? "Unknown GPU"
|
||||
: lines[2]
|
||||
};
|
||||
hardwareInfo.Hash = ToHash($"Azaion_{MacAddress()}_{hardwareInfo.CPU}_{hardwareInfo.GPU}");
|
||||
return hardwareInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private string MacAddress()
|
||||
{
|
||||
var macAddress = NetworkInterface
|
||||
.GetAllNetworkInterfaces()
|
||||
.Where(nic => nic.OperationalStatus == OperationalStatus.Up)
|
||||
.Select(nic => nic.GetPhysicalAddress().ToString())
|
||||
.FirstOrDefault();
|
||||
|
||||
return macAddress ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task<string> RunCommand(string command)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var process = new Process();
|
||||
process.StartInfo.FileName = Environment.OSVersion.Platform == PlatformID.Unix ? "/bin/bash" : "cmd.exe";
|
||||
process.StartInfo.Arguments = Environment.OSVersion.Platform == PlatformID.Unix
|
||||
? $"-c \"{command}\""
|
||||
: $"/c {command}";
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
|
||||
process.Start();
|
||||
var result = await process.StandardOutput.ReadToEndAsync();
|
||||
await process.WaitForExitAsync();
|
||||
|
||||
return result.Trim();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ToHash(string str) =>
|
||||
Convert.ToBase64String(SHA384.HashData(Encoding.UTF8.GetBytes(str)));
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Azaion.Suite.Services;
|
||||
|
||||
public static class Security
|
||||
{
|
||||
private const int BUFFER_SIZE = 524288; // 512 KB buffer size
|
||||
|
||||
public static string ToHash(this string str) =>
|
||||
Convert.ToBase64String(SHA384.HashData(Encoding.UTF8.GetBytes(str)));
|
||||
|
||||
public static string MakeEncryptionKey(string email, string password, string? hardwareHash) =>
|
||||
$"{email}-{password}-{hardwareHash}-#%@AzaionKey@%#---".ToHash();
|
||||
|
||||
public static SecureString ToSecureString(this string str)
|
||||
{
|
||||
var secureString = new SecureString();
|
||||
foreach (var c in str.ToCharArray())
|
||||
secureString.AppendChar(c);
|
||||
|
||||
return secureString;
|
||||
}
|
||||
|
||||
public static string? ToRealString(this SecureString value)
|
||||
{
|
||||
var valuePtr = IntPtr.Zero;
|
||||
try
|
||||
{
|
||||
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
|
||||
return Marshal.PtrToStringUni(valuePtr);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static async Task EncryptTo(this Stream stream, Stream toStream, string key, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (stream is { CanRead: false }) throw new ArgumentNullException(nameof(stream));
|
||||
if (key is not { Length: > 0 }) throw new ArgumentNullException(nameof(key));
|
||||
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = SHA256.HashData(Encoding.UTF8.GetBytes(key));
|
||||
aes.GenerateIV();
|
||||
|
||||
using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
||||
await using var cs = new CryptoStream(toStream, encryptor, CryptoStreamMode.Write, leaveOpen: true);
|
||||
|
||||
// Prepend IV to the encrypted data
|
||||
await toStream.WriteAsync(aes.IV.AsMemory(0, aes.IV.Length), cancellationToken);
|
||||
|
||||
var buffer = new byte[BUFFER_SIZE];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await stream.ReadAsync(buffer, cancellationToken)) > 0)
|
||||
await cs.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
|
||||
}
|
||||
|
||||
public static async Task DecryptTo(this Stream encryptedStream, Stream toStream, string key, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var aes = Aes.Create();
|
||||
aes.Key = SHA256.HashData(Encoding.UTF8.GetBytes(key));
|
||||
|
||||
// Read the IV from the start of the input stream
|
||||
var iv = new byte[aes.BlockSize / 8];
|
||||
_ = await encryptedStream.ReadAsync(iv, cancellationToken);
|
||||
aes.IV = iv;
|
||||
|
||||
using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||
await using var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read, leaveOpen: true);
|
||||
|
||||
// Read and write in chunks
|
||||
var buffer = new byte[BUFFER_SIZE];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await cryptoStream.ReadAsync(buffer, cancellationToken)) > 0)
|
||||
await toStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ApiConfig": {
|
||||
"Url": "https://api.azaion.com",
|
||||
"TimeoutSeconds": 20,
|
||||
"RetryCount": 3
|
||||
},
|
||||
"LocalFilesConfig": {
|
||||
"DllPath": "AzaionSuite"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user