using MediatR; using Web.Api.Common; using Web.Api.Extensions; namespace Web.Api.Endpoints.V1.Members; internal sealed class List : IEndpoint { public void MapEndpoint(IEndpointRouteBuilder app) { app.MapGet("v1/members", async ( ISender sender, CancellationToken ct, bool? hasChannel = null, int page = 1, int size = 50 ) => { var query = new Application.Features.Api.V1.Members.List.Query(hasChannel, page, size); var result = await sender.Send(query, ct); return ApiResponse.Ok(result); }) .WithTags("회원") .WithGroupName("public") .WithName("ListMembers") .WithSummary("회원 조회") .WithDescription(""" DPOT 회원을 페이지네이션해 반환합니다. 탈퇴/삭제된 회원은 자동 제외됩니다. ### 필수 scope - `read:members` ### 쿼리 파라미터 | 이름 | 타입 | 기본 | 설명 | |---|---|---|---| | `hasChannel` | bool? | null | `true` 크리에이터만 / `false` 일반 회원만 / 생략 시 전체 | | `page` | int | 1 | 1부터 시작 | | `size` | int | 50 | 1~200 | ### 응답 필드 - `emailMasked` — 이메일은 `ji***@example.com` 형식으로 마스킹 - `name` — 닉네임 (null 가능) - `hasChannel` — 채널 소유 여부 - `channelSID` / `channelName` — **채널 소유 회원에만** 채워짐 (그 외 `null`) - `createdAt` — 가입일 (UTC ISO 8601) ### 사용 예 ``` GET /v1/members # 전체 GET /v1/members?hasChannel=true # 크리에이터만 GET /v1/members?hasChannel=false # 일반 회원만 GET /v1/members?hasChannel=true&page=2&size=100 ``` """) .Produces(StatusCodes.Status200OK) .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) .RequireAuthorization(policy => policy .AddAuthenticationSchemes("ApiKey", "OAuth2Bearer") .RequireAuthenticatedUser()) .RequireScope("read:members"); } }