youtube bug fix, timezone 수정, lazyloading 수정 .
parent
40afe9392c
commit
e29e10eb29
|
|
@ -4,6 +4,7 @@ Social Account Service
|
||||||
소셜 계정 연동 관련 비즈니스 로직을 처리합니다.
|
소셜 계정 연동 관련 비즈니스 로직을 처리합니다.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import secrets
|
import secrets
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
@ -27,10 +28,8 @@ redis_client = Redis(
|
||||||
decode_responses=True,
|
decode_responses=True,
|
||||||
)
|
)
|
||||||
from app.social.exceptions import (
|
from app.social.exceptions import (
|
||||||
InvalidStateError,
|
|
||||||
OAuthStateExpiredError,
|
OAuthStateExpiredError,
|
||||||
OAuthTokenRefreshError,
|
OAuthTokenRefreshError,
|
||||||
SocialAccountAlreadyConnectedError,
|
|
||||||
SocialAccountNotFoundError,
|
SocialAccountNotFoundError,
|
||||||
TokenExpiredError,
|
TokenExpiredError,
|
||||||
)
|
)
|
||||||
|
|
@ -90,7 +89,7 @@ class SocialAccountService:
|
||||||
await redis_client.setex(
|
await redis_client.setex(
|
||||||
state_key,
|
state_key,
|
||||||
social_oauth_settings.OAUTH_STATE_TTL_SECONDS,
|
social_oauth_settings.OAUTH_STATE_TTL_SECONDS,
|
||||||
str(state_data),
|
json.dumps(state_data), # JSON으로 직렬화
|
||||||
)
|
)
|
||||||
logger.debug(f"[SOCIAL] OAuth state 저장 - key: {state_key}")
|
logger.debug(f"[SOCIAL] OAuth state 저장 - key: {state_key}")
|
||||||
|
|
||||||
|
|
@ -126,9 +125,7 @@ class SocialAccountService:
|
||||||
SocialAccountResponse: 연동된 소셜 계정 정보
|
SocialAccountResponse: 연동된 소셜 계정 정보
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
InvalidStateError: state 토큰이 유효하지 않은 경우
|
OAuthStateExpiredError: state 토큰이 만료되거나 유효하지 않은 경우
|
||||||
OAuthStateExpiredError: state 토큰이 만료된 경우
|
|
||||||
SocialAccountAlreadyConnectedError: 이미 연동된 계정인 경우
|
|
||||||
"""
|
"""
|
||||||
logger.info(f"[SOCIAL] OAuth 콜백 처리 시작 - state: {state[:20]}...")
|
logger.info(f"[SOCIAL] OAuth 콜백 처리 시작 - state: {state[:20]}...")
|
||||||
|
|
||||||
|
|
@ -140,8 +137,8 @@ class SocialAccountService:
|
||||||
logger.warning(f"[SOCIAL] state 토큰 없음 또는 만료 - state: {state[:20]}...")
|
logger.warning(f"[SOCIAL] state 토큰 없음 또는 만료 - state: {state[:20]}...")
|
||||||
raise OAuthStateExpiredError()
|
raise OAuthStateExpiredError()
|
||||||
|
|
||||||
# state 데이터 파싱
|
# state 데이터 파싱 (JSON 역직렬화)
|
||||||
state_data = eval(state_data_str) # {"user_uuid": "...", "platform": "..."}
|
state_data = json.loads(state_data_str)
|
||||||
user_uuid = state_data["user_uuid"]
|
user_uuid = state_data["user_uuid"]
|
||||||
platform = SocialPlatform(state_data["platform"])
|
platform = SocialPlatform(state_data["platform"])
|
||||||
|
|
||||||
|
|
@ -307,7 +304,9 @@ class SocialAccountService:
|
||||||
if account.token_expires_at is None:
|
if account.token_expires_at is None:
|
||||||
should_refresh = True
|
should_refresh = True
|
||||||
else:
|
else:
|
||||||
buffer_time = now() + timedelta(hours=1)
|
# DB datetime은 naive, now()는 aware이므로 naive로 통일하여 비교
|
||||||
|
current_time = now().replace(tzinfo=None)
|
||||||
|
buffer_time = current_time + timedelta(hours=1)
|
||||||
if account.token_expires_at <= buffer_time:
|
if account.token_expires_at <= buffer_time:
|
||||||
should_refresh = True
|
should_refresh = True
|
||||||
|
|
||||||
|
|
@ -514,7 +513,9 @@ class SocialAccountService:
|
||||||
)
|
)
|
||||||
should_refresh = True
|
should_refresh = True
|
||||||
else:
|
else:
|
||||||
buffer_time = now() + timedelta(minutes=10)
|
# DB datetime은 naive, now()는 aware이므로 naive로 통일하여 비교
|
||||||
|
current_time = now().replace(tzinfo=None)
|
||||||
|
buffer_time = current_time + timedelta(minutes=10)
|
||||||
if account.token_expires_at <= buffer_time:
|
if account.token_expires_at <= buffer_time:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[SOCIAL] 토큰 만료 임박, 갱신 시작 - account_id: {account.id}"
|
f"[SOCIAL] 토큰 만료 임박, 갱신 시작 - account_id: {account.id}"
|
||||||
|
|
@ -573,11 +574,13 @@ class SocialAccountService:
|
||||||
if token_response.refresh_token:
|
if token_response.refresh_token:
|
||||||
account.refresh_token = token_response.refresh_token
|
account.refresh_token = token_response.refresh_token
|
||||||
if token_response.expires_in:
|
if token_response.expires_in:
|
||||||
account.token_expires_at = now() + timedelta(
|
# DB에 naive datetime으로 저장 (MySQL DateTime은 timezone 미지원)
|
||||||
|
account.token_expires_at = now().replace(tzinfo=None) + timedelta(
|
||||||
seconds=token_response.expires_in
|
seconds=token_response.expires_in
|
||||||
)
|
)
|
||||||
|
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
await session.refresh(account)
|
||||||
|
|
||||||
logger.info(f"[SOCIAL] 토큰 갱신 완료 - account_id: {account.id}")
|
logger.info(f"[SOCIAL] 토큰 갱신 완료 - account_id: {account.id}")
|
||||||
return account.access_token
|
return account.access_token
|
||||||
|
|
@ -631,10 +634,10 @@ class SocialAccountService:
|
||||||
Returns:
|
Returns:
|
||||||
SocialAccount: 생성된 소셜 계정
|
SocialAccount: 생성된 소셜 계정
|
||||||
"""
|
"""
|
||||||
# 토큰 만료 시간 계산
|
# 토큰 만료 시간 계산 (DB에 naive datetime으로 저장)
|
||||||
token_expires_at = None
|
token_expires_at = None
|
||||||
if token_response.expires_in:
|
if token_response.expires_in:
|
||||||
token_expires_at = now() + timedelta(
|
token_expires_at = now().replace(tzinfo=None) + timedelta(
|
||||||
seconds=token_response.expires_in
|
seconds=token_response.expires_in
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -687,7 +690,8 @@ class SocialAccountService:
|
||||||
if token_response.refresh_token:
|
if token_response.refresh_token:
|
||||||
account.refresh_token = token_response.refresh_token
|
account.refresh_token = token_response.refresh_token
|
||||||
if token_response.expires_in:
|
if token_response.expires_in:
|
||||||
account.token_expires_at = now() + timedelta(
|
# DB에 naive datetime으로 저장
|
||||||
|
account.token_expires_at = now().replace(tzinfo=None) + timedelta(
|
||||||
seconds=token_response.expires_in
|
seconds=token_response.expires_in
|
||||||
)
|
)
|
||||||
if token_response.scope:
|
if token_response.scope:
|
||||||
|
|
@ -703,7 +707,7 @@ class SocialAccountService:
|
||||||
|
|
||||||
# 재연결 시 연결 시간 업데이트
|
# 재연결 시 연결 시간 업데이트
|
||||||
if update_connected_at:
|
if update_connected_at:
|
||||||
account.connected_at = now()
|
account.connected_at = now().replace(tzinfo=None)
|
||||||
|
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(account)
|
await session.refresh(account)
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ async def _update_upload_status(
|
||||||
if error_message:
|
if error_message:
|
||||||
upload.error_message = error_message
|
upload.error_message = error_message
|
||||||
if status == UploadStatus.COMPLETED:
|
if status == UploadStatus.COMPLETED:
|
||||||
upload.uploaded_at = now()
|
upload.uploaded_at = now().replace(tzinfo=None)
|
||||||
|
|
||||||
await session.commit()
|
await session.commit()
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|
|
||||||
|
|
@ -391,6 +391,7 @@ class RefreshToken(Base):
|
||||||
user: Mapped["User"] = relationship(
|
user: Mapped["User"] = relationship(
|
||||||
"User",
|
"User",
|
||||||
back_populates="refresh_tokens",
|
back_populates="refresh_tokens",
|
||||||
|
lazy="selectin", # lazy loading 방지
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
|
@ -591,6 +592,7 @@ class SocialAccount(Base):
|
||||||
user: Mapped["User"] = relationship(
|
user: Mapped["User"] = relationship(
|
||||||
"User",
|
"User",
|
||||||
back_populates="social_accounts",
|
back_populates="social_accounts",
|
||||||
|
lazy="selectin", # lazy loading 방지
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue