- ServiceCodeMiddleware: message/list를 OPTIONAL_FOR_ADMIN에 추가 - MessageListRequestDto: service_code, send_status 필터 필드 추가 - MessageSummaryDto: service_name, service_code, latest_send_status 추가 - IMessageRepository + MessageRepository: GetPagedForListAsync 구현 (Service 조인 + PushSendLog 집계 한 번의 쿼리) - IMessageService + MessageService: serviceId nullable 변경, DetermineSendStatus 헬퍼 - MessageController: GetServiceIdOrNull() 헬퍼 + Swagger 업데이트 Closes #224
101 lines
3.8 KiB
C#
101 lines
3.8 KiB
C#
using SPMS.Domain.Common;
|
|
using SPMS.Domain.Enums;
|
|
using SPMS.Domain.Interfaces;
|
|
|
|
namespace SPMS.API.Middlewares;
|
|
|
|
public class ServiceCodeMiddleware
|
|
{
|
|
private readonly RequestDelegate _next;
|
|
|
|
public ServiceCodeMiddleware(RequestDelegate next) => _next = next;
|
|
|
|
public async Task InvokeAsync(HttpContext context, IServiceRepository serviceRepository)
|
|
{
|
|
var path = context.Request.Path;
|
|
|
|
// === SKIP: X-Service-Code 불필요 ===
|
|
if (path.StartsWithSegments("/v1/out") ||
|
|
path.StartsWithSegments("/v1/in/auth") ||
|
|
path.StartsWithSegments("/v1/in/account") ||
|
|
path.StartsWithSegments("/v1/in/public") ||
|
|
path.StartsWithSegments("/v1/in/service") ||
|
|
(path.StartsWithSegments("/v1/in/device") &&
|
|
!path.StartsWithSegments("/v1/in/device/list")) ||
|
|
path.StartsWithSegments("/swagger") ||
|
|
path.StartsWithSegments("/health"))
|
|
{
|
|
await _next(context);
|
|
return;
|
|
}
|
|
|
|
// === OPTIONAL_FOR_ADMIN: 관리자는 X-Service-Code 선택 ===
|
|
if (path.StartsWithSegments("/v1/in/stats") ||
|
|
path.StartsWithSegments("/v1/in/device/list") ||
|
|
path.StartsWithSegments("/v1/in/message/list"))
|
|
{
|
|
if (context.Request.Headers.TryGetValue("X-Service-Code", out var optionalCode) &&
|
|
!string.IsNullOrWhiteSpace(optionalCode))
|
|
{
|
|
// 헤더가 있으면 기존 검증 수행
|
|
await ValidateAndSetService(context, serviceRepository, optionalCode!);
|
|
return;
|
|
}
|
|
|
|
// 헤더 없음 — 인증된 사용자만 전체 서비스 모드 허용
|
|
if (context.User.Identity?.IsAuthenticated == true)
|
|
{
|
|
// ServiceId 미설정 = 전체 서비스 모드
|
|
await _next(context);
|
|
return;
|
|
}
|
|
|
|
// 비인증 요청 → 에러
|
|
context.Response.StatusCode = 400;
|
|
context.Response.ContentType = "application/json";
|
|
await context.Response.WriteAsJsonAsync(
|
|
ApiResponse.Fail(ErrorCodes.ServiceScopeRequired, "X-Service-Code 헤더가 필요합니다."));
|
|
return;
|
|
}
|
|
|
|
// === REQUIRED: X-Service-Code 필수 ===
|
|
if (!context.Request.Headers.TryGetValue("X-Service-Code", out var serviceCode) ||
|
|
string.IsNullOrWhiteSpace(serviceCode))
|
|
{
|
|
context.Response.StatusCode = 400;
|
|
context.Response.ContentType = "application/json";
|
|
await context.Response.WriteAsJsonAsync(
|
|
ApiResponse.Fail(ErrorCodes.BadRequest, "X-Service-Code 헤더가 필요합니다."));
|
|
return;
|
|
}
|
|
|
|
await ValidateAndSetService(context, serviceRepository, serviceCode!);
|
|
}
|
|
|
|
private async Task ValidateAndSetService(HttpContext context, IServiceRepository serviceRepository, string serviceCode)
|
|
{
|
|
var service = await serviceRepository.GetByServiceCodeAsync(serviceCode);
|
|
if (service == null)
|
|
{
|
|
context.Response.StatusCode = 404;
|
|
context.Response.ContentType = "application/json";
|
|
await context.Response.WriteAsJsonAsync(
|
|
ApiResponse.Fail(ErrorCodes.NotFound, "존재하지 않는 서비스입니다."));
|
|
return;
|
|
}
|
|
|
|
if (service.Status != ServiceStatus.Active)
|
|
{
|
|
context.Response.StatusCode = 503;
|
|
context.Response.ContentType = "application/json";
|
|
await context.Response.WriteAsJsonAsync(
|
|
ApiResponse.Fail(ErrorCodes.Unauthorized, "비활성 상태의 서비스입니다."));
|
|
return;
|
|
}
|
|
|
|
context.Items["Service"] = service;
|
|
context.Items["ServiceId"] = service.Id;
|
|
await _next(context);
|
|
}
|
|
}
|