This commit is contained in:
2025-10-01 23:59:31 +03:00
parent 2a29571dbf
commit b6c4b9b6bb
28 changed files with 689 additions and 383 deletions

View File

@@ -4,21 +4,18 @@ using Microsoft.AspNetCore.Mvc;
namespace LctMonolith.Controllers;
/// <summary>
/// Basic analytics endpoints.
/// </summary>
[ApiController]
[Route("api/analytics")]
[Authorize]
public class AnalyticsController : ControllerBase
{
private readonly IAnalyticsService _analytics;
public AnalyticsController(IAnalyticsService analytics)
{
_analytics = analytics;
}
/// <summary>Get aggregate system summary metrics.</summary>
[HttpGet("summary")]
public async Task<IActionResult> GetSummary(CancellationToken ct)
{
@@ -26,4 +23,3 @@ public class AnalyticsController : ControllerBase
return Ok(summary);
}
}

View File

@@ -11,11 +11,8 @@ using RefreshRequest = LctMonolith.Services.Models.RefreshRequest;
namespace LctMonolith.Controllers;
/// <summary>
/// Authentication endpoints (mocked local identity + JWT issuing).
/// </summary>
[ApiController]
[Route("api/auth")]
[Route("api/auth")]
public class AuthController : ControllerBase
{
private readonly UserManager<AppUser> _userManager;
@@ -29,34 +26,43 @@ public class AuthController : ControllerBase
_tokenService = tokenService;
}
/// <summary>Registers a new user (simplified).</summary>
[HttpPost("register")]
[AllowAnonymous]
public async Task<ActionResult<TokenPair>> Register(AuthRequest req, CancellationToken ct)
{
var existing = await _userManager.FindByEmailAsync(req.Email);
if (existing != null) return Conflict("Email already registered");
if (existing != null)
{
return Conflict("Email already registered");
}
var user = new AppUser { UserName = req.Email, Email = req.Email, FirstName = req.FirstName, LastName = req.LastName };
var result = await _userManager.CreateAsync(user, req.Password);
if (!result.Succeeded) return BadRequest(result.Errors);
if (!result.Succeeded)
{
return BadRequest(result.Errors);
}
var tokens = await _tokenService.IssueAsync(user, ct);
return Ok(tokens);
}
/// <summary>Login with email + password.</summary>
[HttpPost("login")]
[AllowAnonymous]
public async Task<ActionResult<TokenPair>> Login(AuthRequest req, CancellationToken ct)
{
var user = await _userManager.FindByEmailAsync(req.Email);
if (user == null) return Unauthorized();
var passOk = await _signInManager.CheckPasswordSignInAsync(user, req.Password, lockoutOnFailure: false);
if (!passOk.Succeeded) return Unauthorized();
if (user == null)
{
return Unauthorized();
}
var passOk = await _signInManager.CheckPasswordSignInAsync(user, req.Password, false);
if (!passOk.Succeeded)
{
return Unauthorized();
}
var tokens = await _tokenService.IssueAsync(user, ct);
return Ok(tokens);
}
/// <summary>Refresh access token by refresh token.</summary>
[HttpPost("refresh")]
[AllowAnonymous]
public async Task<ActionResult<TokenPair>> Refresh(RefreshRequest req, CancellationToken ct)
@@ -65,7 +71,6 @@ public class AuthController : ControllerBase
return Ok(pair);
}
/// <summary>Revoke refresh token (logout).</summary>
[HttpPost("revoke")]
[Authorize]
public async Task<IActionResult> Revoke(RevokeRequest req, CancellationToken ct)
@@ -74,13 +79,11 @@ public class AuthController : ControllerBase
return NoContent();
}
/// <summary>Returns current user id (debug).</summary>
[HttpGet("me")]
[Authorize]
public ActionResult<object> Me()
{
var id = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue(ClaimTypes.Name);
var id = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue(ClaimTypes.Name);
return Ok(new { userId = id });
}
}

View File

