Commit Graph

20 Commits

Author SHA1 Message Date
king e14d303b82 fix(cafe24): 앞 커밋에서 누락된 디자인파일 일반화 변경분 포함
config.py(Cafe24FtpConfig 일반화 + DESIGN_FILE_SPECS)·db.py(add/list_design_
revision)·router.py(design_files_router 연결)·_nav.html(탭 3개)·테스트·
.env.example·문서가 앞 커밋(8c2a9c4)에 함께 올라갔어야 하는데 스테이징 누락.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 14:50:58 +09:00
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
king 20f3f0f7df fix(cafe24): 모바일 상세설명 '직접등록' 오적용·적용직후 읽기지연 수정
- PUT 시 mobile_description 필드를 더 이상 보내지 않는다. 그 필드를 보내는
  순간 카페24가 모바일 상세설명 설정을 "직접 등록"으로 바꿔버림을 실물로
  확인(separated_mobile_description 이 'T'가 됨). 대신
  separated_mobile_description="F" 만 지정해 "PC 상세설명과 동일"을 강제하고,
  카페24가 모바일 값을 PC 와 자동으로 맞추게 한다. 화면 적용(apply)과 예약
  적용(worker) 양쪽 다 수정.
- products.wait_for_description 추가 — 적용 직후 카페24 관리자 API 의 짧은
  읽기 지연(쓰기 직후 몇 초간 이전 값을 돌려줌 — 쇼핑몰 화면에는 바로 반영됨)을
  0.8초 간격 최대 3회 재확인으로 흡수. "쇼핑몰엔 반영됐는데 카페24 상품관리
  화면만 적용 안 된 것처럼 보이는" 증상 완화.
- CAFE24_SHOP_URL 예시를 www.miras.co.kr 로 갱신(.env.example) — 실제 값은
  운영 서버 .env 에서 직접 설정해야 함(코드는 그대로 이 값을 읽어 다이렉트
  주소를 만듦).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 13:49:05 +09:00
king 63cfdc87ec feat(sheets): 사용자 OAuth 리프레시 토큰 인증 지원
조직 정책(iam.disableServiceAccountKeyCreation)으로 서비스 계정 키를 만들 수
없는 환경 대응. 서비스 계정이 설정돼 있으면 그쪽을 먼저 쓰고, 없으면
GOOGLE_SHEETS_OAUTH_REFRESH_TOKEN + client id/secret 으로 인증한다.
(client id/secret 미지정 시 로그인용 GOOGLE_CLIENT_ID/SECRET 재사용)

scripts/google_sheets_authorize.py: 데스크톱 앱 OAuth 클라이언트로
1회 동의해 refresh token 을 발급받는 스크립트.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 14:45:21 +09:00
king db19a2fcbf feat(cupang): 확정 시 Google 스프레드시트에 출고일 시트 기록
- app/modules/cupang/export.py: 출고리스트 양식(셀/병합)을 한 곳에서 생성 →
  xlsx 다운로드와 구글 시트 기록이 같은 표를 쓴다
- app/integrations/google_sheets.py: 서비스 계정 기반 시트 쓰기 공통 계층
  (없거나 미설정이면 enabled=False 로 조용히 skip, 키는 로그에 남기지 않음)
- 확정 응답에 sheet 결과 포함, 화면에 성공/실패 메시지 표시
- env: CUPANG_SHEET_ID, GOOGLE_SHEETS_CREDENTIALS(_JSON) 추가
- requirements: google-api-python-client, google-auth

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 14:26:24 +09:00
king 469de15998 feat(cafe24): 편집기에 상품 다이렉트 주소 + 클립보드 복사
상세 화면 상단에 고객이 보는 상세페이지 주소를 보여주고 「주소 복사」·「쇼핑몰에서
열기」를 붙였다.

    https://miras.co.kr/product/detail.html?product_no=119

