2024-04-19 19:19:13 +00:00
|
|
|
using System.Text.Json;
|
|
|
|
using AIStudio.Provider;
|
|
|
|
using Microsoft.JSInterop;
|
|
|
|
|
2024-04-20 15:06:50 +00:00
|
|
|
// ReSharper disable NotAccessedPositionalProperty.Local
|
|
|
|
|
2024-04-19 19:19:13 +00:00
|
|
|
namespace AIStudio.Settings;
|
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// The settings manager.
|
|
|
|
/// </summary>
|
2024-04-19 19:19:13 +00:00
|
|
|
public sealed class SettingsManager
|
|
|
|
{
|
|
|
|
private const string SETTINGS_FILENAME = "settings.json";
|
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// The directory where the configuration files are stored.
|
|
|
|
/// </summary>
|
2024-04-19 19:19:13 +00:00
|
|
|
public static string? ConfigDirectory { get; set; }
|
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// The directory where the data files are stored.
|
|
|
|
/// </summary>
|
2024-04-19 19:19:13 +00:00
|
|
|
public static string? DataDirectory { get; set; }
|
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// The configuration data.
|
|
|
|
/// </summary>
|
2024-04-20 15:06:50 +00:00
|
|
|
public Data ConfigurationData { get; private set; } = new();
|
|
|
|
|
|
|
|
private bool IsSetUp => !string.IsNullOrWhiteSpace(ConfigDirectory) && !string.IsNullOrWhiteSpace(DataDirectory);
|
|
|
|
|
|
|
|
#region API Key Handling
|
|
|
|
|
2024-04-19 19:19:13 +00:00
|
|
|
private readonly record struct GetSecretRequest(string Destination, string UserName);
|
2024-04-20 15:06:50 +00:00
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <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>
|
2024-04-20 15:06:50 +00:00
|
|
|
public readonly record struct RequestedSecret(bool Success, string Secret, string Issue);
|
2024-04-19 19:19:13 +00:00
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Try to get the API key for the given provider.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="jsRuntime">The JS runtime to access the Rust code.</param>
|
|
|
|
/// <param name="provider">The provider to get the API key for.</param>
|
|
|
|
/// <returns>The requested secret.</returns>
|
2024-04-20 15:06:50 +00:00
|
|
|
public async Task<RequestedSecret> GetAPIKey(IJSRuntime jsRuntime, IProvider provider) => await jsRuntime.InvokeAsync<RequestedSecret>("window.__TAURI__.invoke", "get_secret", new GetSecretRequest($"provider::{provider.Id}::{provider.InstanceName}::api_key", Environment.UserName));
|
2024-04-19 19:19:13 +00:00
|
|
|
|
|
|
|
private readonly record struct StoreSecretRequest(string Destination, string UserName, string Secret);
|
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Data structure for storing a secret response.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="Success">True, when the secret was successfully stored.</param>
|
|
|
|
/// <param name="Issue">The issue, when the secret could not be stored.</param>
|
2024-04-20 15:06:50 +00:00
|
|
|
public readonly record struct StoreSecretResponse(bool Success, string Issue);
|
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Try to store the API key for the given provider.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="jsRuntime">The JS runtime to access the Rust code.</param>
|
|
|
|
/// <param name="provider">The provider to store the API key for.</param>
|
|
|
|
/// <param name="key">The API key to store.</param>
|
|
|
|
/// <returns>The store secret response.</returns>
|
2024-04-20 15:06:50 +00:00
|
|
|
public async Task<StoreSecretResponse> SetAPIKey(IJSRuntime jsRuntime, IProvider provider, string key) => await jsRuntime.InvokeAsync<StoreSecretResponse>("window.__TAURI__.invoke", "store_secret", new StoreSecretRequest($"provider::{provider.Id}::{provider.InstanceName}::api_key", Environment.UserName, key));
|
|
|
|
|
|
|
|
private readonly record struct DeleteSecretRequest(string Destination, string UserName);
|
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Data structure for deleting a secret response.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="Success">True, when the secret was successfully deleted.</param>
|
|
|
|
/// <param name="Issue">The issue, when the secret could not be deleted.</param>
|
2024-04-20 15:06:50 +00:00
|
|
|
public readonly record struct DeleteSecretResponse(bool Success, string Issue);
|
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Tries to delete the API key for the given provider.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="jsRuntime">The JS runtime to access the Rust code.</param>
|
|
|
|
/// <param name="provider">The provider to delete the API key for.</param>
|
|
|
|
/// <returns>The delete secret response.</returns>
|
2024-04-20 15:06:50 +00:00
|
|
|
public async Task<DeleteSecretResponse> DeleteAPIKey(IJSRuntime jsRuntime, IProvider provider) => await jsRuntime.InvokeAsync<DeleteSecretResponse>("window.__TAURI__.invoke", "delete_secret", new DeleteSecretRequest($"provider::{provider.Id}::{provider.InstanceName}::api_key", Environment.UserName));
|
2024-04-19 19:19:13 +00:00
|
|
|
|
2024-04-20 15:06:50 +00:00
|
|
|
#endregion
|
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Loads the settings from the file system.
|
|
|
|
/// </summary>
|
2024-04-20 15:06:50 +00:00
|
|
|
public async Task LoadSettings()
|
2024-04-19 19:19:13 +00:00
|
|
|
{
|
|
|
|
if(!this.IsSetUp)
|
2024-04-20 15:06:50 +00:00
|
|
|
return;
|
2024-04-19 19:19:13 +00:00
|
|
|
|
|
|
|
var settingsPath = Path.Combine(ConfigDirectory!, SETTINGS_FILENAME);
|
|
|
|
if(!File.Exists(settingsPath))
|
2024-04-20 15:06:50 +00:00
|
|
|
return;
|
2024-04-19 19:19:13 +00:00
|
|
|
|
|
|
|
var settingsJson = await File.ReadAllTextAsync(settingsPath);
|
2024-04-20 15:06:50 +00:00
|
|
|
var loadedConfiguration = JsonSerializer.Deserialize<Data>(settingsJson);
|
|
|
|
if(loadedConfiguration is null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.ConfigurationData = loadedConfiguration;
|
2024-04-19 19:19:13 +00:00
|
|
|
}
|
2024-04-20 15:06:50 +00:00
|
|
|
|
2024-05-04 08:55:00 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Stores the settings to the file system.
|
|
|
|
/// </summary>
|
2024-04-20 15:06:50 +00:00
|
|
|
public async Task StoreSettings()
|
2024-04-19 19:19:13 +00:00
|
|
|
{
|
|
|
|
if(!this.IsSetUp)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var settingsPath = Path.Combine(ConfigDirectory!, SETTINGS_FILENAME);
|
2024-04-20 15:06:50 +00:00
|
|
|
if(!Directory.Exists(ConfigDirectory))
|
|
|
|
Directory.CreateDirectory(ConfigDirectory!);
|
|
|
|
|
|
|
|
var settingsJson = JsonSerializer.Serialize(this.ConfigurationData);
|
2024-04-19 19:19:13 +00:00
|
|
|
await File.WriteAllTextAsync(settingsPath, settingsJson);
|
|
|
|
}
|
|
|
|
}
|