Files
AdPlatform-Server/Management/Controllers/OnboardingController.cs
2026-02-03 15:04:37 -08:00

115 lines
3.8 KiB
C#

using Management.Data;
using Management.Security;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
namespace Management.Controllers;
/// <summary>
/// Onboarding endpoints for new user/client registration.
/// Requires JWT authentication (user may not have session yet).
///
/// ENDPOINTS:
/// GET /api/onboarding/status - Check registration status
/// POST /api/onboarding/register - Register new organization
/// </summary>
[ApiController]
[Route("api/onboarding")]
public sealed class OnboardingController : ControllerBase
{
private readonly SqlService _sql;
private readonly ClientContext _client;
private readonly ILogger<OnboardingController> _log;
public OnboardingController(SqlService sql, ClientContext client, ILogger<OnboardingController> log)
{
_sql = sql;
_client = client;
_log = log;
}
/// <summary>
/// Check registration status for authenticated user.
/// </summary>
[HttpGet("status")]
public async Task<IActionResult> Status(CancellationToken ct)
{
if (!_client.IsAuthenticated)
return Unauthorized(new { ok = false, error = "Valid Entra authentication required" });
var rqst = JsonSerializer.Serialize(new
{
provider = "EntraExternalId",
subject = _client.ClientId,
email = _client.Email
});
try
{
var resp = await _sql.ExecProcAsync("dbo.spOnboarding", "status", rqst, ct: ct);
if (string.IsNullOrWhiteSpace(resp))
return StatusCode(500, new { ok = false, error = "Service unavailable" });
return Content(resp, "application/json");
}
catch (Exception ex)
{
_log.LogError(ex, "[Onboarding] Status error");
return StatusCode(500, new { ok = false, error = "Status check failed", detail = ex.Message });
}
}
/// <summary>
/// Register a new organization.
/// </summary>
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] RegisterRequest request, CancellationToken ct)
{
if (!_client.IsAuthenticated)
return Unauthorized(new { ok = false, error = "Valid Entra authentication required" });
if (string.IsNullOrWhiteSpace(request?.ClientName))
return BadRequest(new { ok = false, error = "clientName is required" });
_log.LogWarning("[Onboarding] Register | Subject={Subject} ClientName={ClientName}",
_client.ClientId, request.ClientName);
var rqst = JsonSerializer.Serialize(new
{
provider = "EntraExternalId",
subject = _client.ClientId,
email = _client.Email,
displayName = _client.ClientName,
clientName = request.ClientName.Trim()
});
try
{
var resp = await _sql.ExecProcAsync("dbo.spOnboarding", "register", rqst, ct: ct);
if (string.IsNullOrWhiteSpace(resp))
return StatusCode(500, new { ok = false, error = "Registration service unavailable" });
using var doc = JsonDocument.Parse(resp);
var root = doc.RootElement;
if (root.TryGetProperty("ok", out var okProp) && okProp.GetBoolean())
return Content(resp, "application/json");
var error = root.TryGetProperty("error", out var errProp) ? errProp.GetString() : "Registration failed";
return BadRequest(new { ok = false, error });
}
catch (Exception ex)
{
_log.LogError(ex, "[Onboarding] Register error");
return StatusCode(500, new { ok = false, error = "Registration failed", detail = ex.Message });
}
}
}
public sealed class RegisterRequest
{
public string? ClientName { get; set; }
}