@@ -20,22 +20,30 @@ public class DialogueController : ControllerBase
[HttpGet("mission/{missionId:guid}")]
public async Task<IActionResult> GetByMission(Guid missionId)
{
var d = await _dialogueService.GetDialogueByMissionIdAsync(missionId);
return d == null ? NotFound() : Ok(d);
var dialogue = await _dialogueService.GetDialogueByMissionIdAsync(missionId);
if (dialogue == null)
{
return NotFound();
}
return Ok(dialogue);
}
[HttpGet("message/{messageId:guid}")]
public async Task<IActionResult> GetMessage(Guid messageId)
{
var m = await _dialogueService.GetDialogueMessageByIdAsync(messageId);
return m == null ? NotFound() : Ok(m);
var message = await _dialogueService.GetDialogueMessageByIdAsync(messageId);
if (message == null)
{
return NotFound();
}
return Ok(message);
}
[HttpGet("message/{messageId:guid}/options")]
public async Task<IActionResult> GetOptions(Guid messageId)
{
var opts = await _dialogueService.GetResponseOptionsAsync(messageId);
return Ok(opts);
var options = await _dialogueService.GetResponseOptionsAsync(messageId);
return Ok(options);
}
public record DialogueResponseRequest(Guid ResponseOptionId, Guid PlayerId);
@@ -44,7 +52,10 @@ public class DialogueController : ControllerBase
public async Task<IActionResult> Respond(Guid messageId, DialogueResponseRequest req)
{
var next = await _dialogueService.ProcessDialogueResponseAsync(messageId, req.ResponseOptionId, req.PlayerId);
if (next == null) return Ok(new { end = true });
if (next == null)
{
return Ok(new { end = true });
}
return Ok(next);
}
@@ -60,16 +71,16 @@ public class DialogueController : ControllerBase
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Create(CreateDialogueRequest dto)
{
var d = new Dialogue
var dialogue = new Dialogue
{
Id = Guid.NewGuid(),
MissionId = dto.MissionId,
InitialDialogueMessageId = dto.InitialDialogueMessageId,
InterimDialogueMessageId = dto.InterimDialogueMessageId,
EndDialogueMessageId = dto.EndDialogueMessageId,
Mission = null! // EF will populate if included
Mission = null!
};
d = await _dialogueService.CreateDialogueAsync(d);
return CreatedAtAction(nameof(GetByMission), new { missionId = d.MissionId }, d);
dialogue = await _dialogueService.CreateDialogueAsync(dialogue);
return CreatedAtAction(nameof(GetByMission), new { missionId = dialogue.MissionId }, dialogue);
}
}

View File

