Files
step3/api/QuestionsApi/Controllers/AccessController.cs
T

72 lines
3.3 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using QuestionsApi.Models;
using QuestionsApi.Services;
using System.Security.Claims;
namespace QuestionsApi.Controllers;
[ApiController]
[Route("api/access")]
[Authorize(Roles = "USER,SUPER")]
public sealed class AccessController(
AccessStore access,
ExamSimulationStore simulations,
RatingStore ratings,
TimeProvider clock) : ControllerBase
{
[HttpGet]
public async Task<IActionResult> Get() =>
Ok(await access.GetStatusAsync(UserId(), User.IsInRole("SUPER")));
[HttpPost("redeem-credits")]
public async Task<IActionResult> RedeemCredits(RedeemCreditsRequest request)
{
if (!ValidOperationId(request.OperationId)) return BadRequest("A valid operationId is required.");
var status = await access.RedeemCreditsAsync(UserId(), request.OperationId.Trim());
return status is null
? Conflict(new { code = "insufficient_credits", message = "100 credits are required to redeem a day pass." })
: Ok(status);
}
[HttpPost("activate-token")]
public async Task<IActionResult> ActivateToken(ActivateAccessTokenRequest request)
{
if (!ValidOperationId(request.OperationId)) return BadRequest("A valid operationId is required.");
if (request.Token.Trim().ToLowerInvariant() is not ("day" or "week" or "month"))
return BadRequest("token must be day, week, or month");
var status = await access.ActivateTokenAsync(UserId(), request.OperationId.Trim(), request.Token);
return status is null
? Conflict(new { code = "token_unavailable", message = "That access token is not available." })
: Ok(status);
}
[HttpPost("qbank-reset")]
public async Task<IActionResult> ResetQBank(ResetQBankRequest request)
{
if (request.Confirmation != "RESET_QBANK") return BadRequest("Type RESET_QBANK to confirm.");
if (!ValidOperationId(request.OperationId)) return BadRequest("A valid operationId is required.");
if (await simulations.FindActiveAsync(UserId()) is not null)
return Conflict(new { code = "active_exam", message = "Finish or abandon the active exam simulation before resetting the QBank." });
var cycle = await access.ApplyResetAsync(UserId(), request.OperationId.Trim());
if (cycle is null)
return Conflict(new { code = "reset_token_unavailable", message = "A QBank reset token is required." });
await ratings.ResetUserAsync(UserId(), clock.GetUtcNow().UtcDateTime);
return Ok(await access.GetStatusAsync(UserId(), User.IsInRole("SUPER")));
}
[HttpPost("steam-checkouts")]
public IActionResult SteamCheckout() => Problem(
statusCode: StatusCodes.Status503ServiceUnavailable,
title: "Steam Wallet checkout is not configured",
detail: "Steam purchases will be enabled after the Steamworks application and product catalog are configured.");
private string UserId() => User.FindFirstValue(ClaimTypes.NameIdentifier)
?? User.FindFirstValue(ClaimTypes.Name)
?? User.Identity?.Name
?? throw new UnauthorizedAccessException();
private static bool ValidOperationId(string value) =>
!string.IsNullOrWhiteSpace(value) && value.Trim().Length <= 100;
}