update redirect path format
parent
a6daff4e38
commit
cea23efac3
|
|
@ -152,7 +152,7 @@ async def download_and_save_song(
|
|||
|
||||
# 프론트엔드에서 접근 가능한 URL 생성
|
||||
relative_path = f"/media/song/{today}/{unique_id}/{file_name}"
|
||||
base_url = f"http://{prj_settings.PROJECT_DOMAIN}"
|
||||
base_url = f"{prj_settings.PROJECT_DOMAIN}"
|
||||
file_url = f"{base_url}{relative_path}"
|
||||
logger.info(f"[download_and_save_song] URL generated - task_id: {task_id}, url: {file_url}")
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ User 모듈 SQLAlchemy 모델 정의
|
|||
from datetime import date, datetime
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
from sqlalchemy import BigInteger, Boolean, Date, DateTime, ForeignKey, Index, Integer, String, func
|
||||
from sqlalchemy import BigInteger, Boolean, Date, DateTime, ForeignKey, Index, Integer, String, Text, func
|
||||
from sqlalchemy.dialects.mysql import JSON
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database.session import Base
|
||||
|
|
@ -246,6 +247,18 @@ class User(Base):
|
|||
lazy="selectin",
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# SocialAccount 1:N 관계
|
||||
# ==========================================================================
|
||||
# 한 사용자는 여러 소셜 계정을 연동할 수 있음 (YouTube, Instagram, Facebook)
|
||||
# ==========================================================================
|
||||
social_accounts: Mapped[List["SocialAccount"]] = relationship(
|
||||
"SocialAccount",
|
||||
back_populates="user",
|
||||
cascade="all, delete-orphan",
|
||||
lazy="selectin",
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<User("
|
||||
|
|
@ -373,3 +386,179 @@ class RefreshToken(Base):
|
|||
f"expires_at={self.expires_at}"
|
||||
f")>"
|
||||
)
|
||||
|
||||
|
||||
class SocialAccount(Base):
|
||||
"""
|
||||
소셜 계정 연동 테이블
|
||||
|
||||
사용자가 연동한 외부 소셜 플랫폼 계정 정보를 저장합니다.
|
||||
YouTube, Instagram, Facebook 계정 연동을 지원합니다.
|
||||
|
||||
Attributes:
|
||||
id: 고유 식별자 (자동 증가)
|
||||
user_id: 사용자 외래키 (User.id 참조)
|
||||
platform: 플랫폼 구분 (youtube, instagram, facebook)
|
||||
access_token: OAuth 액세스 토큰
|
||||
refresh_token: OAuth 리프레시 토큰 (선택)
|
||||
token_expires_at: 토큰 만료 일시
|
||||
scope: 허용된 권한 범위
|
||||
platform_user_id: 플랫폼 내 사용자 고유 ID
|
||||
platform_username: 플랫폼 내 사용자명/핸들
|
||||
platform_data: 플랫폼별 추가 정보 (JSON)
|
||||
is_active: 연동 활성화 상태
|
||||
connected_at: 연동 일시
|
||||
updated_at: 정보 수정 일시
|
||||
|
||||
플랫폼별 platform_data 예시:
|
||||
- YouTube: {"channel_id": "UC...", "channel_title": "채널명"}
|
||||
- Instagram: {"business_account_id": "...", "facebook_page_id": "..."}
|
||||
- Facebook: {"page_id": "...", "page_access_token": "..."}
|
||||
|
||||
Relationships:
|
||||
user: 연동된 사용자 (User 테이블 참조)
|
||||
"""
|
||||
|
||||
__tablename__ = "social_account"
|
||||
__table_args__ = (
|
||||
Index("idx_social_account_user_id", "user_id"),
|
||||
Index("idx_social_account_platform", "platform"),
|
||||
Index("idx_social_account_is_active", "is_active"),
|
||||
Index(
|
||||
"uq_user_platform_account",
|
||||
"user_id",
|
||||
"platform",
|
||||
"platform_user_id",
|
||||
unique=True,
|
||||
),
|
||||
{
|
||||
"mysql_engine": "InnoDB",
|
||||
"mysql_charset": "utf8mb4",
|
||||
"mysql_collate": "utf8mb4_unicode_ci",
|
||||
},
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# 기본 식별자
|
||||
# ==========================================================================
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
primary_key=True,
|
||||
nullable=False,
|
||||
autoincrement=True,
|
||||
comment="고유 식별자",
|
||||
)
|
||||
|
||||
user_id: Mapped[int] = mapped_column(
|
||||
BigInteger,
|
||||
ForeignKey("user.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
comment="사용자 외래키 (User.id 참조)",
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# 플랫폼 구분
|
||||
# ==========================================================================
|
||||
platform: Mapped[str] = mapped_column(
|
||||
String(20),
|
||||
nullable=False,
|
||||
comment="플랫폼 구분 (youtube, instagram, facebook)",
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# OAuth 토큰 정보
|
||||
# ==========================================================================
|
||||
access_token: Mapped[str] = mapped_column(
|
||||
Text,
|
||||
nullable=False,
|
||||
comment="OAuth 액세스 토큰",
|
||||
)
|
||||
|
||||
refresh_token: Mapped[Optional[str]] = mapped_column(
|
||||
Text,
|
||||
nullable=True,
|
||||
comment="OAuth 리프레시 토큰 (플랫폼에 따라 선택적)",
|
||||
)
|
||||
|
||||
token_expires_at: Mapped[Optional[datetime]] = mapped_column(
|
||||
DateTime,
|
||||
nullable=True,
|
||||
comment="토큰 만료 일시",
|
||||
)
|
||||
|
||||
scope: Mapped[Optional[str]] = mapped_column(
|
||||
Text,
|
||||
nullable=True,
|
||||
comment="허용된 권한 범위 (OAuth scope)",
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# 플랫폼 계정 식별 정보
|
||||
# ==========================================================================
|
||||
platform_user_id: Mapped[str] = mapped_column(
|
||||
String(100),
|
||||
nullable=False,
|
||||
comment="플랫폼 내 사용자 고유 ID",
|
||||
)
|
||||
|
||||
platform_username: Mapped[Optional[str]] = mapped_column(
|
||||
String(100),
|
||||
nullable=True,
|
||||
comment="플랫폼 내 사용자명/핸들 (@username)",
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# 플랫폼별 추가 정보 (JSON)
|
||||
# ==========================================================================
|
||||
platform_data: Mapped[Optional[dict]] = mapped_column(
|
||||
JSON,
|
||||
nullable=True,
|
||||
comment="플랫폼별 추가 정보 (채널ID, 페이지ID 등)",
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# 연동 상태
|
||||
# ==========================================================================
|
||||
is_active: Mapped[bool] = mapped_column(
|
||||
Boolean,
|
||||
nullable=False,
|
||||
default=True,
|
||||
comment="연동 활성화 상태 (비활성화 시 사용 중지)",
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# 시간 정보
|
||||
# ==========================================================================
|
||||
connected_at: Mapped[datetime] = mapped_column(
|
||||
DateTime,
|
||||
nullable=False,
|
||||
server_default=func.now(),
|
||||
comment="연동 일시",
|
||||
)
|
||||
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime,
|
||||
nullable=False,
|
||||
server_default=func.now(),
|
||||
onupdate=func.now(),
|
||||
comment="정보 수정 일시",
|
||||
)
|
||||
|
||||
# ==========================================================================
|
||||
# User 관계
|
||||
# ==========================================================================
|
||||
user: Mapped["User"] = relationship(
|
||||
"User",
|
||||
back_populates="social_accounts",
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<SocialAccount("
|
||||
f"id={self.id}, "
|
||||
f"user_id={self.user_id}, "
|
||||
f"platform='{self.platform}', "
|
||||
f"platform_username='{self.platform_username}', "
|
||||
f"is_active={self.is_active}"
|
||||
f")>"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ from typing import Optional
|
|||
from sqlalchemy import select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from config import prj_settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from app.user.exceptions import (
|
||||
|
|
@ -111,7 +113,7 @@ class AuthService:
|
|||
profile_image_url=user.profile_image_url,
|
||||
is_new_user=is_new_user,
|
||||
),
|
||||
redirect_url="http://localhost:3000",
|
||||
redirect_url=f"{prj_settings.PROJECT_DOMAIN}",
|
||||
)
|
||||
|
||||
async def refresh_tokens(
|
||||
|
|
|
|||
Loading…
Reference in New Issue