using Application.Abstractions.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Infrastructure.YouTube;
///
/// YouTube Data API v3 요청에 API Key를 자동으로 추가하는 DelegatingHandler
/// DB Config에서 YouTube API Key를 조회하여 쿼리스트링에 &key=xxx 추가
///
internal sealed class YouTubeApiKeyHandler(
IServiceProvider serviceProvider,
ILogger logger
) : DelegatingHandler
{
private string? _cachedApiKey;
private DateTime _cachedAt = DateTime.MinValue;
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(30);
protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var apiKey = await GetApiKeyAsync(cancellationToken);
if (!string.IsNullOrEmpty(apiKey) && request.RequestUri is not null)
{
// 이미 Authorization 헤더(OAuth)가 있으면 API Key 추가 안 함
if (request.Headers.Authorization is null)
{
var uriStr = request.RequestUri.ToString();
var separator = uriStr.Contains('?') ? "&" : "?";
request.RequestUri = new Uri($"{uriStr}{separator}key={Uri.EscapeDataString(apiKey)}");
}
}
else
{
logger.LogWarning("[YouTube] API Key가 비어있습니다. DB Config > External > YouTubeApiKeyEnc를 확인하세요.");
}
return await base.SendAsync(request, cancellationToken);
}
private async Task GetApiKeyAsync(CancellationToken ct)
{
if (_cachedApiKey is not null && DateTime.UtcNow - _cachedAt < CacheDuration)
{
return _cachedApiKey;
}
try
{
using var scope = serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService();
var config = await db.Config.AsNoTracking().FirstOrDefaultAsync(ct);
if (config is null)
{
return null;
}
// Config.External.YouTubeApiKeyEnc에서 API Key 가져옴
_cachedApiKey = config.External?.YouTubeApiKeyEnc;
_cachedAt = DateTime.UtcNow;
return _cachedApiKey;
}
catch (Exception ex)
{
logger.LogWarning(ex, "[YouTube] API Key 조회 실패");
return _cachedApiKey; // 이전 캐시 반환
}
}
}