Add project files.
This commit is contained in:
92
Management/Controllers/Admin/AdminClientsController.cs
Normal file
92
Management/Controllers/Admin/AdminClientsController.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using Management.Data;
|
||||
using Management.Security;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Management.Controllers.Admin;
|
||||
|
||||
/// <summary>
|
||||
/// Admin endpoints for client (organization) management.
|
||||
/// Requires Admin role.
|
||||
///
|
||||
/// ENDPOINTS:
|
||||
/// GET /api/admin/clients - List clients
|
||||
/// GET /api/admin/clients/{id} - Get client
|
||||
/// POST /api/admin/clients - Create client
|
||||
/// PUT /api/admin/clients/{id} - Update client
|
||||
/// DELETE /api/admin/clients/{id} - Deactivate client
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/admin/clients")]
|
||||
public sealed class AdminClientsController : AdminControllerBase
|
||||
{
|
||||
public AdminClientsController(SqlService sql, ClientContext client, ILogger<AdminClientsController> log)
|
||||
: base(sql, client, log) { }
|
||||
|
||||
/// <summary>
|
||||
/// List all clients with optional filtering.
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public Task<IActionResult> List(
|
||||
[FromQuery] string? status,
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 50,
|
||||
CancellationToken ct = default)
|
||||
=> CallProc("spAdminClients", "list", new { status, page, pageSize }, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Get client by ID.
|
||||
/// </summary>
|
||||
[HttpGet("{clientId}")]
|
||||
public Task<IActionResult> Get(string clientId, CancellationToken ct)
|
||||
=> CallProc("spAdminClients", "get", new { clientId }, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new client.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public Task<IActionResult> Create([FromBody] CreateClientRequest request, CancellationToken ct)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request?.ClientName))
|
||||
return Task.FromResult(ValidationError("clientName is required"));
|
||||
|
||||
Logger.LogWarning("[Admin] CreateClient | Name={Name} | By={User}", request.ClientName, Client.Email);
|
||||
return CallProc("spAdminClients", "create", new { clientName = request.ClientName.Trim() }, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update client.
|
||||
/// </summary>
|
||||
[HttpPut("{clientId}")]
|
||||
public Task<IActionResult> Update(string clientId, [FromBody] UpdateClientRequest request, CancellationToken ct)
|
||||
{
|
||||
Logger.LogWarning("[Admin] UpdateClient | Id={Id} | By={User}", clientId, Client.Email);
|
||||
return CallProc("spAdminClients", "update", new
|
||||
{
|
||||
clientId,
|
||||
clientName = request?.ClientName?.Trim(),
|
||||
status = request?.Status
|
||||
}, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivate client (soft delete).
|
||||
/// </summary>
|
||||
[HttpDelete("{clientId}")]
|
||||
public Task<IActionResult> Delete(string clientId, CancellationToken ct)
|
||||
{
|
||||
Logger.LogWarning("[Admin] DeleteClient | Id={Id} | By={User}", clientId, Client.Email);
|
||||
return CallProc("spAdminClients", "delete", new { clientId }, ct);
|
||||
}
|
||||
}
|
||||
|
||||
// DTOs
|
||||
public sealed class CreateClientRequest
|
||||
{
|
||||
public string? ClientName { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateClientRequest
|
||||
{
|
||||
public string? ClientName { get; set; }
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
58
Management/Controllers/Admin/AdminControllerBase.cs
Normal file
58
Management/Controllers/Admin/AdminControllerBase.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using Management.Data;
|
||||
using Management.Security;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Management.Controllers.Admin;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for admin controllers with shared functionality.
|
||||
/// </summary>
|
||||
public abstract class AdminControllerBase : ControllerBase
|
||||
{
|
||||
protected readonly SqlService Sql;
|
||||
protected readonly ClientContext Client;
|
||||
protected readonly ILogger Logger;
|
||||
|
||||
protected AdminControllerBase(SqlService sql, ClientContext client, ILogger logger)
|
||||
{
|
||||
Sql = sql;
|
||||
Client = client;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute stored procedure and return appropriate IActionResult.
|
||||
/// </summary>
|
||||
protected async Task<IActionResult> CallProc(string proc, string action, object rqst, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = JsonSerializer.Serialize(rqst);
|
||||
var resp = await Sql.ExecProcAsync($"dbo.{proc}", action, json, ct: ct);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(resp))
|
||||
return StatusCode(500, new { ok = false, error = "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() : "Operation failed";
|
||||
return BadRequest(new { ok = false, error });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "[Admin] {Proc}.{Action} error", proc, action);
|
||||
return StatusCode(500, new { ok = false, error = "Operation failed", detail = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return BadRequest for validation failures.
|
||||
/// </summary>
|
||||
protected IActionResult ValidationError(string error)
|
||||
=> BadRequest(new { ok = false, error });
|
||||
}
|
||||
65
Management/Controllers/Admin/AdminSessionsController.cs
Normal file
65
Management/Controllers/Admin/AdminSessionsController.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Management.Data;
|
||||
using Management.Security;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Management.Controllers.Admin;
|
||||
|
||||
/// <summary>
|
||||
/// Admin endpoints for session management.
|
||||
/// Requires Admin role.
|
||||
///
|
||||
/// ENDPOINTS:
|
||||
/// GET /api/admin/sessions - List sessions
|
||||
/// POST /api/admin/sessions/{id}/revoke - Revoke session
|
||||
/// POST /api/admin/users/{id}/revoke-sessions - Revoke all user sessions
|
||||
/// POST /api/admin/sessions/cleanup - Cleanup expired sessions
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/admin/sessions")]
|
||||
public sealed class AdminSessionsController : AdminControllerBase
|
||||
{
|
||||
public AdminSessionsController(SqlService sql, ClientContext client, ILogger<AdminSessionsController> log)
|
||||
: base(sql, client, log) { }
|
||||
|
||||
/// <summary>
|
||||
/// List sessions with optional filtering.
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public Task<IActionResult> List(
|
||||
[FromQuery] string? clientId,
|
||||
[FromQuery] string? userId,
|
||||
[FromQuery] bool activeOnly = true,
|
||||
[FromQuery] int limit = 100,
|
||||
CancellationToken ct = default)
|
||||
=> CallProc("spAdminSessions", "list", new { clientId, userId, activeOnly, limit }, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Revoke a session.
|
||||
/// </summary>
|
||||
[HttpPost("{sessionId}/revoke")]
|
||||
public Task<IActionResult> Revoke(string sessionId, CancellationToken ct)
|
||||
{
|
||||
Logger.LogWarning("[Admin] RevokeSession | SessionId={SessionId} | By={User}", sessionId, Client.Email);
|
||||
return CallProc("spAdminSessions", "revoke", new { sessionId }, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Revoke all sessions for a user.
|
||||
/// </summary>
|
||||
[HttpPost("~/api/admin/users/{userId}/revoke-sessions")]
|
||||
public Task<IActionResult> RevokeAllForUser(string userId, CancellationToken ct)
|
||||
{
|
||||
Logger.LogWarning("[Admin] RevokeAllSessions | UserId={UserId} | By={User}", userId, Client.Email);
|
||||
return CallProc("spAdminSessions", "revokeAllForUser", new { userId }, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup expired sessions.
|
||||
/// </summary>
|
||||
[HttpPost("cleanup")]
|
||||
public Task<IActionResult> Cleanup([FromQuery] int daysOld = 30, CancellationToken ct = default)
|
||||
{
|
||||
Logger.LogWarning("[Admin] CleanupSessions | DaysOld={DaysOld} | By={User}", daysOld, Client.Email);
|
||||
return CallProc("spAdminSessions", "cleanup", new { daysOld }, ct);
|
||||
}
|
||||
}
|
||||
140
Management/Controllers/Admin/AdminUsersController.cs
Normal file
140
Management/Controllers/Admin/AdminUsersController.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using Management.Data;
|
||||
using Management.Security;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Management.Controllers.Admin;
|
||||
|
||||
/// <summary>
|
||||
/// Admin endpoints for user management.
|
||||
/// Requires Admin role.
|
||||
///
|
||||
/// ENDPOINTS:
|
||||
/// GET /api/admin/users - List users
|
||||
/// GET /api/admin/users/{id} - Get user
|
||||
/// POST /api/admin/users - Create user
|
||||
/// PUT /api/admin/users/{id} - Update user
|
||||
/// DELETE /api/admin/users/{id} - Deactivate user
|
||||
/// POST /api/admin/users/{id}/clients - Link user to client
|
||||
/// DELETE /api/admin/users/{id}/clients/{cltId} - Unlink user from client
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/admin/users")]
|
||||
public sealed class AdminUsersController : AdminControllerBase
|
||||
{
|
||||
public AdminUsersController(SqlService sql, ClientContext client, ILogger<AdminUsersController> log)
|
||||
: base(sql, client, log) { }
|
||||
|
||||
/// <summary>
|
||||
/// List users with optional filtering.
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public Task<IActionResult> List(
|
||||
[FromQuery] string? status,
|
||||
[FromQuery] string? clientId,
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 50,
|
||||
CancellationToken ct = default)
|
||||
=> CallProc("spAdminUsers", "list", new { status, clientId, page, pageSize }, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Get user by ID.
|
||||
/// </summary>
|
||||
[HttpGet("{userId}")]
|
||||
public Task<IActionResult> Get(string userId, CancellationToken ct)
|
||||
=> CallProc("spAdminUsers", "get", new { userId }, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new user.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public Task<IActionResult> Create([FromBody] CreateUserRequest request, CancellationToken ct)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request?.Email))
|
||||
return Task.FromResult(ValidationError("email is required"));
|
||||
|
||||
Logger.LogWarning("[Admin] CreateUser | Email={Email} | By={User}", request.Email, Client.Email);
|
||||
return CallProc("spAdminUsers", "create", new
|
||||
{
|
||||
email = request.Email.Trim(),
|
||||
displayName = request.DisplayName?.Trim(),
|
||||
clientId = request.ClientId,
|
||||
role = request.Role ?? "User"
|
||||
}, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update user.
|
||||
/// </summary>
|
||||
[HttpPut("{userId}")]
|
||||
public Task<IActionResult> Update(string userId, [FromBody] UpdateUserRequest request, CancellationToken ct)
|
||||
{
|
||||
Logger.LogWarning("[Admin] UpdateUser | Id={Id} | By={User}", userId, Client.Email);
|
||||
return CallProc("spAdminUsers", "update", new
|
||||
{
|
||||
userId,
|
||||
displayName = request?.DisplayName?.Trim(),
|
||||
status = request?.Status
|
||||
}, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivate user (soft delete).
|
||||
/// </summary>
|
||||
[HttpDelete("{userId}")]
|
||||
public Task<IActionResult> Delete(string userId, CancellationToken ct)
|
||||
{
|
||||
Logger.LogWarning("[Admin] DeleteUser | Id={Id} | By={User}", userId, Client.Email);
|
||||
return CallProc("spAdminUsers", "delete", new { userId }, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Link user to client with role.
|
||||
/// </summary>
|
||||
[HttpPost("{userId}/clients")]
|
||||
public Task<IActionResult> LinkToClient(string userId, [FromBody] LinkUserRequest request, CancellationToken ct)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request?.ClientId))
|
||||
return Task.FromResult(ValidationError("clientId is required"));
|
||||
|
||||
Logger.LogWarning("[Admin] LinkUser | UserId={UserId} ClientId={ClientId} | By={User}",
|
||||
userId, request.ClientId, Client.Email);
|
||||
return CallProc("spAdminUsers", "linkToClient", new
|
||||
{
|
||||
userId,
|
||||
clientId = request.ClientId,
|
||||
role = request.Role ?? "User"
|
||||
}, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unlink user from client.
|
||||
/// </summary>
|
||||
[HttpDelete("{userId}/clients/{clientId}")]
|
||||
public Task<IActionResult> UnlinkFromClient(string userId, string clientId, CancellationToken ct)
|
||||
{
|
||||
Logger.LogWarning("[Admin] UnlinkUser | UserId={UserId} ClientId={ClientId} | By={User}",
|
||||
userId, clientId, Client.Email);
|
||||
return CallProc("spAdminUsers", "unlinkFromClient", new { userId, clientId }, ct);
|
||||
}
|
||||
}
|
||||
|
||||
// DTOs
|
||||
public sealed class CreateUserRequest
|
||||
{
|
||||
public string? Email { get; set; }
|
||||
public string? DisplayName { get; set; }
|
||||
public string? ClientId { get; set; }
|
||||
public string? Role { get; set; }
|
||||
}
|
||||
|
||||
public sealed class UpdateUserRequest
|
||||
{
|
||||
public string? DisplayName { get; set; }
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
|
||||
public sealed class LinkUserRequest
|
||||
{
|
||||
public string? ClientId { get; set; }
|
||||
public string? Role { get; set; }
|
||||
}
|
||||
76
Management/Controllers/MonitoringController.cs
Normal file
76
Management/Controllers/MonitoringController.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Management.Data;
|
||||
using Management.Security;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Management.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Monitoring endpoints for system health and stats.
|
||||
/// Requires Admin session.
|
||||
///
|
||||
/// ENDPOINTS:
|
||||
/// GET /api/monitoring/health - System health overview
|
||||
/// GET /api/monitoring/stats - Detailed statistics
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/monitoring")]
|
||||
public sealed class MonitoringController : ControllerBase
|
||||
{
|
||||
private readonly SqlService _sql;
|
||||
private readonly ClientContext _client;
|
||||
private readonly ILogger<MonitoringController> _log;
|
||||
|
||||
public MonitoringController(SqlService sql, ClientContext client, ILogger<MonitoringController> log)
|
||||
{
|
||||
_sql = sql;
|
||||
_client = client;
|
||||
_log = log;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System health overview.
|
||||
/// </summary>
|
||||
[HttpGet("health")]
|
||||
public async Task<IActionResult> Health(CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var resp = await _sql.ExecProcAsync("dbo.spMonitoring", "health", "{}", 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, "[Monitoring] Health error");
|
||||
return StatusCode(500, new { ok = false, error = "Health check failed", detail = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detailed system statistics.
|
||||
/// </summary>
|
||||
[HttpGet("stats")]
|
||||
public async Task<IActionResult> Stats([FromQuery] int hours = 24, CancellationToken ct = default)
|
||||
{
|
||||
var rqst = JsonSerializer.Serialize(new { hours });
|
||||
|
||||
try
|
||||
{
|
||||
var resp = await _sql.ExecProcAsync("dbo.spMonitoring", "stats", 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, "[Monitoring] Stats error");
|
||||
return StatusCode(500, new { ok = false, error = "Stats failed", detail = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
114
Management/Controllers/OnboardingController.cs
Normal file
114
Management/Controllers/OnboardingController.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
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; }
|
||||
}
|
||||
37
Management/Controllers/TestController.cs
Normal file
37
Management/Controllers/TestController.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Management.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Management.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Test endpoints (anonymous, no auth required).
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/test")]
|
||||
public class TestController : ControllerBase
|
||||
{
|
||||
private readonly SqlService _sql;
|
||||
|
||||
public TestController(SqlService sql)
|
||||
{
|
||||
_sql = sql;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Database connectivity test.
|
||||
/// </summary>
|
||||
[HttpGet("ping")]
|
||||
public async Task<IActionResult> Ping(CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var resp = await _sql.ExecProcAsync("dbo.spTemplate", "ping",
|
||||
"""{ "clientId":"00000000-0000-0000-0000-000000000001" }""", ct: ct);
|
||||
return Content(resp, "application/json");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new { ok = false, error = "Database connection failed", detail = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
33
Management/Controllers/WeatherForecastController.cs
Normal file
33
Management/Controllers/WeatherForecastController.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Management.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetWeatherForecast")]
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user