You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

70 lines
3.1 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# backend/app/connectors/registry.py — CONNECTOR_<DOMAIN> 로 mock|real 선택하는 단일 지점.
# 페이지/라우터는 항상 레지스트리를 통해서만 sync 커넥터를 얻는다(코드 불변의 핵심).
from sqlmodel import Session, select
from ..config import get_settings
from ..models import ConnectorAccount
from .base import BaseConnector
def _impl(domain: str, mode: str, provider: str = ""):
"""도메인 × 모드 → 구현 클래스 (lazy import 로 의존성 격리)."""
if domain == "mail": # mock 제거(phase-16+): 실 계정만 동기화
if mode == "real": # real 메일은 provider 로 Gmail/Outlook 분기
from .mail.real_gmail import GmailConnector
from .mail.real_outlook import OutlookConnector
return OutlookConnector if provider == "outlook" else GmailConnector
if mode == "imap":
from .mail.real_imap import ImapConnector
return ImapConnector
raise ValueError(f"unsupported mail connector mode: {mode}")
if domain == "calendar": # mock 제거(phase-16+)
if mode == "real": # real 캘린더는 provider 로 Google/Outlook 분기
if provider == "outlook_calendar":
from .calendar.real_outlook import OutlookCalendarConnector
return OutlookCalendarConnector
from .calendar.real_google import GoogleCalendarConnector
return GoogleCalendarConnector
if mode == "ics":
from .calendar.ics_import import IcsConnector
return IcsConnector
raise ValueError(f"unsupported calendar connector mode: {mode}")
raise ValueError(f"unknown domain {domain}")
class ConnectorRegistry:
@staticmethod
def mode_for(domain) -> str:
dom = domain.value if hasattr(domain, "value") else str(domain)
return getattr(get_settings(), f"connector_{dom}", "mock")
@staticmethod
def effective_mode(account: ConnectorAccount) -> str:
"""모드 결정 정본:
- 계정 mode 가 mock 이 아니면(real/csv/healthkit/ics) 그 모드를 쓴다(연결된 계정).
- 시드 mock 계정은 env=real 이어도 mock 유지(데모 결정성).
- csv/healthkit/ics env 면 그 모드를 따른다(로컬 우선 임포트)."""
env_mode = ConnectorRegistry.mode_for(account.domain)
acct_mode = account.mode.value if hasattr(account.mode, "value") else str(account.mode)
if acct_mode != "mock":
return acct_mode
return "mock" if env_mode == "real" else env_mode
@staticmethod
def get(session: Session, account: ConnectorAccount) -> BaseConnector:
mode = ConnectorRegistry.effective_mode(account)
dom = account.domain.value if hasattr(account.domain, "value") else account.domain
return _impl(dom, mode, account.provider)(account)
@staticmethod
def accounts(session: Session, domain: str | None = None) -> list[ConnectorAccount]:
q = select(ConnectorAccount)
if domain:
q = q.where(ConnectorAccount.domain == domain)
return session.exec(q.order_by(ConnectorAccount.id)).all()