Zeeshaun a283969e4c
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 48s
Deploy Promiscuity Character API / deploy (push) Successful in 46s
Deploy Promiscuity Crafting API / deploy (push) Successful in 1m9s
Deploy Promiscuity Inventory API / deploy (push) Successful in 46s
Deploy Promiscuity Locations API / deploy (push) Successful in 48s
Deploy Promiscuity Mail API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 8s
Mailbox support + crafting api
2026-03-26 09:36:42 -05:00

136 lines
5.3 KiB
C#

using CraftingApi.Models;
using CraftingApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
namespace CraftingApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class CraftingController : ControllerBase
{
private readonly CraftingStore _crafting;
public CraftingController(CraftingStore crafting)
{
_crafting = crafting;
}
[HttpGet("recipes")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> ListRecipes()
{
var recipes = await _crafting.ListRecipesAsync();
return Ok(recipes.Select(CraftingRecipeResponse.FromModel).ToList());
}
[HttpGet("recipes/{recipeKey}")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> GetRecipe(string recipeKey)
{
var recipe = await _crafting.GetRecipeAsync(recipeKey);
return recipe is null ? NotFound() : Ok(CraftingRecipeResponse.FromModel(recipe));
}
[HttpPost("recipes/{recipeKey}")]
[Authorize(Roles = "SUPER")]
public async Task<IActionResult> CreateRecipe(string recipeKey, [FromBody] UpsertCraftingRecipeRequest request)
{
var validationError = ValidateRecipeRequest(recipeKey, request);
if (validationError is not null)
return validationError;
var recipe = _crafting.BuildRecipe(recipeKey, request);
var created = await _crafting.CreateRecipeAsync(recipe);
return created ? Ok(CraftingRecipeResponse.FromModel(recipe)) : Conflict("Recipe already exists");
}
[HttpPut("recipes/{recipeKey}")]
[Authorize(Roles = "SUPER")]
public async Task<IActionResult> UpsertRecipe(string recipeKey, [FromBody] UpsertCraftingRecipeRequest request)
{
var validationError = ValidateRecipeRequest(recipeKey, request);
if (validationError is not null)
return validationError;
var recipe = await _crafting.UpsertRecipeAsync(recipeKey, request);
return Ok(CraftingRecipeResponse.FromModel(recipe));
}
[HttpGet("characters/{characterId}/available-recipes")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> AvailableRecipes(string characterId)
{
var access = await ResolveCharacterAccessAsync(characterId);
if (access is IActionResult errorResult)
return errorResult;
var available = await _crafting.GetAvailableRecipesAsync((CraftingStore.CharacterAccessResult)access!);
return Ok(available);
}
[HttpPost("characters/{characterId}/craft")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> Craft(string characterId, [FromBody] CraftRecipeRequest request)
{
if (string.IsNullOrWhiteSpace(request.RecipeKey))
return BadRequest("recipeKey required");
if (request.Quantity <= 0)
return BadRequest("quantity must be greater than 0");
var access = await ResolveCharacterAccessAsync(characterId);
if (access is IActionResult errorResult)
return errorResult;
var result = await _crafting.CraftAsync((CraftingStore.CharacterAccessResult)access!, request);
return result.Status switch
{
CraftingStore.CraftStatus.RecipeNotFound => NotFound("Recipe not found"),
CraftingStore.CraftStatus.MissingStation => Conflict("Required crafting station is not available"),
CraftingStore.CraftStatus.MissingInputs => Conflict(new { missingRequirements = result.MissingRequirements }),
CraftingStore.CraftStatus.InvalidRecipe => BadRequest(new { missingRequirements = result.MissingRequirements }),
_ => Ok(new CraftRecipeResponse
{
CharacterId = characterId,
RecipeKey = request.RecipeKey.Trim().ToLowerInvariant(),
CraftedQuantity = result.CraftedQuantity,
Consumed = result.Consumed,
Produced = result.Produced
})
};
}
private async Task<object> ResolveCharacterAccessAsync(string characterId)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (string.IsNullOrWhiteSpace(userId))
return Unauthorized();
var access = await _crafting.ResolveCharacterAsync(characterId, userId, User.IsInRole("SUPER"));
if (!access.Exists)
return NotFound();
if (!access.IsAuthorized)
return Forbid();
return access;
}
private IActionResult? ValidateRecipeRequest(string recipeKey, UpsertCraftingRecipeRequest request)
{
if (string.IsNullOrWhiteSpace(recipeKey))
return BadRequest("recipeKey required");
if (string.IsNullOrWhiteSpace(request.Name))
return BadRequest("name required");
if (request.Inputs.Count == 0)
return BadRequest("At least one input is required");
if (request.Outputs.Count == 0)
return BadRequest("At least one output is required");
if (request.Inputs.Any(i => string.IsNullOrWhiteSpace(i.ItemKey) || i.Quantity <= 0))
return BadRequest("All inputs must have itemKey and quantity > 0");
if (request.Outputs.Any(i => string.IsNullOrWhiteSpace(i.ItemKey) || i.Quantity <= 0))
return BadRequest("All outputs must have itemKey and quantity > 0");
return null;
}
}