almost done
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using EmbedIO;
|
||||
using EmbedIO.Routing;
|
||||
using EmbedIO.WebApi;
|
||||
using Humanizer;
|
||||
using Torch.Views;
|
||||
using TorchRemote.Models.Responses;
|
||||
using TorchRemote.Models.Shared.Settings;
|
||||
using TorchRemote.Plugin.Abstractions.Controllers;
|
||||
@@ -17,27 +17,19 @@ public class SettingsController : WebApiController, ISettingsController
|
||||
{
|
||||
private const string RootPath = "/settings";
|
||||
|
||||
[Route(HttpVerbs.Get, RootPath)]
|
||||
public IEnumerable<string> GetAllSettings()
|
||||
{
|
||||
return Statics.SettingManager.Settings.Keys;
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, $"{RootPath}/{{fullName}}")]
|
||||
public SettingInfoResponse Get(string fullName)
|
||||
{
|
||||
if (!Statics.SettingManager.Settings.TryGetValue(fullName, out var setting))
|
||||
throw HttpException.NotFound($"Setting with fullName {fullName} not found", fullName);
|
||||
|
||||
return new(StringExtensions.Humanize(setting.Name), setting.Schema, setting
|
||||
.Type.GetProperties(
|
||||
BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(b => setting.IncludeDisplayOnly &&
|
||||
b.HasAttribute<DisplayAttribute>())
|
||||
.Select(b =>
|
||||
{
|
||||
var attr = b.GetCustomAttribute<DisplayAttribute>();
|
||||
return new SettingPropertyInfo(
|
||||
attr?.Name ?? StringExtensions.Humanize(b.Name),
|
||||
Statics.SerializerOptions
|
||||
.PropertyNamingPolicy!.ConvertName(b.Name),
|
||||
attr?.Description,
|
||||
attr?.Order is 0 or null ? null : attr.Order);
|
||||
}).ToArray());
|
||||
return new(StringExtensions.Humanize(setting.Name), setting.Schema);
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, $"{RootPath}/{{fullName}}/values")]
|
||||
@@ -59,9 +51,12 @@ public class SettingsController : WebApiController, ISettingsController
|
||||
|
||||
return type switch
|
||||
{
|
||||
_ when type == typeof(int) || type == typeof(int?) => (PropertyBase)new IntegerProperty(name, (int?)value),
|
||||
_ when type == typeof(int) || type == typeof(int?) => (PropertyBase)new IntegerProperty(
|
||||
name, (int?)value),
|
||||
_ when type == typeof(bool) || type == typeof(bool?) => new BooleanProperty(name, (bool?)value),
|
||||
_ when type == typeof(string) => new StringProperty(name, (string?)value),
|
||||
_ when type.IsPrimitive => new NumberProperty(name, value is null ? null : (double?)Convert.ChangeType(value, typeof(double))),
|
||||
_ when type.IsPrimitive => new NumberProperty(
|
||||
name, value is null ? null : (double?)Convert.ChangeType(value, typeof(double))),
|
||||
_ when type == typeof(DateTime) || type == typeof(DateTime?) => new DateTimeProperty(
|
||||
name, (DateTime?)value),
|
||||
_ when type == typeof(TimeSpan) || type == typeof(TimeSpan?) => new DurationProperty(
|
||||
@@ -71,6 +66,8 @@ public class SettingsController : WebApiController, ISettingsController
|
||||
_ when type == typeof(Guid) || type == typeof(Guid?) => new UuidProperty(
|
||||
name, (Guid?)value),
|
||||
_ when type == typeof(Uri) => new UriProperty(name, (Uri?)value),
|
||||
_ when typeof(ICollection).IsAssignableFrom(type) =>
|
||||
new ArrayProperty(name, JsonSerializer.SerializeToElement(value, type, Statics.SerializerOptions)),
|
||||
_ when type.IsClass => new ObjectProperty(
|
||||
name, JsonSerializer.SerializeToElement(value, type, Statics.SerializerOptions)),
|
||||
_ => throw HttpException.NotFound("Property type not found", name),
|
||||
@@ -147,6 +144,9 @@ public class SettingsController : WebApiController, ISettingsController
|
||||
case ObjectProperty objectProperty when type.IsClass:
|
||||
propInfo.SetValue(instance, objectProperty.Value.Deserialize(type, Statics.SerializerOptions));
|
||||
break;
|
||||
case ArrayProperty arrayProperty when typeof(ICollection).IsAssignableFrom(type):
|
||||
propInfo.SetValue(instance, arrayProperty.Value.Deserialize(type, Statics.SerializerOptions));
|
||||
break;
|
||||
case StringProperty stringProperty when type == typeof(string):
|
||||
propInfo.SetValue(instance, stringProperty.Value);
|
||||
break;
|
||||
@@ -168,4 +168,4 @@ public class SettingsController : WebApiController, ISettingsController
|
||||
return true;
|
||||
}).Count();
|
||||
}
|
||||
}
|
||||
}
|
@@ -51,17 +51,17 @@ public class ApiServerManager : Manager
|
||||
Statics.ChatModule = chatModule;
|
||||
|
||||
_server = new WebServer(o => o
|
||||
.WithUrlPrefix(_config.Listener.UrlPrefix)
|
||||
.WithMicrosoftHttpListener())
|
||||
.WithLocalSessionManager()
|
||||
.WithModule(apiModule
|
||||
.WithController<ServerController>()
|
||||
.WithController<SettingsController>()
|
||||
.WithController<WorldsController>()
|
||||
.WithController<ChatController>())
|
||||
.WithModule(new LogsModule("/api/live/logs", true))
|
||||
.WithModule(chatModule)
|
||||
.WithBearerToken("/api", new SymmetricSecurityKey(Convert.FromBase64String(_config.SecurityKey)), new BasicAuthorizationServerProvider());
|
||||
.WithUrlPrefix(_config.Listener.UrlPrefix)
|
||||
.WithMicrosoftHttpListener())
|
||||
.WithLocalSessionManager()
|
||||
.WithModule(apiModule
|
||||
.WithController<ServerController>()
|
||||
.WithController<SettingsController>()
|
||||
.WithController<WorldsController>()
|
||||
.WithController<ChatController>())
|
||||
.WithModule(new LogsModule("/api/live/logs", true))
|
||||
.WithModule(chatModule)
|
||||
.WithBearerToken("/api", new SymmetricSecurityKey(Convert.FromBase64String(_config.SecurityKey)), new BasicAuthorizationServerProvider());
|
||||
}
|
||||
|
||||
public override void Attach()
|
||||
@@ -92,4 +92,4 @@ public class ApiServerManager : Manager
|
||||
|
||||
return Convert.ToBase64String(aes.Key);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,12 +1,16 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Json.Schema;
|
||||
using Json.Schema.Generation;
|
||||
using NLog;
|
||||
using Torch;
|
||||
using Torch.API;
|
||||
using Torch.Managers;
|
||||
using Torch.Server;
|
||||
using Torch.Server.Managers;
|
||||
using Torch.Server.ViewModels;
|
||||
using TorchRemote.Plugin.Refiners;
|
||||
using TorchRemote.Plugin.Utils;
|
||||
|
||||
namespace TorchRemote.Plugin.Managers;
|
||||
@@ -17,6 +21,8 @@ public class SettingManager : Manager
|
||||
|
||||
[Dependency]
|
||||
private readonly InstanceManager _instanceManager = null!;
|
||||
[Dependency]
|
||||
private readonly PluginManager _pluginManager = null!;
|
||||
|
||||
public SettingManager(ITorchBase torchInstance) : base(torchInstance)
|
||||
{
|
||||
@@ -26,25 +32,66 @@ public class SettingManager : Manager
|
||||
{
|
||||
base.Attach();
|
||||
_instanceManager.InstanceLoaded += InstanceManagerOnInstanceLoaded;
|
||||
|
||||
RegisterSetting("Torch Config", Torch.Config, typeof(TorchConfig));
|
||||
|
||||
foreach (var plugin in _pluginManager.Plugins.Values)
|
||||
{
|
||||
var type = plugin.GetType();
|
||||
object persistentInstance;
|
||||
|
||||
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
|
||||
bool IsSuitable(MemberInfo m, Type t) =>
|
||||
t.IsGenericType && typeof(Persistent<>).IsAssignableFrom(t.GetGenericTypeDefinition()) &&
|
||||
(m.Name.Contains(
|
||||
"config", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
m.Name.Contains(
|
||||
"cfg", StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
var fields = type.GetFields(flags).Where(b => IsSuitable(b, b.FieldType)).ToArray();
|
||||
var props = type.GetProperties(flags).Where(b => IsSuitable(b, b.PropertyType)).ToArray();
|
||||
|
||||
if (fields.FirstOrDefault() is { } field)
|
||||
{
|
||||
persistentInstance = field.GetValue(plugin);
|
||||
}
|
||||
else if (props.FirstOrDefault() is { } prop)
|
||||
{
|
||||
persistentInstance = prop.GetValue(plugin);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (persistentInstance is null)
|
||||
continue;
|
||||
|
||||
var persistentType = persistentInstance.GetType();
|
||||
var getter = persistentType.GetProperty("Data")!;
|
||||
|
||||
RegisterSetting(plugin.Name, getter.GetValue(persistentInstance), persistentType.GenericTypeArguments[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void InstanceManagerOnInstanceLoaded(ConfigDedicatedViewModel config)
|
||||
{
|
||||
RegisterSetting(config.SessionSettings, typeof(SessionSettingsViewModel));
|
||||
RegisterSetting("Session Settings", config.SessionSettings, typeof(SessionSettingsViewModel));
|
||||
}
|
||||
|
||||
public void RegisterSetting(object value, Type type, bool includeOnlyDisplay = true)
|
||||
public void RegisterSetting(string name, object value, Type type)
|
||||
{
|
||||
var builder = new JsonSchemaBuilder().FromType(type, new()
|
||||
{
|
||||
PropertyNamingMethod = input => Statics.SerializerOptions.PropertyNamingPolicy!.ConvertName(input)
|
||||
PropertyNamingMethod = input => Statics.SerializerOptions.PropertyNamingPolicy!.ConvertName(input),
|
||||
Refiners = { new DisplayAttributeRefiner() }
|
||||
});
|
||||
|
||||
Settings[type.FullName!] = new(type.Name, type, builder.Build(), value, includeOnlyDisplay);
|
||||
Log.Info("Registered {0} type", type.FullName);
|
||||
Settings[type.FullName!] = new(name, type, builder.Build(), value);
|
||||
Log.Info("Registered {0} type with name {1}", type.FullName, name);
|
||||
}
|
||||
|
||||
public IDictionary<string, Setting> Settings { get; } = new ConcurrentDictionary<string, Setting>();
|
||||
}
|
||||
|
||||
public record Setting(string Name, Type Type, JsonSchema Schema, object Value, bool IncludeDisplayOnly);
|
||||
public record Setting(string Name, Type Type, JsonSchema Schema, object Value);
|
28
TorchRemote.Plugin/Refiners/DisplayAttributeRefiner.cs
Normal file
28
TorchRemote.Plugin/Refiners/DisplayAttributeRefiner.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Json.Schema.Generation;
|
||||
using Json.Schema.Generation.Intents;
|
||||
using Torch.Views;
|
||||
|
||||
namespace TorchRemote.Plugin.Refiners;
|
||||
|
||||
public class DisplayAttributeRefiner : ISchemaRefiner
|
||||
{
|
||||
public bool ShouldRun(SchemaGenerationContextBase context)
|
||||
{
|
||||
return context.GetAttributes().OfType<DisplayAttribute>().Any();
|
||||
}
|
||||
|
||||
public void Run(SchemaGenerationContextBase context)
|
||||
{
|
||||
foreach (var displayAttribute in context.GetAttributes().OfType<DisplayAttribute>())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(displayAttribute.Name))
|
||||
context.Intents.Add(new TitleIntent(displayAttribute.Name));
|
||||
|
||||
if (!string.IsNullOrEmpty(displayAttribute.Description))
|
||||
context.Intents.Add(new DescriptionIntent(displayAttribute.Description));
|
||||
|
||||
if (displayAttribute.ReadOnly)
|
||||
context.Intents.Add(new ReadOnlyIntent(displayAttribute.ReadOnly));
|
||||
}
|
||||
}
|
||||
}
|
@@ -75,7 +75,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EmbedIO" Version="3.5.0" />
|
||||
<PackageReference Include="EmbedIO" Version="3.4.3" />
|
||||
<PackageReference Include="EmbedIO.BearerToken" Version="3.4.2" />
|
||||
<PackageReference Include="Json.More.Net" Version="1.7.1" />
|
||||
<PackageReference Include="JsonPointer.Net" Version="2.2.2" />
|
||||
|
@@ -1 +0,0 @@
|
||||
global using JsonData = TorchRemote.Plugin.Utils.JsonDataAttribute;
|
@@ -2,5 +2,5 @@
|
||||
<PluginManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<Name>TorchRemote.Plugin</Name>
|
||||
<Guid>284017F3-9682-4841-A544-EB04DB8CB9BA</Guid>
|
||||
<Version>v1.0.1</Version>
|
||||
<Version>v1.0.2</Version>
|
||||
</PluginManifest>
|
Reference in New Issue
Block a user