Files
S8n.MySolutionTemplate/MyCompany.MyProject.BackendApi/Program.cs
2026-02-11 02:12:03 +03:00

280 lines
8.5 KiB
C#

using System.Reflection;
using System.Text.Json;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
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", async (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);
}
Console.Error.WriteLine($"Invocation result type: {result?.GetType()}");
// If method returns a Task, await it
if (result is Task taskResult)
{
Console.Error.WriteLine($"Detected Task, awaiting...");
await taskResult.ConfigureAwait(false);
Console.Error.WriteLine($"Task completed, status: {taskResult.Status}");
// If it's a Task<T>, get the result via reflection
var resultType = taskResult.GetType();
Console.Error.WriteLine($"Task result type: {resultType}");
if (typeof(Task).IsAssignableFrom(resultType))
{
Console.Error.WriteLine($"Generic Task detected");
// Get the Result property
var resultProperty = resultType.GetProperty("Result");
if (resultProperty != null)
{
result = resultProperty.GetValue(taskResult);
Console.Error.WriteLine($"Result value: {result}");
Console.Error.WriteLine($"Result value type: {result?.GetType()}");
}
else
{
Console.Error.WriteLine($"Result property not found");
result = null;
}
}
else
{
// Task (non-generic) returns nothing
Console.Error.WriteLine($"Non-generic Task, setting result to null");
result = null;
}
}
Console.Error.WriteLine($"Final result: {result}");
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;
}