feat(backend): add member GET endpoints, POST /users/@me/members endpoint
This commit is contained in:
		
							parent
							
								
									16f230b97d
								
							
						
					
					
						commit
						e7ec0e6661
					
				
					 10 changed files with 152 additions and 55 deletions
				
			
		
							
								
								
									
										68
									
								
								Foxnouns.Backend/Controllers/MembersController.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								Foxnouns.Backend/Controllers/MembersController.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | |||
| using Foxnouns.Backend.Database; | ||||
| using Foxnouns.Backend.Database.Models; | ||||
| using Foxnouns.Backend.Middleware; | ||||
| using Foxnouns.Backend.Services; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| 
 | ||||
| namespace Foxnouns.Backend.Controllers; | ||||
| 
 | ||||
| [Route("/api/v2/users/{userRef}/members")] | ||||
| public class MembersController( | ||||
|     ILogger logger, | ||||
|     DatabaseContext db, | ||||
|     MemberRendererService memberRendererService, | ||||
|     ISnowflakeGenerator snowflakeGenerator) : ApiControllerBase | ||||
| { | ||||
|     private readonly ILogger _logger = logger.ForContext<MembersController>(); | ||||
| 
 | ||||
|     [HttpGet] | ||||
|     [ProducesResponseType<IEnumerable<MemberRendererService.PartialMember>>(StatusCodes.Status200OK)] | ||||
|     public async Task<IActionResult> GetMembersAsync(string userRef) | ||||
|     { | ||||
|         var user = await db.ResolveUserAsync(userRef, CurrentToken); | ||||
|         return Ok(await memberRendererService.RenderUserMembersAsync(user, CurrentToken)); | ||||
|     } | ||||
| 
 | ||||
|     [HttpGet("{memberRef}")] | ||||
|     [ProducesResponseType<MemberRendererService.MemberResponse>(StatusCodes.Status200OK)] | ||||
|     public async Task<IActionResult> GetMemberAsync(string userRef, string memberRef) | ||||
|     { | ||||
|         var member = await db.ResolveMemberAsync(userRef, memberRef, CurrentToken); | ||||
|         return Ok(memberRendererService.RenderMember(member, CurrentToken)); | ||||
|     } | ||||
| 
 | ||||
|     [HttpPost("/api/v2/users/@me/members")] | ||||
|     [ProducesResponseType<MemberRendererService.MemberResponse>(StatusCodes.Status200OK)] | ||||
|     [Authorize("member.create")] | ||||
|     public async Task<IActionResult> CreateMemberAsync([FromBody] CreateMemberRequest req) | ||||
|     { | ||||
|         await using var tx = await db.Database.BeginTransactionAsync(); | ||||
| 
 | ||||
|         // "Translation of the 'string.Equals' overload with a 'StringComparison' parameter is not supported." | ||||
|         // Member names are case-insensitive, so we need to compare the lowercase forms of both. | ||||
| #pragma warning disable CA1862 | ||||
|         if (await db.Members.AnyAsync(m => m.UserId == CurrentUser!.Id && m.Name.ToLower() == req.Name.ToLower())) | ||||
| #pragma warning restore CA1862 | ||||
|         { | ||||
|             throw new ApiError.BadRequest("A member with that name already exists", "name"); | ||||
|         } | ||||
| 
 | ||||
|         var member = new Member | ||||
|         { | ||||
|             Id = snowflakeGenerator.GenerateSnowflake(), | ||||
|             Name = req.Name, | ||||
|             User = CurrentUser! | ||||
|         }; | ||||
|         db.Add(member); | ||||
| 
 | ||||
|         _logger.Debug("Creating member {MemberName} ({Id}) for {UserId}", member.Name, member.Id, CurrentUser!.Id); | ||||
| 
 | ||||
|         await db.SaveChangesAsync(); | ||||
|         await tx.CommitAsync(); | ||||
| 
 | ||||
|         return Ok(memberRendererService.RenderMember(member, CurrentToken)); | ||||
|     } | ||||
| 
 | ||||
|     public record CreateMemberRequest(string Name); | ||||
| } | ||||
|  | @ -11,7 +11,7 @@ public class MetaController(DatabaseContext db, IClock clock) : ApiControllerBas | |||
|     private const string Repository = "https://codeberg.org/pronounscc/pronouns.cc"; | ||||
| 
 | ||||
|     [HttpGet] | ||||
|     [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(MetaResponse))] | ||||
|     [ProducesResponseType<MetaResponse>(StatusCodes.Status200OK)] | ||||
|     public async Task<IActionResult> GetMeta() | ||||
|     { | ||||
|         var now = clock.GetCurrentInstant(); | ||||
|  |  | |||
|  | @ -16,16 +16,7 @@ public class UsersController(DatabaseContext db, UserRendererService userRendere | |||
|     [ProducesResponseType<UserRendererService.UserResponse>(statusCode: StatusCodes.Status200OK)] | ||||
|     public async Task<IActionResult> GetUserAsync(string userRef) | ||||
|     { | ||||
|         var user = await db.ResolveUserAsync(userRef); | ||||
|         return await GetUserInnerAsync(user); | ||||
|     } | ||||
| 
 | ||||
|     [HttpGet("@me")] | ||||
|     [Authorize("identify")] | ||||
|     [ProducesResponseType<UserRendererService.UserResponse>(statusCode: StatusCodes.Status200OK)] | ||||
|     public async Task<IActionResult> GetMeAsync() | ||||
|     { | ||||
|         var user = await db.ResolveUserAsync(CurrentUser!.Id); | ||||
|         var user = await db.ResolveUserAsync(userRef, CurrentToken); | ||||
|         return await GetUserInnerAsync(user); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue