profileShare

rasmusjy / splitapp-backend-clean-onion

Read-only snapshot

No repository description.

main default branch 429 files Expires Sep 13, 2026, 9:06 AM
InvitationsController.cs 4,746 bytes
1 using App.BLL.Services;
2 using App.DTO.Mappers;
3 using App.DTO.v1;
4 using Asp.Versioning;
5 using Microsoft.AspNetCore.Authentication.JwtBearer;
6 using Microsoft.AspNetCore.Authorization;
7 using Microsoft.AspNetCore.Mvc;
8 using System.Net;
9 using System.Security.Claims;
10
11 namespace WebApp.ApiControllers;
12
13 [ApiVersion("1.0")]
14 [Route("api/v{version:apiVersion}/[controller]")]
15 [ApiController]
16 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
17 public class InvitationsController : ControllerBase
18 {
19 private readonly IInvitationService _invitationService;
20
21 public InvitationsController(IInvitationService invitationService)
22 {
23 _invitationService = invitationService;
24 }
25
26 private Guid GetUserId() => Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
27
28 // POST: api/v1/invitations
29 [HttpPost]
30 [Produces("application/json")]
31 [Consumes("application/json")]
32 [ProducesResponseType<InvitationDto>((int)HttpStatusCode.Created)]
33 [ProducesResponseType((int)HttpStatusCode.Forbidden)]
34 public async Task<ActionResult<InvitationDto>> CreateInvitation([FromBody] InvitationCreateDto dto)
35 {
36 var userId = GetUserId();
37
38 var (invitation, errorCode) = await _invitationService.CreateInvitationGuardedAsync(dto.TripId, userId);
39 if (invitation == null)
40 {
41 if (errorCode == "forbidden") return Forbid();
42 return NotFound();
43 }
44
45 // Reload with navigation properties
46 var reloaded = await _invitationService.GetByTokenAsync(invitation.Token);
47
48 return CreatedAtAction(nameof(GetInvitation), new { token = reloaded!.Token }, InvitationMapper.MapToDto(reloaded));
49 }
50
51 // GET: api/v1/invitations/{token}
52 [HttpGet("{token}")]
53 [AllowAnonymous]
54 [Produces("application/json")]
55 [ProducesResponseType<InvitationDto>((int)HttpStatusCode.OK)]
56 [ProducesResponseType((int)HttpStatusCode.NotFound)]
57 public async Task<ActionResult<InvitationDto>> GetInvitation(string token)
58 {
59 var invitation = await _invitationService.GetByTokenAsync(token);
60 if (invitation == null) return NotFound();
61
62 return Ok(InvitationMapper.MapToDto(invitation));
63 }
64
65 // POST: api/v1/invitations/{token}/accept
66 [HttpPost("{token}/accept")]
67 [Produces("application/json")]
68 [ProducesResponseType((int)HttpStatusCode.OK)]
69 [ProducesResponseType((int)HttpStatusCode.NotFound)]
70 [ProducesResponseType((int)HttpStatusCode.BadRequest)]
71 public async Task<IActionResult> AcceptInvitation(string token)
72 {
73 var userId = GetUserId();
74
75 var (ok, errorCode) = await _invitationService.AcceptInvitationGuardedAsync(token, userId);
76 if (!ok)
77 {
78 return errorCode switch
79 {
80 "notfound" => NotFound(),
81 "not-pending" => BadRequest("Invitation is no longer pending."),
82 "expired" => BadRequest("Invitation has expired."),
83 "already-participant" => BadRequest("You are already a participant in this trip."),
84 "failed" => BadRequest("Could not accept invitation."),
85 _ => BadRequest("Could not accept invitation.")
86 };
87 }
88
89 return Ok();
90 }
91
92 // POST: api/v1/invitations/{token}/decline
93 [HttpPost("{token}/decline")]
94 [Produces("application/json")]
95 [ProducesResponseType((int)HttpStatusCode.OK)]
96 [ProducesResponseType((int)HttpStatusCode.NotFound)]
97 [ProducesResponseType((int)HttpStatusCode.BadRequest)]
98 public async Task<IActionResult> DeclineInvitation(string token)
99 {
100 var (ok, errorCode) = await _invitationService.DeclineInvitationAsync(token);
101 if (!ok)
102 {
103 return errorCode switch
104 {
105 "notfound" => NotFound(),
106 "not-pending" => BadRequest("Invitation is no longer pending."),
107 _ => BadRequest()
108 };
109 }
110
111 return Ok();
112 }
113
114 // POST: api/v1/invitations/{token}/revoke
115 [HttpPost("{token}/revoke")]
116 [Produces("application/json")]
117 [ProducesResponseType((int)HttpStatusCode.OK)]
118 [ProducesResponseType((int)HttpStatusCode.NotFound)]
119 [ProducesResponseType((int)HttpStatusCode.Forbidden)]
120 public async Task<IActionResult> RevokeInvitation(string token)
121 {
122 var userId = GetUserId();
123
124 var (ok, errorCode) = await _invitationService.RevokeInvitationByTokenAsync(token, userId);
125 if (!ok)
126 {
127 return errorCode switch
128 {
129 "notfound" => NotFound(),
130 "forbidden" => Forbid(),
131 _ => NotFound()
132 };
133 }
134
135 return Ok();
136 }
137 }
138