도메인은 하드코딩하지 않고 CAFE24_SHOP_URL 로 받는다. 커스텀 도메인은 mall_id 로
알 수 없기 때문이다. 미설정 시 카페24 기본 도메인(https://<mall_id>.cafe24.com)으로
대체해 환경변수가 없어도 항상 유효한 주소가 나온다. 스킴 누락·끝 슬래시도 정규화한다.

주소 칸은 readonly <input> 이라 기존 복사 버튼(data-copy)이 값을 그대로 읽어간다 —
클립보드 로직을 새로 만들지 않았다. 클립보드 API 가 막힌 환경에서는 입력칸 선택으로
대체되고, 칸을 클릭하면 전체 선택된다.

CAFE24_SHOP_URL 은 .env.example 과 문서에 설명을 함께 넣었다(신규 환경변수 규칙).

검증: 유닛테스트 61개 통과(신규 2개 — 스킴/슬래시 유무 3가지 입력에서 같은 주소,
미설정 시 카페24 도메인 대체). 렌더 확인 — 주소 표시·복사 버튼·새 창 열기(noopener),
주소를 만들 수 없으면 줄 자체를 숨김.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 16:46:04 +09:00
king c6fb8ed375 feat(cafe24): 상품 상세페이지 관리 모듈 Phase 1
카페24 관리자에 직접 접속하지 않고 상품 상세페이지(description HTML)를
편집·예약 적용·복원하기 위한 모듈의 기반을 만든다. Phase 1 은 공통
Integration 계층, cafe24_db, OAuth 연결 화면까지다.

카페24 OAuth/API 클라이언트를 상품관리 모듈 안에 두지 않고
app/integrations/cafe24/ 로 분리했다. 향후 추가할 주문관리(주문 조회·송장
일괄등록·취소/반품/교환)가 같은 토큰과 클라이언트를 그대로 재사용해야 하기
때문이다. 라우터에서 httpx 를 직접 부르지 않고 Cafe24Client 만 쓰게 해서
재시도·rate limit·API 로그·토큰 갱신을 한 곳에 모았다.

토큰은 Fernet 으로 암호화해 저장한다(CAFE24_TOKEN_SECRET). DB 덤프가
유출돼도 access/refresh token 이 평문으로 남지 않게 하기 위함이며, API 로그와
연결 상태 화면에는 토큰·시크릿을 일절 기록/표시하지 않는다.

토큰 갱신은 행 잠금(SELECT ... FOR UPDATE) 안에서 한다. 카페24는 refresh
token 을 회전시키므로, 이후 추가될 예약 worker 컨테이너와 web 컨테이너가
동시에 갱신하면 한쪽 토큰이 무효화된다.

기존 파일 변경은 목록에 한 줄씩 추가하는 형태로 44줄뿐이며 기존 라우트·
테이블·인증 로직은 건드리지 않았다. CAFE24_DB_URL 미설정 시 store 가 None
이라 앱은 정상 기동하고 모듈만 "설정 필요" 안내를 표시한다.

가드 헬퍼를 common.py 로 분리한 것은 router.py 가 routes_system.py 를
include 하는 구조에서 순환 import 가 생기기 때문이다.

검증: 신규 테스트 16개 통과(암호화 왕복, 토큰 만료·자동갱신, 상태 노출 시
토큰 미유출, 재시도 예산, 예약 상태 전이). dispatch 기존 테스트 9개 통과.
cafe24_db_init.sql 은 로컬에 Docker 가 없어 미실행 — 서버 적용 시 확인 필요.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 00:23:02 +09:00
king c192d61c71 feat(project): 아사나식 프로젝트 관리 모듈 추가
- project_db 신규(projects/members/stages/tasks/comments/activity, project_app CRUD)
- 프로젝트/서브프로젝트(self-FK), 업무·진행단계(칸반), 멤버 배정
- 메인 뷰 달력(FullCalendar)/타임라인(vis-timeline) 토글 + 보드/리스트
- 진입은 로그인 회사 직원 전원, 생성/배정은 관리자 전용
- 업무 배정·완료 시 관리자 메일 알림(app/mail.py, SMTP_* env, 미설정 시 skip)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 11:22:05 +09:00
king 99eea01fc1 feat(dispatch): 말레이시아 TikTok 출고관리 모듈 추가
03_TikTok_Order_Export.xlsx 업로드 → 1박스=1카드 출고 작업 리스트,
SKU 피킹 요약, Kagayaku 전달표(A4 인쇄)를 자동 생성.

- 1박스 묶음 기준: Package ID > Tracking ID > Order ID, 같은 박스 같은 SKU 합산
- 작업 상태 5단계 토글(AJAX 즉시 저장) + dispatch_logs 기록
- 고객 이름/주소/전화 미저장(파싱 시 폐기, 스키마에 컬럼 없음)
- 엑셀은 openpyxl 파싱(pandas 미사용), DB는 dispatch_db 전용
- 인증은 기존 Google OAuth + 신규 권한키 dispatch 재사용
- scripts/sql/dispatch_db_init.sql, docs/DISPATCH_MODULE.md 동봉

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 15:11:43 +09:00
king f137f2a6a6 feat(malaysia): 말레이시아 창고 재고관리 모듈 추가
- 신규 모듈 app/modules/malaysia (prefix /malaysia, 권한키 malaysia)
- malaysia_stock_db: warehouses/malaysia_items/set_bom/stock_movement/
  daily_stocktake/daily_stocktake_line (멱등 init SQL)
- 낱개(MT/MX/MZ) 입출고·조정 movement, 세트(MY) BOM 관리, 세트 출고 BOM 분해
- 일일 재고조사: 세트→낱개 자동 분해(direct+from_set=total), 확정 시 차이만 STOCKTAKE
- prefix 검증 이중화(DB CHECK + store.py 순수함수), MD- 전면 제외
- 상품명은 itemcode_db 읽기 전용 재사용(중복 마스터 없음)
- 화면 4종 + JSON API, 순수로직 테스트 14건, README/docs 갱신

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 13:34:31 +09:00
king 269e779129 feat(vacation): 휴가 관리 모듈 — 월간 달력 bar/연차·반차 신청/승인 워크플로/공휴일·연차 설정
- vacation_db 전용 저장소(VacationDBStore), VACATION_DB_URL 미설정 시 '설정 필요' 안내
- 월간 달력: 구글식 휴가 bar(주 단위 lane 배정), 일/공휴일 빨강·토요일 파랑
- 신청 폼: 종류/기간/시작·종료 구분(종일/오전/오후)/사유 + 예상 일수 미리보기
- 일수 계산은 서버(compute_days)에서 주말+공휴일(is_red) 제외 재계산
- 워크플로: 작성중/반려→제출→승인|반려, 취소 soft delete, 본인만 수정(작성중/반려)
- 권한: vacation(접근)/vacation_approver(승인), admin 통과. 설정은 admin 전용
- 공휴일(vacation_holidays) DB 기반 provider + settings CRUD, 연차잔여(vacation_balances)
- 엑셀 내보내기(openpyxl), scripts/sql/vacation_db_init.sql(멱등, 2026 공휴일 seed)
- main.py 등록, 메뉴 ready 전환, docs/.env.example/CLAUDE.md 갱신

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 08:20:20 +09:00
king 96e2c49506 docs(cupang): itemcode_db 실제 스키마 반영 (single_items/set_items UNION 검색 SQL)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 03:33:08 +09:00
king 9e707be5ac feat(cupang): 쿠팡 밀크런 모듈 추가 (출고 달력/박스계산/입고센터)
- app/modules/cupang: router/db/store/itemcode + 템플릿 5종
- cupang_db 전용(JSON 폴백 없음). CUPANG_DB_URL 미설정 시 안내 페이지
- 월간 달력 + 출고 묶음(헤더+라인), 박스 입수량 자동계산(서버 재계산)
- 입고센터 관리(사용중 soft delete), 박스규칙 upsert, 엑셀 내보내기
- 상품 검색은 itemcode_db 읽기 전용(미설정 시 수동 입력)
- MODULE_KEYS/메뉴/아이콘/라벨 동기화, scripts/sql/cupang_db_init.sql(멱등)
- app/data/ gitignore 추가(PII)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 20:47:32 +09:00
king 9c58b022e2 feat(expense): 개인경비 모듈 + ERP 좌측메뉴 셸 + expense_db 연결
- ERP 셸: 좌측 사이드바 + 상단 헤더 + 콘텐츠 영역(erp_base.html)
- 분기: 슈퍼관리자 → 업무모듈 카드, 일반사용자 → ERP 메인(erp_home.html)
- 신규 라우트: /modules (슈퍼관리자 모듈 카드 재진입)
- 개인경비 모듈: app/modules/expense/ (라우터/저장소/템플릿)
  - JSON 저장소(ExpenseStore) + PostgreSQL 저장소(ExpenseDBStore)
  - 팩토리: EXPENSE_DB_URL 있으면 DB, 없으면 JSON 폴백
- DB: scripts/sql/expense_db_init.sql (멱등 DDL, postgres-db 컨테이너)
  - expense_db, 역할 expense_app, 테이블 expense_items, 인덱스/트리거
- 마이그레이션: scripts/migrate_expense_json_to_db.py (멱등, --dry-run)
- 의존성: psycopg[binary,pool]>=3.2
- 환경변수: EXPENSE_DB_URL 추가
- 문서: CLAUDE.md 작업 기준 + docs/{PROJECT_OVERVIEW,SERVER_ARCHITECTURE,DATABASES,DEPLOYMENT}.md
  - main-app 배포 경로 /opt/www/main 고정 명시
  - 위험 명령(DROP/TRUNCATE/rm -rf/volume 삭제) 사용자 승인 정책
2026-05-29 02:21:03 +09:00
king 98ac452e7d feat(menu): CS 발주 업무 카드를 /corm/ 으로 연결
- 메뉴 카드 url 기본값을 /corm/ 로 (NPM 리버스 프록시 하위 경로)
- 헬스 배지가 가리킬 health_url 을 /corm/health/db 로 설정
- .env.example 의 CS_ORDER_URL 안내를 /corm/ 로 정리
2026-05-24 15:41:01 +09:00
king 7ddc765326 feat(sso): SESSION_SECRET_KEY 미설정 시 fail-fast + ALLOWED_EMAILS env화
- SESSION_SECRET_KEY 가 .env 에 없으면 컨테이너 기동 단계에서 RuntimeError.
  알려진 기본값(change-this-session-secret) 폴백 제거로
  운영 .env 누락 시 양쪽 secret 불일치로 SSO 가 조용히 깨지는 사고 방지.
- ALLOWED_EMAILS 를 환경변수(쉼표 구분)로 덮어쓸 수 있게 변경.
  env 미설정 시 기존 4명 기본 목록 유지 → 운영 호환.
- .env.example 갱신: SESSION_SECRET_KEY 안내 강화, ALLOWED_EMAILS 항목 추가.
2026-05-24 13:25:06 +09:00
king c274e50ee0 feat(sso): SESSION_MAX_AGE를 env로 노출 — OMS와 통일
- SessionMiddleware max_age 를 SESSION_MAX_AGE 환경변수에서 읽도록 변경
  (기본 28800초=8h, OMS .env.example 과 일치)
- .env.example 에 SESSION_MAX_AGE 항목 추가
- run-local.ps1 을 .gitignore 에 추가 (run-local.bat 와 동일 정책 — 로컬 전용)
2026-05-24 13:04:02 +09:00
king e33cc8686b feat: OMS(orderlist)와 SSO 호환되도록 세션 계약 정렬
- 세션 쿠키 이름을 SESSION_COOKIE_NAME env 로 OMS 와 통일
- 로그인 성공 시 top-level user_email / user_name 키도 함께 저장
- get_user() 가 OMS 가 만든 SSO 세션(top-level email만 있는 경우)도 인정
- /login 에 ?next= 지원 (open redirect 방지 위해 같은 호스트 절대경로만 허용)
2026-05-24 05:38:43 +09:00
king e8a85be36c feat: 주문리스트를 /orderlist/ 리버스 프록시 경로로 통합
- 메뉴 카드 새 탭 열기 + /orderlist/health/db 헬스 배지 (온라인/오프라인) 추가
- CUSTOMER_ORDER_LIST_URL 기본값을 /orderlist/ 로 (NPM 같은 도메인 하위 경로)
- orderlist 측 compose 정리 스크립트 추가 (ports 제거, npm_default 외부 네트워크 attach)
2026-05-24 05:04:29 +09:00
king c93e2be0a7 feat: 초기 커밋 — Pretendard 폰트 적용 및 Docker 배포 구성
- Pretendard Regular/Bold/Thin 폰트를 app/static/fonts/ 에 추가하고
  @font-face 로 등록하여 전체 페이지에 적용
- Dockerfile(python:3.12-slim + uvicorn --proxy-headers) 작성
- docker-compose.yml: NPM 뒤 운영 서버용 (포트 80)
- docker-compose.local.yml: 로컬 테스트용 (포트 8080)
- .env.local.example: 로컬 OAuth 설정 가이드 포함
- .dockerignore: 이미지 빌드 컨텍스트 최소화
- .gitignore: .env / .env.local 제외 명시
- README: Docker 빌드·배포·업데이트 절차 문서화

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-21 22:43:41 +09:00