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 == "/" || !path.StartsWithSegments("/v1") || 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") || path == "/v1/in/tag/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); } }