242 lines
6.9 KiB
C#
242 lines
6.9 KiB
C#
using System.Reflection;
|
|
using System.Text.Json;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Scalar.AspNetCore;
|
|
|
|
// Ensure S8n.Components.Packages assembly is loaded
|
|
_ = typeof(S8n.Components.Basics.Calculator).Assembly;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
// Add services to the container.
|
|
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
|
builder.Services.AddOpenApi();
|
|
|
|
var app = builder.Build();
|
|
|
|
// Configure the HTTP request pipeline.
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.MapOpenApi();
|
|
app.MapScalarApiReference();
|
|
}
|
|
|
|
app.UseHttpsRedirection();
|
|
|
|
var summaries = new[]
|
|
{
|
|
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
|
};
|
|
|
|
app.MapGet("/weatherforecast", () =>
|
|
{
|
|
var forecast = Enumerable.Range(1, 5).Select(index =>
|
|
new WeatherForecast
|
|
(
|
|
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
|
Random.Shared.Next(-20, 55),
|
|
summaries[Random.Shared.Next(summaries.Length)]
|
|
))
|
|
.ToArray();
|
|
return forecast;
|
|
})
|
|
.WithName("GetWeatherForecast");
|
|
|
|
// Component registry endpoint
|
|
app.MapGet("/api/components", () =>
|
|
{
|
|
var components = new List<ComponentDefinition>();
|
|
var componentsPath = Path.Combine(Directory.GetCurrentDirectory(), "..", "components");
|
|
|
|
if (!Directory.Exists(componentsPath))
|
|
{
|
|
// Fallback to absolute path from workspace root
|
|
componentsPath = Path.Combine(Directory.GetCurrentDirectory(), "components");
|
|
}
|
|
|
|
if (Directory.Exists(componentsPath))
|
|
{
|
|
var jsonFiles = Directory.GetFiles(componentsPath, "*.json", SearchOption.AllDirectories);
|
|
foreach (var file in jsonFiles)
|
|
{
|
|
try
|
|
{
|
|
var json = File.ReadAllText(file);
|
|
var component = JsonSerializer.Deserialize<ComponentDefinition>(json, new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true
|
|
});
|
|
if (component != null)
|
|
{
|
|
components.Add(component);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Log error but continue
|
|
Console.WriteLine($"Error reading component file {file}: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
return Results.Ok(components);
|
|
})
|
|
.WithName("GetComponents");
|
|
|
|
// Runtime execution endpoint
|
|
app.MapPost("/api/runtime/execute", (RuntimeRequest request) =>
|
|
{
|
|
try
|
|
{
|
|
// Get the type from the class name
|
|
var type = Type.GetType(request.ClassName);
|
|
if (type == null)
|
|
{
|
|
// Try to find in loaded assemblies
|
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
type = assembly.GetType(request.ClassName);
|
|
if (type != null) break;
|
|
}
|
|
}
|
|
|
|
if (type == null)
|
|
{
|
|
return Results.BadRequest(new RuntimeResponse
|
|
{
|
|
Error = $"Class not found: {request.ClassName}"
|
|
});
|
|
}
|
|
|
|
// Create instance
|
|
var instance = Activator.CreateInstance(type);
|
|
if (instance == null)
|
|
{
|
|
return Results.BadRequest(new RuntimeResponse
|
|
{
|
|
Error = $"Failed to create instance of: {request.ClassName}"
|
|
});
|
|
}
|
|
|
|
// Get the method
|
|
var method = type.GetMethod(request.MethodName);
|
|
if (method == null)
|
|
{
|
|
return Results.BadRequest(new RuntimeResponse
|
|
{
|
|
Error = $"Method not found: {request.MethodName}"
|
|
});
|
|
}
|
|
|
|
// Parse inputs and invoke method
|
|
var inputs = request.Inputs as JsonElement?;
|
|
object? result;
|
|
|
|
if (inputs.HasValue)
|
|
{
|
|
// Get method parameters
|
|
var parameters = method.GetParameters();
|
|
var args = new List<object?>();
|
|
|
|
foreach (var param in parameters)
|
|
{
|
|
if (inputs.Value.TryGetProperty(param.Name!, out var property))
|
|
{
|
|
var value = JsonSerializer.Deserialize(property.GetRawText(), param.ParameterType);
|
|
args.Add(value);
|
|
}
|
|
else if (param.IsOptional)
|
|
{
|
|
args.Add(param.DefaultValue);
|
|
}
|
|
else
|
|
{
|
|
return Results.BadRequest(new RuntimeResponse
|
|
{
|
|
Error = $"Missing required parameter: {param.Name}"
|
|
});
|
|
}
|
|
}
|
|
|
|
result = method.Invoke(instance, args.ToArray());
|
|
}
|
|
else
|
|
{
|
|
result = method.Invoke(instance, null);
|
|
}
|
|
|
|
return Results.Ok(new RuntimeResponse
|
|
{
|
|
Outputs = result
|
|
});
|
|
}
|
|
catch (TargetInvocationException tie) when (tie.InnerException != null)
|
|
{
|
|
return Results.BadRequest(new RuntimeResponse
|
|
{
|
|
Error = tie.InnerException.Message
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Results.BadRequest(new RuntimeResponse
|
|
{
|
|
Error = ex.Message
|
|
});
|
|
}
|
|
})
|
|
.WithName("ExecuteRuntime");
|
|
|
|
app.Run();
|
|
|
|
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
|
{
|
|
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
}
|
|
|
|
record RuntimeRequest
|
|
{
|
|
public string ClassName { get; set; } = string.Empty;
|
|
public string MethodName { get; set; } = string.Empty;
|
|
public object? Inputs { get; set; }
|
|
}
|
|
|
|
record RuntimeResponse
|
|
{
|
|
public object? Outputs { get; set; }
|
|
public string? Error { get; set; }
|
|
}
|
|
|
|
record ComponentDefinition
|
|
{
|
|
public string Code { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Description { get; set; } = string.Empty;
|
|
public string Icon { get; set; } = string.Empty;
|
|
public List<ComponentInput> Inputs { get; set; } = new();
|
|
public List<ComponentOutput> Outputs { get; set; } = new();
|
|
public string Source { get; set; } = string.Empty;
|
|
public string Class { get; set; } = string.Empty;
|
|
public List<string> Methods { get; set; } = new();
|
|
public string Gui { get; set; } = string.Empty;
|
|
public string Category { get; set; } = string.Empty;
|
|
public List<string> Tags { get; set; } = new();
|
|
}
|
|
|
|
record ComponentInput
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Type { get; set; } = string.Empty;
|
|
public string Description { get; set; } = string.Empty;
|
|
public List<string>? Enum { get; set; }
|
|
public bool Required { get; set; } = true;
|
|
}
|
|
|
|
record ComponentOutput
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string Type { get; set; } = string.Empty;
|
|
public string Description { get; set; } = string.Empty;
|
|
}
|