Add WebAPI middleware for multi-instance Roslyn Bridge routing
Add RoslynBridge.WebApi - ASP.NET Core 8.0 middleware that: - Provides a centralized REST API for accessing multiple VS instances - Manages instance registry with discovery by port, solution, or PID - Proxies requests to the appropriate VS instance - Tracks request/response history for debugging - Auto-cleanup of stale instances via background service Features: - Health endpoints: /api/health, /api/health/ping - Roslyn endpoints: /api/roslyn/projects, /api/roslyn/diagnostics, etc. - Instance management: /api/instances (register, heartbeat, unregister) - History tracking: /api/history, /api/history/stats - Swagger UI at root (/) for API documentation - CORS enabled for web applications Services: - InstanceRegistryService: Thread-safe registry of VS instances - HistoryService: In-memory request/response history (max 1000 entries) - InstanceCleanupService: Background service to remove stale instances - RoslynBridgeClient: HTTP client for proxying to VS instances Update RoslynBridge.sln to include RoslynBridge.WebApi project. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
168
RoslynBridge.WebApi/Controllers/InstancesController.cs
Normal file
168
RoslynBridge.WebApi/Controllers/InstancesController.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RoslynBridge.WebApi.Models;
|
||||
using RoslynBridge.WebApi.Services;
|
||||
|
||||
namespace RoslynBridge.WebApi.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller for managing Visual Studio instance registrations
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Produces("application/json")]
|
||||
public class InstancesController : ControllerBase
|
||||
{
|
||||
private readonly IInstanceRegistryService _registryService;
|
||||
private readonly ILogger<InstancesController> _logger;
|
||||
|
||||
public InstancesController(
|
||||
IInstanceRegistryService registryService,
|
||||
ILogger<InstancesController> logger)
|
||||
{
|
||||
_registryService = registryService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a new Visual Studio instance
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance information</param>
|
||||
/// <returns>Registration result</returns>
|
||||
[HttpPost("register")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
public IActionResult Register([FromBody] VSInstanceInfo instance)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_registryService.Register(instance);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
success = true,
|
||||
message = "Instance registered successfully",
|
||||
processId = instance.ProcessId,
|
||||
port = instance.Port
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister a Visual Studio instance
|
||||
/// </summary>
|
||||
/// <param name="processId">Process ID of the instance to unregister</param>
|
||||
/// <returns>Unregistration result</returns>
|
||||
[HttpPost("unregister/{processId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public IActionResult Unregister(int processId)
|
||||
{
|
||||
var removed = _registryService.Unregister(processId);
|
||||
|
||||
if (!removed)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Instance not found" });
|
||||
}
|
||||
|
||||
return Ok(new { success = true, message = "Instance unregistered successfully" });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update heartbeat for a Visual Studio instance
|
||||
/// </summary>
|
||||
/// <param name="processId">Process ID of the instance</param>
|
||||
/// <returns>Heartbeat update result</returns>
|
||||
[HttpPost("heartbeat/{processId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public IActionResult Heartbeat(int processId)
|
||||
{
|
||||
var updated = _registryService.UpdateHeartbeat(processId);
|
||||
|
||||
if (!updated)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Instance not found" });
|
||||
}
|
||||
|
||||
return Ok(new { success = true, message = "Heartbeat updated" });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all registered Visual Studio instances
|
||||
/// </summary>
|
||||
/// <returns>List of registered instances</returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(IEnumerable<VSInstanceInfo>), StatusCodes.Status200OK)]
|
||||
public IActionResult GetAll()
|
||||
{
|
||||
var instances = _registryService.GetAllInstances();
|
||||
return Ok(instances);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get instance by process ID
|
||||
/// </summary>
|
||||
/// <param name="processId">Process ID</param>
|
||||
/// <returns>Instance information</returns>
|
||||
[HttpGet("by-pid/{processId}")]
|
||||
[ProducesResponseType(typeof(VSInstanceInfo), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public IActionResult GetByProcessId(int processId)
|
||||
{
|
||||
var instance = _registryService.GetByProcessId(processId);
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Instance not found" });
|
||||
}
|
||||
|
||||
return Ok(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get instance by solution path
|
||||
/// </summary>
|
||||
/// <param name="solutionPath">Solution file path</param>
|
||||
/// <returns>Instance information</returns>
|
||||
[HttpGet("by-solution")]
|
||||
[ProducesResponseType(typeof(VSInstanceInfo), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public IActionResult GetBySolutionPath([FromQuery] string solutionPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(solutionPath))
|
||||
{
|
||||
return BadRequest(new { success = false, message = "Solution path is required" });
|
||||
}
|
||||
|
||||
var instance = _registryService.GetBySolutionPath(solutionPath);
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
return NotFound(new { success = false, message = "No instance found for this solution" });
|
||||
}
|
||||
|
||||
return Ok(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get instance by port
|
||||
/// </summary>
|
||||
/// <param name="port">Port number</param>
|
||||
/// <returns>Instance information</returns>
|
||||
[HttpGet("by-port/{port}")]
|
||||
[ProducesResponseType(typeof(VSInstanceInfo), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public IActionResult GetByPort(int port)
|
||||
{
|
||||
var instance = _registryService.GetByPort(port);
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
return NotFound(new { success = false, message = "Instance not found" });
|
||||
}
|
||||
|
||||
return Ok(instance);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user