Handler.cs 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Application.Abstractions.YouTube;
  4. using Domain.Entities.Members;
  5. using Microsoft.EntityFrameworkCore;
  6. using Microsoft.Extensions.Logging;
  7. using SharedKernel.Results;
  8. namespace Application.Features.Api.MyPage.Sns.Connect;
  9. internal sealed class Handler(
  10. IAppDbContext db,
  11. IGoogleOAuthService oauthService,
  12. ILogger<Handler> logger
  13. ) : ICommandHandler<Command, Result<Response>>
  14. {
  15. private const string Provider = "Google";
  16. public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
  17. {
  18. if (string.IsNullOrWhiteSpace(request.Code))
  19. {
  20. return Result.Failure<Response>(Error.Problem("Sns.CodeRequired", "Authorization code 가 필요합니다."));
  21. }
  22. if (string.IsNullOrWhiteSpace(request.RedirectUri))
  23. {
  24. return Result.Failure<Response>(Error.Problem("Sns.RedirectUriRequired", "Redirect URI 가 필요합니다."));
  25. }
  26. var config = await db.Config.AsNoTracking().OrderByDescending(x => x.ID).FirstOrDefaultAsync(ct);
  27. if (config is null
  28. || string.IsNullOrEmpty(config.External.GoogleClientId)
  29. || string.IsNullOrEmpty(config.External.GoogleClientSecretEnc)
  30. ) {
  31. return Result.Failure<Response>(Error.Problem("Sns.OAuthConfigMissing", "Google OAuth 설정이 없습니다."));
  32. }
  33. var tokens = await oauthService.ExchangeCodeAsync(
  34. request.Code,
  35. request.RedirectUri,
  36. config.External.GoogleClientId,
  37. config.External.GoogleClientSecretEnc,
  38. ct
  39. );
  40. if (tokens is null)
  41. {
  42. return Result.Failure<Response>(Error.Problem("Sns.OAuthExchangeFailed", "Google 인증 코드 교환에 실패했습니다."));
  43. }
  44. var existing = await db.MemberOAuthToken.FirstOrDefaultAsync(t => t.MemberID == request.MemberID && t.Provider == Provider, ct);
  45. var scopesStr = string.Join(" ", tokens.Scopes);
  46. if (existing is null)
  47. {
  48. var token = MemberOAuthToken.Create(
  49. request.MemberID,
  50. Provider,
  51. tokens.AccessToken,
  52. tokens.RefreshToken,
  53. tokens.ExpiresAt,
  54. scopesStr
  55. );
  56. db.MemberOAuthToken.Add(token);
  57. }
  58. else
  59. {
  60. existing.UpdateTokens(tokens.AccessToken, tokens.RefreshToken, tokens.ExpiresAt, scopesStr);
  61. }
  62. await db.SaveChangesAsync(ct);
  63. logger.LogInformation("[Sns.Connect] Google 연동 완료: memberID={MemberID}", request.MemberID);
  64. return Result.Success(new Response(Provider, DateTime.UtcNow, tokens.ExpiresAt));
  65. }
  66. }