mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-10 16:53:47 +00:00
Migrated the get secret calls from Tauri JS to the runtime API
This commit is contained in:
53 files changed
+299
-243
No files matched your search
@@ -93,20 +93,21 @@ public sealed class Encryption(ILogger<Encryption> logger, byte[] secretPassword
|
||||
public async Task<string> Decrypt(EncryptedText encryptedData)
|
||||
{
|
||||
// Build a memory stream to access the given base64 encoded data:
|
||||
await using var encodedEncryptedStream = new MemoryStream(Encoding.ASCII.GetBytes(encryptedData));
|
||||
await using var encodedEncryptedStream = new MemoryStream(Encoding.ASCII.GetBytes(encryptedData.EncryptedData));
|
||||
|
||||
// Wrap around the base64 decoder stream:
|
||||
await using var base64Stream = new CryptoStream(encodedEncryptedStream, new FromBase64Transform(), CryptoStreamMode.Read);
|
||||
|
||||
// A buffer for the salt's bytes:
|
||||
var readSaltBytes = new byte[16]; // 16 bytes = Guid
|
||||
|
||||
|
||||
// Read the salt's bytes out of the stream:
|
||||
var readBytes = await base64Stream.ReadAsync(readSaltBytes);
|
||||
if(readBytes != 16)
|
||||
var readBytes = 0;
|
||||
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1));
|
||||
while(readBytes < readSaltBytes.Length && !cts.Token.IsCancellationRequested)
|
||||
{
|
||||
logger.LogError($"Read {readBytes} bytes instead of 16 bytes for the salt.");
|
||||
throw new CryptographicException("Failed to read the salt bytes.");
|
||||
readBytes += await base64Stream.ReadAsync(readSaltBytes, readBytes, readSaltBytes.Length - readBytes, cts.Token);
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(60), cts.Token);
|
||||
}
|
||||
|
||||
// Check the salt bytes:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Tools.Rust;
|
||||
|
||||
public readonly record struct GetSecretRequest(
|
||||
string Destination,
|
||||
[property:JsonPropertyName("user_name")] string UserName);
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AIStudio.Tools.Rust;
|
||||
|
||||
/// <summary>
|
||||
/// Data structure for any requested secret.
|
||||
/// </summary>
|
||||
/// <param name="Success">True, when the secret was successfully retrieved.</param>
|
||||
/// <param name="Secret">The secret, e.g., API key.</param>
|
||||
/// <param name="Issue">The issue, when the secret could not be retrieved.</param>
|
||||
public readonly record struct RequestedSecret(bool Success, EncryptedText Secret, string Issue);
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace AIStudio.Tools;
|
||||
namespace AIStudio.Tools.Rust;
|
||||
|
||||
/// <summary>
|
||||
/// The response from the set clipboard operation.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
namespace AIStudio.Tools.Rust;
|
||||
|
||||
/// <summary>
|
||||
/// The response of the update check.
|
||||
@@ -1,19 +1,24 @@
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Tools.Rust;
|
||||
|
||||
// ReSharper disable NotAccessedPositionalProperty.Local
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// Calling Rust functions.
|
||||
/// </summary>
|
||||
public sealed class Rust(string apiPort) : IDisposable
|
||||
public sealed class RustService(string apiPort) : IDisposable
|
||||
{
|
||||
private readonly HttpClient http = new()
|
||||
{
|
||||
BaseAddress = new Uri($"http://127.0.0.1:{apiPort}"),
|
||||
};
|
||||
|
||||
private ILogger<Rust>? logger;
|
||||
private ILogger<RustService>? logger;
|
||||
private Encryption? encryptor;
|
||||
|
||||
public void SetLogger(ILogger<Rust> logService)
|
||||
public void SetLogger(ILogger<RustService> logService)
|
||||
{
|
||||
this.logger = logService;
|
||||
}
|
||||
@@ -87,7 +92,8 @@ public sealed class Rust(string apiPort) : IDisposable
|
||||
var severity = Severity.Error;
|
||||
try
|
||||
{
|
||||
var response = await this.http.PostAsync("/clipboard/set", new StringContent(await text.Encrypt(this.encryptor!)));
|
||||
var encryptedText = await text.Encrypt(this.encryptor!);
|
||||
var response = await this.http.PostAsync("/clipboard/set", new StringContent(encryptedText.EncryptedData));
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to copy the text to the clipboard due to an network error: '{response.StatusCode}'");
|
||||
@@ -136,7 +142,7 @@ public sealed class Rust(string apiPort) : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InstallUpdate(IJSRuntime jsRuntime)
|
||||
public async Task InstallUpdate()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -149,6 +155,28 @@ public sealed class Rust(string apiPort) : IDisposable
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to get the API key for the given provider.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider to get the API key for.</param>
|
||||
/// <returns>The requested secret.</returns>
|
||||
public async Task<RequestedSecret> GetAPIKey(IProvider provider)
|
||||
{
|
||||
var secretRequest = new GetSecretRequest($"provider::{provider.Id}::{provider.InstanceName}::api_key", Environment.UserName);
|
||||
var result = await this.http.PostAsJsonAsync("/secrets/get", secretRequest);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to get the API key for provider '{provider.Id}' due to an API issue: '{result.StatusCode}'");
|
||||
return new RequestedSecret(false, new EncryptedText(string.Empty), "Failed to get the API key due to an API issue.");
|
||||
}
|
||||
|
||||
var secret = await result.Content.ReadFromJsonAsync<RequestedSecret>();
|
||||
if (!secret.Success)
|
||||
this.logger!.LogError($"Failed to get the API key for provider '{provider.Id}': '{secret.Issue}'");
|
||||
|
||||
return secret;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
@@ -6,11 +6,11 @@ namespace AIStudio.Tools.Services;
|
||||
/// Wire up the clipboard service to copy Markdown to the clipboard.
|
||||
/// We use our own Rust-based clipboard service for this.
|
||||
/// </summary>
|
||||
public sealed class MarkdownClipboardService(Rust rust, ISnackbar snackbar) : IMudMarkdownClipboardService
|
||||
public sealed class MarkdownClipboardService(RustService rust, ISnackbar snackbar) : IMudMarkdownClipboardService
|
||||
{
|
||||
private ISnackbar Snackbar { get; } = snackbar;
|
||||
|
||||
private Rust Rust { get; } = rust;
|
||||
private RustService Rust { get; } = rust;
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when the user wants to copy the Markdown to the clipboard.
|
||||
|
||||
@@ -12,11 +12,11 @@ public sealed class UpdateService : BackgroundService, IMessageBusReceiver
|
||||
|
||||
private readonly SettingsManager settingsManager;
|
||||
private readonly MessageBus messageBus;
|
||||
private readonly Rust rust;
|
||||
private readonly RustService rust;
|
||||
|
||||
private TimeSpan updateInterval;
|
||||
|
||||
public UpdateService(MessageBus messageBus, SettingsManager settingsManager, Rust rust)
|
||||
public UpdateService(MessageBus messageBus, SettingsManager settingsManager, RustService rust)
|
||||
{
|
||||
this.settingsManager = settingsManager;
|
||||
this.messageBus = messageBus;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// Model for setting clipboard text.
|
||||
/// </summary>
|
||||
/// <param name="Text">The text to set to the clipboard.</param>
|
||||
public record SetClipboardText(string Text);
|
||||
Reference in new issue
Block a user