| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Application.Abstractions.YouTube;
- using Microsoft.EntityFrameworkCore;
- using Microsoft.Extensions.Logging;
- using SharedKernel.Results;
- namespace Application.Features.Api.MyPage.Sns.Disconnect;
- internal sealed class Handler(
- IAppDbContext db,
- IGoogleOAuthService oauthService,
- ILogger<Handler> logger
- ) : ICommandHandler<Command, Result<Response>>
- {
- private static readonly string[] AllowedProviders = ["Google"];
- private static readonly TimeSpan RetryDelay = TimeSpan.FromMilliseconds(500);
- public async Task<Result<Response>> Handle(Command request, CancellationToken ct)
- {
- if (!AllowedProviders.Contains(request.Provider))
- {
- return Result.Failure<Response>(Error.Problem("Sns.UnsupportedProvider", "지원하지 않는 SNS provider 입니다."));
- }
- var token = await db.MemberOAuthToken.FirstOrDefaultAsync(t => t.MemberID == request.MemberID && t.Provider == request.Provider, ct);
- if (token is null)
- {
- // idempotent: 이미 연동되어 있지 않아도 성공으로 처리
- return Result.Success(new Response(request.Provider, true));
- }
- // Google revoke 시도 — refresh_token 우선 (refresh_token revoke 는 access_token 도 함께 무효화)
- var revokeTarget = !string.IsNullOrEmpty(token.RefreshTokenEnc)
- ? token.RefreshTokenEnc
- : token.AccessTokenEnc;
- var revoked = await oauthService.RevokeTokenAsync(revokeTarget, ct);
- if (!revoked)
- {
- // 1회 재시도 (네트워크 일시 장애/Google 5xx 대비)
- try
- {
- await Task.Delay(RetryDelay, ct);
- revoked = await oauthService.RevokeTokenAsync(revokeTarget, ct);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- }
- // DB 삭제 — revoke 성공/실패 무관 (DB 삭제는 항상 진행)
- db.MemberOAuthToken.Remove(token);
- await db.SaveChangesAsync(ct);
- if (!revoked)
- {
- logger.LogWarning("[Sns.Disconnect] revoke 실패 — DB 만 삭제. memberID={MemberID}, provider={Provider}", request.MemberID, request.Provider);
- }
- else
- {
- logger.LogInformation("[Sns.Disconnect] 연동 해제 완료: memberID={MemberID}, provider={Provider}", request.MemberID, request.Provider);
- }
- return Result.Success(new Response(request.Provider, revoked));
- }
- }
|