mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 10:56:31 +00:00
failsafe load dlls
add user config queue offsets throttle improvements
This commit is contained in:
@@ -7,9 +7,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" />
|
||||
<PackageReference Include="MessagePack" Version="3.1.0" />
|
||||
<PackageReference Include="MessagePack.Annotations" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" />
|
||||
<PackageReference Include="NetMQ" Version="4.0.1.13" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Azaion.CommonSecurity.DTO;
|
||||
|
||||
internal class BusinessExceptionDto
|
||||
{
|
||||
public int ErrorCode { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Azaion.CommonSecurity.DTO;
|
||||
|
||||
public class DirectoriesConfig
|
||||
{
|
||||
public string ApiResourcesDirectory { get; set; } = null!;
|
||||
|
||||
public string VideosDirectory { get; set; } = null!;
|
||||
public string LabelsDirectory { get; set; } = null!;
|
||||
public string ImagesDirectory { get; set; } = null!;
|
||||
public string ResultsDirectory { get; set; } = null!;
|
||||
public string ThumbnailsDirectory { get; set; } = null!;
|
||||
|
||||
public string GpsSatDirectory { get; set; } = null!;
|
||||
public string GpsRouteDirectory { get; set; } = null!;
|
||||
}
|
||||
@@ -8,10 +8,7 @@ public abstract class ExternalClientConfig
|
||||
public int RetryCount {get;set;}
|
||||
}
|
||||
|
||||
public class InferenceClientConfig : ExternalClientConfig
|
||||
{
|
||||
public string ResourcesFolder { get; set; } = "";
|
||||
}
|
||||
public class InferenceClientConfig : ExternalClientConfig;
|
||||
|
||||
public class GpsDeniedClientConfig : ExternalClientConfig
|
||||
{
|
||||
|
||||
@@ -6,6 +6,4 @@ public class HardwareInfo
|
||||
public string GPU { get; set; } = null!;
|
||||
public string MacAddress { get; set; } = null!;
|
||||
public string Memory { get; set; } = null!;
|
||||
|
||||
public string Hash { get; set; } = null!;
|
||||
}
|
||||
@@ -4,4 +4,5 @@ public class SecureAppConfig
|
||||
{
|
||||
public InferenceClientConfig InferenceClientConfig { get; set; } = null!;
|
||||
public GpsDeniedClientConfig GpsDeniedClientConfig { get; set; } = null!;
|
||||
public DirectoriesConfig DirectoriesConfig { get; set; } = null!;
|
||||
}
|
||||
@@ -1,11 +1,21 @@
|
||||
using MessagePack;
|
||||
|
||||
namespace Azaion.CommonSecurity.DTO;
|
||||
|
||||
[MessagePackObject]
|
||||
public class User
|
||||
{
|
||||
[Key("i")] public string Id { get; set; } = "";
|
||||
[Key("e")] public string Email { get; set; } = "";
|
||||
[Key("r")]public RoleEnum Role { get; set; }
|
||||
public string Id { get; set; } = "";
|
||||
public string Email { get; set; } = "";
|
||||
public RoleEnum Role { get; set; }
|
||||
public UserConfig? UserConfig { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class UserConfig
|
||||
{
|
||||
public UserQueueOffsets? QueueConfig { get; set; } = new();
|
||||
}
|
||||
|
||||
public class UserQueueOffsets
|
||||
{
|
||||
public int AnnotationsOffset { get; set; }
|
||||
public int AnnotationsConfirmOffset { get; set; }
|
||||
public int AnnotationsCommandsOffset { get; set; }
|
||||
}
|
||||
@@ -9,6 +9,9 @@ public class SecurityConstants
|
||||
public const string DUMMY_DIR = "dummy";
|
||||
|
||||
#region ExternalClientsConfig
|
||||
//public const string API_URL = "http://localhost:5219";
|
||||
public const string API_URL = "https://api.azaion.com";
|
||||
|
||||
public const string EXTERNAL_INFERENCE_PATH = "azaion-inference.exe";
|
||||
public const string EXTERNAL_GPS_DENIED_FOLDER = "gps-denied";
|
||||
public static readonly string ExternalGpsDeniedPath = Path.Combine(EXTERNAL_GPS_DENIED_FOLDER, "image-matcher.exe");
|
||||
@@ -22,6 +25,13 @@ public class SecurityConstants
|
||||
public const int DEFAULT_RETRY_COUNT = 25;
|
||||
public const int DEFAULT_TIMEOUT_SECONDS = 5;
|
||||
|
||||
# region Cache keys
|
||||
|
||||
public const string CURRENT_USER_CACHE_KEY = "CurrentUser";
|
||||
public const string HARDWARE_INFO_KEY = "HardwareInfo";
|
||||
|
||||
# endregion
|
||||
|
||||
public static readonly SecureAppConfig DefaultSecureAppConfig = new()
|
||||
{
|
||||
InferenceClientConfig = new InferenceClientConfig
|
||||
@@ -29,8 +39,7 @@ public class SecurityConstants
|
||||
ZeroMqHost = DEFAULT_ZMQ_INFERENCE_HOST,
|
||||
ZeroMqPort = DEFAULT_ZMQ_INFERENCE_PORT,
|
||||
OneTryTimeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
|
||||
RetryCount = DEFAULT_RETRY_COUNT,
|
||||
ResourcesFolder = ""
|
||||
RetryCount = DEFAULT_RETRY_COUNT
|
||||
},
|
||||
GpsDeniedClientConfig = new GpsDeniedClientConfig
|
||||
{
|
||||
@@ -38,6 +47,10 @@ public class SecurityConstants
|
||||
ZeroMqPort = DEFAULT_ZMQ_GPS_DENIED_PORT,
|
||||
OneTryTimeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
|
||||
RetryCount = DEFAULT_RETRY_COUNT,
|
||||
},
|
||||
DirectoriesConfig = new DirectoriesConfig
|
||||
{
|
||||
ApiResourcesDirectory = ""
|
||||
}
|
||||
};
|
||||
#endregion ExternalClientsConfig
|
||||
|
||||
@@ -1,26 +1,117 @@
|
||||
using Azaion.CommonSecurity.DTO;
|
||||
using Azaion.CommonSecurity.DTO.Commands;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using Azaion.CommonSecurity.DTO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Azaion.CommonSecurity.Services;
|
||||
|
||||
public interface IAuthProvider
|
||||
public interface IAzaionApi
|
||||
{
|
||||
void Login(ApiCredentials credentials);
|
||||
ApiCredentials Credentials { get; }
|
||||
User CurrentUser { get; }
|
||||
T? Get<T>(string url);
|
||||
Stream GetResource(string filename);
|
||||
}
|
||||
|
||||
public class AuthProvider(IInferenceClient inferenceClient) : IAuthProvider
|
||||
public class AzaionApi(HttpClient client, ICache cache, ApiCredentials credentials, IHardwareService hardwareService) : IAzaionApi
|
||||
{
|
||||
public User CurrentUser { get; private set; } = null!;
|
||||
private string _jwtToken = null!;
|
||||
const string APP_JSON = "application/json";
|
||||
public ApiCredentials Credentials => credentials;
|
||||
|
||||
public void Login(ApiCredentials credentials)
|
||||
public User CurrentUser
|
||||
{
|
||||
inferenceClient.Send(RemoteCommand.Create(CommandType.Login, credentials));
|
||||
var user = inferenceClient.Get<User>();
|
||||
if (user == null)
|
||||
throw new Exception("Can't get user from Auth provider");
|
||||
get
|
||||
{
|
||||
var user = cache.GetFromCache(SecurityConstants.CURRENT_USER_CACHE_KEY,
|
||||
() => Get<User>("currentUser"));
|
||||
if (user == null)
|
||||
throw new Exception("Can't get current user");
|
||||
|
||||
CurrentUser = user;
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private HttpResponseMessage Send(HttpRequestMessage request, CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_jwtToken))
|
||||
Authorize();
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _jwtToken);
|
||||
var response = client.Send(request);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
Authorize();
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _jwtToken);
|
||||
response = client.Send(request);
|
||||
}
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
return response;
|
||||
|
||||
var stream = response.Content.ReadAsStream();
|
||||
var content = new StreamReader(stream).ReadToEnd();
|
||||
if (response.StatusCode == HttpStatusCode.Conflict)
|
||||
{
|
||||
var result = JsonConvert.DeserializeObject<BusinessExceptionDto>(content);
|
||||
throw new Exception($"Failed: {response.StatusCode}! Error Code: {result.ErrorCode}. Message: {result.Message}");
|
||||
}
|
||||
throw new Exception($"Failed: {response.StatusCode}! Result: {content}");
|
||||
}
|
||||
|
||||
public Stream GetResource(string filename)
|
||||
{
|
||||
var hardware = cache.GetFromCache(SecurityConstants.HARDWARE_INFO_KEY, hardwareService.GetHardware);
|
||||
|
||||
var response = Send(new HttpRequestMessage(HttpMethod.Post, $"/resources/get/{credentials.Folder}")
|
||||
{
|
||||
Content = new StringContent(JsonConvert.SerializeObject(new { filename, credentials.Password, hardware }), Encoding.UTF8, APP_JSON)
|
||||
});
|
||||
return response.Content.ReadAsStream();
|
||||
}
|
||||
|
||||
private void Authorize()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(credentials.Email) || credentials.Password.Length == 0)
|
||||
throw new Exception("Email or password is empty! Please do EnterCredentials first!");
|
||||
|
||||
var payload = new
|
||||
{
|
||||
email = credentials.Email,
|
||||
password = credentials.Password
|
||||
};
|
||||
var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, APP_JSON);
|
||||
var message = new HttpRequestMessage(HttpMethod.Post, "login") { Content = content };
|
||||
var response = client.Send(message);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new Exception($"EnterCredentials failed: {response.StatusCode}");
|
||||
|
||||
var stream = response.Content.ReadAsStream();
|
||||
var json = new StreamReader(stream).ReadToEnd();
|
||||
var result = JsonConvert.DeserializeObject<LoginResponse>(json);
|
||||
|
||||
if (string.IsNullOrEmpty(result?.Token))
|
||||
throw new Exception("JWT Token not found in response");
|
||||
|
||||
_jwtToken = result.Token;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public T? Get<T>(string url)
|
||||
{
|
||||
var response = Send(new HttpRequestMessage(HttpMethod.Get, url));
|
||||
var stream = response.Content.ReadAsStream();
|
||||
var json = new StreamReader(stream).ReadToEnd();
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using LazyCache;
|
||||
|
||||
namespace Azaion.CommonSecurity.Services;
|
||||
|
||||
public interface ICache
|
||||
{
|
||||
T GetFromCache<T>(string key, Func<T> fetchFunc, TimeSpan? expiration = null);
|
||||
void Invalidate(string key);
|
||||
}
|
||||
|
||||
public class MemoryCache : ICache
|
||||
{
|
||||
private readonly IAppCache _cache = new CachingService();
|
||||
|
||||
public T GetFromCache<T>(string key, Func<T> fetchFunc, TimeSpan? expiration = null)
|
||||
{
|
||||
expiration ??= TimeSpan.FromHours(4);
|
||||
return _cache.GetOrAdd(key, entry =>
|
||||
{
|
||||
var result = fetchFunc();
|
||||
entry.AbsoluteExpirationRelativeToNow = expiration;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public void Invalidate(string key) => _cache.Remove(key);
|
||||
}
|
||||
@@ -38,29 +38,20 @@ public class HardwareService : IHardwareService
|
||||
.Replace("Name=", "")
|
||||
.Replace(" ", " ")
|
||||
.Trim()
|
||||
.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries);
|
||||
.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.ToArray();
|
||||
|
||||
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";
|
||||
}
|
||||
if (lines.Length < 3)
|
||||
throw new Exception("Can't get hardware info");
|
||||
|
||||
var macAddress = MacAddress();
|
||||
var hardwareInfo = new HardwareInfo
|
||||
{
|
||||
Memory = memoryStr,
|
||||
CPU = lines.Length > 1 && string.IsNullOrEmpty(lines[1])
|
||||
? "Unknown CPU"
|
||||
: lines[1].Trim(),
|
||||
GPU = lines.Length > 2 && string.IsNullOrEmpty(lines[2])
|
||||
? "Unknown GPU"
|
||||
: lines[2],
|
||||
MacAddress = macAddress
|
||||
CPU = lines[0],
|
||||
GPU = lines[1],
|
||||
Memory = lines[2],
|
||||
MacAddress = GetMacAddress()
|
||||
};
|
||||
hardwareInfo.Hash = ToHash($"Az|{hardwareInfo.CPU}|{hardwareInfo.GPU}|{macAddress}");
|
||||
return hardwareInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -70,7 +61,7 @@ public class HardwareService : IHardwareService
|
||||
}
|
||||
}
|
||||
|
||||
private string MacAddress()
|
||||
private string GetMacAddress()
|
||||
{
|
||||
var macAddress = NetworkInterface
|
||||
.GetAllNetworkInterfaces()
|
||||
|
||||
@@ -71,9 +71,6 @@ public class InferenceClient : IInferenceClient
|
||||
_dealer.SendFrame(MessagePackSerializer.Serialize(command));
|
||||
}
|
||||
|
||||
public void SendString(string text) =>
|
||||
Send(new RemoteCommand(CommandType.Load, MessagePackSerializer.Serialize(text)));
|
||||
|
||||
public T? Get<T>(int retries = 24, int tryTimeoutSeconds = 5, CancellationToken ct = default) where T : class
|
||||
{
|
||||
var bytes = GetBytes(retries, tryTimeoutSeconds, ct);
|
||||
@@ -83,8 +80,9 @@ public class InferenceClient : IInferenceClient
|
||||
public byte[]? GetBytes(int retries = 24, int tryTimeoutSeconds = 5, CancellationToken ct = default)
|
||||
{
|
||||
var tryNum = 0;
|
||||
while (!ct.IsCancellationRequested && tryNum++ < retries)
|
||||
while (!ct.IsCancellationRequested && tryNum < retries)
|
||||
{
|
||||
tryNum++;
|
||||
if (!_dealer.TryReceiveFrameBytes(TimeSpan.FromSeconds(tryTimeoutSeconds), out var bytes))
|
||||
continue;
|
||||
|
||||
@@ -92,7 +90,7 @@ public class InferenceClient : IInferenceClient
|
||||
}
|
||||
|
||||
if (!ct.IsCancellationRequested)
|
||||
throw new Exception($"Unable to get bytes after {tryNum} retries, {tryTimeoutSeconds} seconds each");
|
||||
throw new Exception($"Unable to get bytes after {tryNum - 1} retries, {tryTimeoutSeconds} seconds each");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public class ResourceLoader([FromKeyedServices(SecurityConstants.EXTERNAL_INFERE
|
||||
public MemoryStream LoadFile(string fileName, string? folder = null)
|
||||
{
|
||||
inferenceClient.Send(RemoteCommand.Create(CommandType.Load, new LoadFileData(fileName, folder)));
|
||||
var bytes = inferenceClient.GetBytes();
|
||||
var bytes = inferenceClient.GetBytes(2, 3);
|
||||
if (bytes == null)
|
||||
throw new Exception($"Unable to receive {fileName}");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user