Files
dbx-main/app/integrations/cafe24/__init__.py
T
king 93037411fb feat(cafe24): 모바일 스와이프(product-swiper.js) 편집 화면 추가 (FTP)
- product-swiper.js는 상품이 아니라 카페24 "디자인 보관함" 스킨 파일이라
  Admin API(OAuth)로는 접근 불가 — 실물 확인(스크린샷) 결과 디자인 보관함
  FTP 계정(호스트/포트/ID/PW, OAuth와 별개)으로만 읽기/쓰기 가능.
  app/integrations/cafe24/design_ftp.py 를 표준 ftplib 로 새로 추가.
- 상단 탭에 "모바일 스와이프" 버튼 추가. 편집 화면(swiper.html)은 상세페이지
  편집기(_editor.html)와 완전히 같은 문법강조·색상·단축키 JS를 그대로 옮겨
  씀(요청사항) — 상품 전용 UI(목록·진열/판매·예약)는 제외.
- 적용 순서도 상세페이지와 동일한 원칙: FTP에서 현재값 재조회 → BACKUP →
  지문 대조(충돌 거부) → FTP 쓰기 → MANUAL 버전 + 감사로그(apply_swiper).
  textarea의 CRLF는 적용 전 LF로 정규화(안 하면 매번 "변경됨"으로 오판).
- 버전 이력은 cafe24_product_revisions(product_no NOT NULL)에 넣을 수 없어
  새 테이블 cafe24_swiper_revisions 추가(scripts/sql/cafe24_db_003_*.sql,
  서버에서 별도 실행 필요).
- 신규 env: CAFE24_FTP_HOST(미설정 시 {mall_id}.ftp.cafe24.com)/PORT/USER/
  PASSWORD, CAFE24_SWIPER_FTP_PATH. .env.example·문서 갱신.
- 유닛테스트 4건 추가(FTP config 기본값, 가짜 FTP로 읽기/쓰기 왕복·오류 처리).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 14:33:35 +09:00

103 lines
2.8 KiB
Python

"""카페24 연동 공통 계층 (상품관리 + 향후 주문관리 공유).
구성
config.py 환경변수 → Cafe24Config (하드코딩 금지)
crypto.py 토큰 Fernet 암복호화
oauth.py 인증 URL / code→token / refresh
tokens.py TokenService — 저장·만료판정·자동갱신(행 잠금)
client.py Cafe24Client — 전송·재시도·429/5xx·API 로그
products.py 상품 엔드포인트 래퍼
errors.py 공통 예외
사용 예 (모듈 라우터에서):
from app.integrations.cafe24 import build_cafe24_api
api = build_cafe24_api(store) # store = Cafe24Store
html = products.get_description(api.client, 123)
CAFE24_* 환경변수가 없어도 import 는 성공한다. 실제 호출 시점에
Cafe24ConfigError 가 나며, 라우터가 "설정 필요" 안내를 보여준다.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from . import design_ftp, products
from .client import Cafe24Client
from .config import (
DEFAULT_SCOPES,
DESIGN_SCOPES,
ORDER_SCOPES,
PRODUCT_SCOPES,
Cafe24Config,
Cafe24FtpConfig,
load_config,
load_ftp_config,
)
from .errors import (
Cafe24ApiError,
Cafe24AuthError,
Cafe24ConfigError,
Cafe24Error,
Cafe24FtpError,
Cafe24RateLimitError,
)
from .oauth import TokenBundle, build_authorize_url, exchange_code, new_state, refresh_tokens
from .tokens import TokenService
__all__ = [
"Cafe24Api",
"build_cafe24_api",
"Cafe24Client",
"Cafe24Config",
"Cafe24FtpConfig",
"load_ftp_config",
"design_ftp",
"TokenService",
"TokenBundle",
"load_config",
"build_authorize_url",
"exchange_code",
"refresh_tokens",
"new_state",
"products",
"PRODUCT_SCOPES",
"ORDER_SCOPES",
"DESIGN_SCOPES",
"DEFAULT_SCOPES",
"Cafe24Error",
"Cafe24ConfigError",
"Cafe24AuthError",
"Cafe24RateLimitError",
"Cafe24ApiError",
"Cafe24FtpError",
]
@dataclass(frozen=True)
class Cafe24Api:
"""설정 + 토큰서비스 + 클라이언트 묶음. 라우터/worker 가 이것만 들고 다닌다."""
config: Cafe24Config
tokens: TokenService
client: Cafe24Client
def build_cafe24_api(store: Any, *, scopes: tuple[str, ...] = DEFAULT_SCOPES) -> Cafe24Api:
"""Cafe24Store 를 저장소로 쓰는 API 묶음 생성.
store 는 토큰 3개 메서드(get_token_row/save_token_row/token_lock)와
API 로그 기록용 log_api_call 을 제공해야 한다.
"""
config = load_config(scopes=scopes)
token_service = TokenService(store, config)
client = Cafe24Client(
config,
token_service,
api_logger=getattr(store, "log_api_call", None),
)
return Cafe24Api(config=config, tokens=token_service, client=client)