@@ -11,19 +11,25 @@ namespace LctMonolith.Controllers;
public class InventoryController : ControllerBase
{
private readonly IInventoryService _inventoryService;
public InventoryController(IInventoryService inventoryService) => _inventoryService = inventoryService;
private Guid CurrentUserId() => Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
public InventoryController(IInventoryService inventoryService)
{
_inventoryService = inventoryService;
}
private Guid CurrentUserId()
{
return Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
}
/// <summary>Get inventory for current authenticated user.</summary>
[HttpGet]
public async Task<IActionResult> GetMine(CancellationToken ct)
{
var items = await _inventoryService.GetStoreInventoryAsync(CurrentUserId(), ct);
return Ok(items.Select(i => new { i.StoreItemId, i.Quantity, i.AcquiredAt }));
var shaped = items.Select(i => new { i.StoreItemId, i.Quantity, i.AcquiredAt });
return Ok(shaped);
}
/// <summary>Admin: get inventory for specific user.</summary>
[HttpGet("user/{userId:guid}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> GetByUser(Guid userId, CancellationToken ct)

View File

@@ -12,7 +12,11 @@ namespace LctMonolith.Controllers;
public class MissionCategoriesController : ControllerBase
{
private readonly IMissionCategoryService _service;
public MissionCategoriesController(IMissionCategoryService service) => _service = service;
public MissionCategoriesController(IMissionCategoryService service)
{
_service = service;
}
[HttpGet]
public async Task<IActionResult> GetAll()
@@ -24,35 +28,45 @@ public class MissionCategoriesController : ControllerBase
[HttpGet("{id:guid}")]
public async Task<IActionResult> Get(Guid id)
{
var c = await _service.GetCategoryByIdAsync(id);
return c == null ? NotFound() : Ok(c);
var category = await _service.GetCategoryByIdAsync(id);
if (category == null)
{
return NotFound();
}
return Ok(category);
}
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Create(CreateMissionCategoryDto dto)
{
var c = await _service.CreateCategoryAsync(new MissionCategory { Title = dto.Title });
return CreatedAtAction(nameof(Get), new { id = c.Id }, c);
var created = await _service.CreateCategoryAsync(new MissionCategory { Title = dto.Title });
return CreatedAtAction(nameof(Get), new { id = created.Id }, created);
}
[HttpPut("{id:guid}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Update(Guid id, CreateMissionCategoryDto dto)
{
var c = await _service.GetCategoryByIdAsync(id);
if (c == null) return NotFound();
c.Title = dto.Title;
await _service.UpdateCategoryAsync(c);
return Ok(c);
var existing = await _service.GetCategoryByIdAsync(id);
if (existing == null)
{
return NotFound();
}
existing.Title = dto.Title;
await _service.UpdateCategoryAsync(existing);
return Ok(existing);
}
[HttpDelete("{id:guid}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Delete(Guid id)
{
var ok = await _service.DeleteCategoryAsync(id);
return ok ? NoContent() : NotFound();
var removed = await _service.DeleteCategoryAsync(id);
if (!removed)
{
return NotFound();
}
return NoContent();
}
}

View File

@@ -23,8 +23,12 @@ public class MissionsController : ControllerBase
[HttpGet("{id:guid}")]
public async Task<IActionResult> Get(Guid id)
{
var m = await _missions.GetMissionByIdAsync(id);
return m == null ? NotFound() : Ok(m);
var mission = await _missions.GetMissionByIdAsync(id);
if (mission == null)
{
return NotFound();
}
return Ok(mission);
}
[HttpGet("category/{categoryId:guid}")]
@@ -80,7 +84,10 @@ public class MissionsController : ControllerBase
public async Task<IActionResult> Update(Guid id, CreateMissionRequest dto)
{
var existing = await _missions.GetMissionByIdAsync(id);
if (existing == null) return NotFound();
if (existing == null)
{
return NotFound();
}
existing.Title = dto.Title;
existing.Description = dto.Description ?? string.Empty;
existing.MissionCategoryId = dto.MissionCategoryId;
@@ -95,8 +102,12 @@ public class MissionsController : ControllerBase
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Delete(Guid id)
{
var ok = await _missions.DeleteMissionAsync(id);
return ok ? NoContent() : NotFound();
var removed = await _missions.DeleteMissionAsync(id);
if (!removed)
{
return NotFound();
}
return NoContent();
}
public record CompleteMissionRequest(Guid PlayerId, object? Proof);
@@ -105,8 +116,10 @@ public class MissionsController : ControllerBase
public async Task<IActionResult> Complete(Guid missionId, CompleteMissionRequest r)
{
var result = await _missions.CompleteMissionAsync(missionId, r.PlayerId, r.Proof);
if (!result.Success) return BadRequest(result);
if (!result.Success)
{
return BadRequest(result);
}
return Ok(result);
}
}

View File

@@ -6,9 +6,6 @@ using Microsoft.AspNetCore.Mvc;
namespace LctMonolith.Controllers;
/// <summary>
/// In-app user notifications API.
/// </summary>
[ApiController]
[Route("api/notifications")]
[Authorize]
@@ -21,17 +18,18 @@ public class NotificationsController : ControllerBase
_notifications = notifications;
}
private Guid GetUserId() => Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
private Guid GetUserId()
{
return Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
}
/// <summary>Get up to 100 unread notifications.</summary>
[HttpGet("unread")]
[HttpGet("unread")]
public async Task<IActionResult> GetUnread(CancellationToken ct)
{
var list = await _notifications.GetUnreadAsync(GetUserId(), ct);
return Ok(list);
}
/// <summary>Get recent notifications (paged by take).</summary>
[HttpGet]
public async Task<IActionResult> GetAll([FromQuery] int take = 100, CancellationToken ct = default)
{
@@ -39,20 +37,17 @@ public class NotificationsController : ControllerBase
return Ok(list);
}
/// <summary>Mark a notification as read.</summary>
[HttpPost("mark/{id:guid}")]
[HttpPost("mark/{id:guid}")]
public async Task<IActionResult> MarkRead(Guid id, CancellationToken ct)
{
await _notifications.MarkReadAsync(GetUserId(), id, ct);
return NoContent();
}
/// <summary>Mark all notifications as read.</summary>
[HttpPost("mark-all")]
[HttpPost("mark-all")]
public async Task<IActionResult> MarkAll(CancellationToken ct)
{
var cnt = await _notifications.MarkAllReadAsync(GetUserId(), ct);
return Ok(new { updated = cnt });
var updated = await _notifications.MarkAllReadAsync(GetUserId(), ct);
return Ok(new { updated });
}
}

View File

@@ -18,47 +18,54 @@ public class PlayersController : ControllerBase
_progressService = progressService;
}
[HttpGet("{playerId:guid}")]
[HttpGet("{playerId:guid}")]
public async Task<IActionResult> GetPlayer(Guid playerId)
{
var player = await _playerService.GetPlayerWithProgressAsync(playerId);
return Ok(player);
}
[HttpGet("user/{userId:guid}")]
[HttpGet("user/{userId:guid}")]
public async Task<IActionResult> GetByUser(Guid userId)
{
var p = await _playerService.GetPlayerByUserIdAsync(userId.ToString());
if (p == null) return NotFound();
return Ok(p);
var player = await _playerService.GetPlayerByUserIdAsync(userId.ToString());
if (player == null)
{
return NotFound();
}
return Ok(player);
}
public class CreatePlayerRequest { public Guid UserId { get; set; } public string Username { get; set; } = string.Empty; }
public class CreatePlayerRequest
{
public Guid UserId { get; set; }
public string Username { get; set; } = string.Empty;
}
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Create(CreatePlayerRequest req)
{
var p = await _playerService.CreatePlayerAsync(req.UserId.ToString(), req.Username);
return CreatedAtAction(nameof(GetPlayer), new { playerId = p.Id }, p);
var player = await _playerService.CreatePlayerAsync(req.UserId.ToString(), req.Username);
return CreatedAtAction(nameof(GetPlayer), new { playerId = player.Id }, player);
}
public record AdjustValueRequest(int Value);
[HttpPost("{playerId:guid}/experience")]
[Authorize(Roles = "Admin")] // manual adjust
[Authorize(Roles = "Admin")]
public async Task<IActionResult> AddExperience(Guid playerId, AdjustValueRequest r)
{
var p = await _playerService.AddPlayerExperienceAsync(playerId, r.Value);
return Ok(new { p.Id, p.Experience });
var player = await _playerService.AddPlayerExperienceAsync(playerId, r.Value);
return Ok(new { player.Id, player.Experience });
}
[HttpPost("{playerId:guid}/mana")]
[Authorize(Roles = "Admin")] // manual adjust
[Authorize(Roles = "Admin")]
public async Task<IActionResult> AddMana(Guid playerId, AdjustValueRequest r)
{
var p = await _playerService.AddPlayerManaAsync(playerId, r.Value);
return Ok(new { p.Id, p.Mana });
var player = await _playerService.AddPlayerManaAsync(playerId, r.Value);
return Ok(new { player.Id, player.Mana });
}
[HttpGet("top")]
@@ -68,11 +75,10 @@ public class PlayersController : ControllerBase
return Ok(list);
}
[HttpGet("{playerId:guid}/progress")]
[HttpGet("{playerId:guid}/progress")]
public async Task<IActionResult> GetProgress(Guid playerId)
{
var prog = await _progressService.GetPlayerOverallProgressAsync(playerId);
return Ok(prog);
var progress = await _progressService.GetPlayerOverallProgressAsync(playerId);
return Ok(progress);
}
}

View File

@@ -12,9 +12,16 @@ namespace LctMonolith.Controllers;
public class ProfileController : ControllerBase
{
private readonly IProfileService _profiles;
public ProfileController(IProfileService profiles) => _profiles = profiles;
private Guid CurrentUserId() => Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
public ProfileController(IProfileService profiles)
{
_profiles = profiles;
}
private Guid CurrentUserId()
{
return Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
}
public class UpdateProfileDto
{
@@ -28,40 +35,54 @@ public class ProfileController : ControllerBase
[HttpGet("me")]
public async Task<IActionResult> GetMe(CancellationToken ct)
{
var p = await _profiles.GetByUserIdAsync(CurrentUserId(), ct);
if (p == null) return NotFound();
return Ok(p);
var profile = await _profiles.GetByUserIdAsync(CurrentUserId(), ct);
if (profile == null)
{
return NotFound();
}
return Ok(profile);
}
[HttpGet("{userId:guid}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> GetByUser(Guid userId, CancellationToken ct)
{
var p = await _profiles.GetByUserIdAsync(userId, ct);
return p == null ? NotFound() : Ok(p);
var profile = await _profiles.GetByUserIdAsync(userId, ct);
if (profile == null)
{
return NotFound();
}
return Ok(profile);
}
[HttpPut]
public async Task<IActionResult> Upsert(UpdateProfileDto dto, CancellationToken ct)
{
var p = await _profiles.UpsertAsync(CurrentUserId(), dto.FirstName, dto.LastName, dto.BirthDate, dto.About, dto.Location, ct);
return Ok(p);
var profile = await _profiles.UpsertAsync(CurrentUserId(), dto.FirstName, dto.LastName, dto.BirthDate, dto.About, dto.Location, ct);
return Ok(profile);
}
[HttpPost("avatar")]
[RequestSizeLimit(7_000_000)] // ~7MB
[RequestSizeLimit(7_000_000)]
public async Task<IActionResult> UploadAvatar(IFormFile file, CancellationToken ct)
{
if (file == null || file.Length == 0) return BadRequest("File required");
if (file == null || file.Length == 0)
{
return BadRequest("File required");
}
await using var stream = file.OpenReadStream();
var p = await _profiles.UpdateAvatarAsync(CurrentUserId(), stream, file.ContentType, file.FileName, ct);
return Ok(new { p.AvatarUrl });
var profile = await _profiles.UpdateAvatarAsync(CurrentUserId(), stream, file.ContentType, file.FileName, ct);
return Ok(new { profile.AvatarUrl });
}
[HttpDelete("avatar")]
public async Task<IActionResult> DeleteAvatar(CancellationToken ct)
{
var ok = await _profiles.DeleteAvatarAsync(CurrentUserId(), ct);
return ok ? NoContent() : NotFound();
var removed = await _profiles.DeleteAvatarAsync(CurrentUserId(), ct);
if (!removed)
{
return NotFound();
}
return NoContent();
}
}

View File

@@ -30,8 +30,12 @@ public class RanksController : ControllerBase
[HttpGet("{id:guid}")]
public async Task<IActionResult> Get(Guid id)
{
var r = await _rankService.GetRankByIdAsync(id);
return r == null ? NotFound() : Ok(r);
var rank = await _rankService.GetRankByIdAsync(id);
if (rank == null)
{
return NotFound();
}
return Ok(rank);
}
[HttpPost]
@@ -46,27 +50,33 @@ public class RanksController : ControllerBase
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Update(Guid id, CreateRankDto dto)
{
var r = await _rankService.GetRankByIdAsync(id);
if (r == null) return NotFound();
r.Title = dto.Title;
r.ExpNeeded = dto.ExpNeeded;
await _rankService.UpdateRankAsync(r);
return Ok(r);
var rank = await _rankService.GetRankByIdAsync(id);
if (rank == null)
{
return NotFound();
}
rank.Title = dto.Title;
rank.ExpNeeded = dto.ExpNeeded;
await _rankService.UpdateRankAsync(rank);
return Ok(rank);
}
[HttpDelete("{id:guid}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Delete(Guid id)
{
var ok = await _rankService.DeleteRankAsync(id);
return ok ? NoContent() : NotFound();
var removed = await _rankService.DeleteRankAsync(id);
if (!removed)
{
return NotFound();
}
return NoContent();
}
[HttpGet("validate-advance/{playerId:guid}/{targetRankId:guid}")]
public async Task<IActionResult> CanAdvance(Guid playerId, Guid targetRankId)
{
var ok = await _ruleValidation.ValidateRankAdvancementRulesAsync(playerId, targetRankId);
return Ok(new { playerId, targetRankId, canAdvance = ok });
var can = await _ruleValidation.ValidateRankAdvancementRulesAsync(playerId, targetRankId);
return Ok(new { playerId, targetRankId, canAdvance = can });
}
}

View File

@@ -16,23 +16,22 @@ public class RewardController : ControllerBase
_rewardService = rewardService;
}
/// <summary>List skill rewards configured for a mission.</summary>
[HttpGet("mission/{missionId:guid}/skills")]
public async Task<IActionResult> GetMissionSkillRewards(Guid missionId)
{
var rewards = await _rewardService.GetMissionSkillRewardsAsync(missionId);
return Ok(rewards.Select(r => new { r.SkillId, r.Value }));
var shaped = rewards.Select(r => new { r.SkillId, r.Value });
return Ok(shaped);
}
/// <summary>List item rewards configured for a mission.</summary>
[HttpGet("mission/{missionId:guid}/items")]
public async Task<IActionResult> GetMissionItemRewards(Guid missionId)
{
var rewards = await _rewardService.GetMissionItemRewardsAsync(missionId);
return Ok(rewards.Select(r => new { r.ItemId }));
var shaped = rewards.Select(r => new { r.ItemId });
return Ok(shaped);
}
/// <summary>Check if mission rewards can be claimed by player (missionId used as rewardId).</summary>
[HttpGet("mission/{missionId:guid}/can-claim/{playerId:guid}")]
public async Task<IActionResult> CanClaim(Guid missionId, Guid playerId)
{
@@ -42,19 +41,20 @@ public class RewardController : ControllerBase
public record ClaimRewardRequest(Guid PlayerId);
/// <summary>Claim mission rewards if available (idempotent on already claimed).</summary>
[HttpPost("mission/{missionId:guid}/claim")]
public async Task<IActionResult> Claim(Guid missionId, ClaimRewardRequest req)
{
var can = await _rewardService.CanClaimRewardAsync(missionId, req.PlayerId);
if (!can) return Conflict(new { message = "Rewards already claimed or mission not completed" });
if (!can)
{
return Conflict(new { message = "Rewards already claimed or mission not completed" });
}
await _rewardService.DistributeMissionRewardsAsync(missionId, req.PlayerId);
return Ok(new { missionId, req.PlayerId, status = "claimed" });
}
public record ForceDistributeRequest(Guid PlayerId);
/// <summary>Admin: force distribute rewards regardless of previous state (may duplicate). Use cautiously.</summary>
[HttpPost("mission/{missionId:guid}/force-distribute")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> ForceDistribute(Guid missionId, ForceDistributeRequest req)

View File

@@ -28,8 +28,12 @@ public class SkillsController : ControllerBase
[HttpGet("{id:guid}")]
public async Task<IActionResult> Get(Guid id)
{
var s = await _skillService.GetSkillByIdAsync(id);
return s == null ? NotFound() : Ok(s);
var skill = await _skillService.GetSkillByIdAsync(id);
if (skill == null)
{
return NotFound();
}
return Ok(skill);
}
[HttpPost]
@@ -44,19 +48,26 @@ public class SkillsController : ControllerBase
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Update(Guid id, CreateSkillDto dto)
{
var s = await _skillService.GetSkillByIdAsync(id);
if (s == null) return NotFound();
s.Title = dto.Title;
await _skillService.UpdateSkillAsync(s);
return Ok(s);
var skill = await _skillService.GetSkillByIdAsync(id);
if (skill == null)
{
return NotFound();
}
skill.Title = dto.Title;
await _skillService.UpdateSkillAsync(skill);
return Ok(skill);
}
[HttpDelete("{id:guid}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Delete(Guid id)
{
var ok = await _skillService.DeleteSkillAsync(id);
return ok ? NoContent() : NotFound();
var removed = await _skillService.DeleteSkillAsync(id);
if (!removed)
{
return NotFound();
}
return NoContent();
}
[HttpGet("player/{playerId:guid}")]
@@ -76,4 +87,3 @@ public class SkillsController : ControllerBase
return Ok(new { ps.PlayerId, ps.SkillId, ps.Score });
}
}

View File

@@ -7,9 +7,6 @@ using Microsoft.AspNetCore.Mvc;
namespace LctMonolith.Controllers;
/// <summary>
/// Store endpoints for listing items and purchasing.
/// </summary>
[ApiController]
[Route("api/store")]
[Authorize]
@@ -22,9 +19,11 @@ public class StoreController : ControllerBase
_storeService = storeService;
}
private Guid GetUserId() => Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
private Guid GetUserId()
{
return Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
}
/// <summary>List active store items.</summary>
[HttpGet("items")]
public async Task<IActionResult> GetItems(CancellationToken ct)
{
@@ -32,7 +31,6 @@ public class StoreController : ControllerBase
return Ok(items);
}
/// <summary>Purchase an item for the authenticated user.</summary>
[HttpPost("purchase")]
public async Task<IActionResult> Purchase(PurchaseRequest req, CancellationToken ct)
{
@@ -40,4 +38,3 @@ public class StoreController : ControllerBase
return Ok(new { inv.StoreItemId, inv.Quantity, inv.AcquiredAt });
}
}