Make it possible to store notes for instances

This commit is contained in:
Gervasio Marchand 2022-12-22 18:38:51 -03:00
parent 601c261e47
commit 115a0f2a3f
No known key found for this signature in database
GPG Key ID: B7736CB188DD0A38
9 changed files with 119 additions and 10 deletions

View File

@ -5,11 +5,11 @@ using Spectre.Console.Cli;
namespace FakeRelay.Cli.Commands;
public class AddInstanceCommand : ConfigEnabledAsyncCommand<InstanceSettings>
public class AddInstanceCommand : ConfigEnabledAsyncCommand<AddInstanceSettings>
{
public override async Task<int> ExecuteAsync(CommandContext context, InstanceSettings settings)
public override async Task<int> ExecuteAsync(CommandContext context, AddInstanceSettings settings)
{
var token = await ApiKeysHelper.AddTokenForHostAsync(settings.Host);
var token = await ApiKeysHelper.AddTokenForHostAsync(settings.Host, settings.Notes);
AnsiConsole.Markup($"[green]Key generated for {settings.Host}[/]\n");
AnsiConsole.Markup($"[red]{token}[/]\n");
return 0;

View File

@ -0,0 +1,16 @@
using FakeRelay.Cli.Settings;
using FakeRelay.Core.Helpers;
using Spectre.Console;
using Spectre.Console.Cli;
namespace FakeRelay.Cli.Commands;
public class AnnotateInstanceCommand : ConfigEnabledAsyncCommand<AnnotateInstanceSettings>
{
public override async Task<int> ExecuteAsync(CommandContext context, AnnotateInstanceSettings settings)
{
await ApiKeysHelper.UpdateNotesForHostAsync(settings.Host, settings.Notes);
AnsiConsole.Markup("[green]Done![/]\n");
return 0;
}
}

View File

@ -10,11 +10,13 @@ public class ListInstancesCommand : ConfigEnabledAsyncCommand<EmptyCommandSettin
{
var keyToHost = await ApiKeysHelper.GetTokenToHostAsync();
var hostToKeys = keyToHost.ToLookup(d => d.Value, d => d.Key);
var hostToNotes = await ApiKeysHelper.GetHostToNotesAsync();
// Create a table
var table = new Table();
table.AddColumn("Instance");
table.AddColumn("Notes");
table.AddColumn("Key");
foreach (var group in hostToKeys.OrderBy(g => g.Key))
@ -22,7 +24,8 @@ public class ListInstancesCommand : ConfigEnabledAsyncCommand<EmptyCommandSettin
var host = group.Key;
foreach (var key in group)
{
table.AddRow($"[green]{host}[/]", $"[red]{key}[/]");
var notes = hostToNotes.GetValueOrDefault(host) ?? "";
table.AddRow($"[green]{host}[/]", notes , $"[red]{key}[/]");
}
}

View File

@ -9,6 +9,8 @@ app.Configure(config =>
{
instance.AddCommand<AddInstanceCommand>("add")
.WithDescription("Adds an instance to the relay and generates a key.");
instance.AddCommand<AnnotateInstanceCommand>("annotate")
.WithDescription("Adds or updates the notes associated with the instance.");
instance.AddCommand<UpdateInstanceCommand>("update")
.WithDescription("Generates a new key for the instance. The old one can't be used anymore.");
instance.AddCommand<DeleteInstanceCommand>("delete")

View File

@ -0,0 +1,9 @@
using Spectre.Console.Cli;
namespace FakeRelay.Cli.Settings;
public class AddInstanceSettings : InstanceSettings
{
[CommandOption("-n|--notes")]
public string? Notes { get; set; }
}

View File

@ -0,0 +1,16 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace FakeRelay.Cli.Settings;
public class AnnotateInstanceSettings : InstanceSettings
{
[Description("The notes for the instance.")]
[CommandArgument(1, "<NOTES>")]
public string Notes { get; set; }
public AnnotateInstanceSettings()
{
Notes = "";
}
}

View File

@ -5,7 +5,7 @@ namespace FakeRelay.Core;
public class Config
{
public static Config? Instance { get; private set; }
public static Config Instance { get; private set; } = new();
public byte[] PrivateKey { get; }
public string PublicKey { get; }
@ -14,6 +14,12 @@ public class Config
public string ConfigPath { get; }
public string? HomeRedirect { get; }
private Config()
{
PrivateKey = Array.Empty<byte>();
PublicKey = Host = ConfigPath = "";
}
private Config(string publicKey, byte[] privateKey, string host, string configPath, string? homeRedirect)
{
PrivateKey = privateKey;
@ -25,7 +31,7 @@ public class Config
public static void Init(string path)
{
if (Instance != null)
if (Instance.Host != "")
{
return;
}

View File

@ -1,7 +1,13 @@
namespace FakeRelay.Core;
using System.Diagnostics.CodeAnalysis;
namespace FakeRelay.Core;
public static class Extensions
{
public static bool StartsWithCI(this string s, string prefix) =>
s.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
public static bool IsNullOrEmpty([NotNullWhen(returnValue: false)]this string? s) => string.IsNullOrEmpty(s);
public static bool HasValue([NotNullWhen(returnValue: true)]this string? s) => !s.IsNullOrEmpty();
}

View File

@ -7,7 +7,12 @@ namespace FakeRelay.Core.Helpers;
public static class ApiKeysHelper
{
private static string? _tokensFilePath;
private static string TokensFilePath => _tokensFilePath ??= Config.Instance.ConfigPath.Replace(".json", "-tokens.json");
private static string TokensFilePath =>
_tokensFilePath ??= Config.Instance.ConfigPath.Replace(".json", "-tokens.json");
private static string? _notesFilePath;
private static string NotesFilePath =>
_notesFilePath ??= Config.Instance.ConfigPath.Replace(".json", "-notes.json");
public static async Task<ImmutableDictionary<string, string>> GetTokenToHostAsync()
{
@ -21,6 +26,19 @@ public static class ApiKeysHelper
.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase);
}
public static async Task<Dictionary<string, string>> GetHostToNotesAsync()
{
if (!File.Exists(NotesFilePath))
{
return new Dictionary<string, string>();
}
var content = await File.ReadAllTextAsync(NotesFilePath);
return JSON.Deserialize<Dictionary<string, string>>(content)
// set the comparer so that it's case insensitive
.ToDictionary(e => e.Key, e => e.Value, StringComparer.OrdinalIgnoreCase);
}
public static async Task<string> UpdateTokenForHostAsync(string host)
{
var dict = await GetTokenToHostAsync();
@ -38,8 +56,41 @@ public static class ApiKeysHelper
return await AddTokenForHostAsync(host, dict);
}
public static async Task<string> AddTokenForHostAsync(string host) =>
await AddTokenForHostAsync(host, await GetTokenToHostAsync());
public static async Task UpdateNotesForHostAsync(string host, string? notes)
{
var tokensToHost = await GetTokenToHostAsync();
if (!tokensToHost.Values.Contains(host))
{
throw new Exception($"There's no entry for {host}");
}
var notesDict = await GetHostToNotesAsync();
if (notes.HasValue())
{
notesDict[host] = notes;
}
else if (notesDict.ContainsKey(host))
{
notesDict.Remove(host);
}
else
{
return;
}
var content = JSON.Serialize(notesDict);
await File.WriteAllTextAsync(NotesFilePath, content);
}
public static async Task<string> AddTokenForHostAsync(string host, string? notes)
{
if (notes.HasValue())
{
await UpdateNotesForHostAsync(host, notes);
}
return await AddTokenForHostAsync(host, await GetTokenToHostAsync());
}
public static async Task DeleteTokenForHostAsync(string host)
{