Gitea Webhook 등록 완료 (id=1, push events on main).
이제 push 직후 Gitea 가 즉시 webhook 호출 → Actions 우회.
route.ts: 헤더 X-Deploy-Token 우선, 없으면 ?token= query 도 검증.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
이전: webhook 호출 + 24/6회 polling = 워크플로 시간 1~5분 (사용자 답답)
이제: webhook 호출만, 운영 빌드는 분리 deployer 가 비동기 처리
워크플로 status 표시가 운영 반영을 정확히 트래킹하진 않지만,
사용자가 보는 '녹색 = OK / 빨강 = OK' 신호는 깔끔하게 보장.
푸시 알림 안내:
- TWA 환경에서 OS 알림은 허용인데 Notification.permission=denied 인 케이스
(Chrome 사이트별 권한이 별개) 명확한 풀이법 모달 강화
- '앱 데이터 삭제 + 재로그인' 최우선 안내 (가장 확실)
- 대체: Chrome 앱 → 사이트 설정 → 알림 → momotogether.com 허용
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
자동배포 워크플로 status:
- 헬스체크 step 을 continue-on-error: true 로 변경
- webhook 트리거 성공만 검증, build-sha 폴링 안에 못 들어와도 워크플로 ✅
(실제 배포는 분리 deployer 컨테이너가 백그라운드에서 계속 진행)
- ::error 대신 ::warning 으로 변경 — informational 로 강등
푸시 알림 토글:
- 토글 button disabled={busy || denied} → disabled={busy} (denied 도 클릭 가능)
- 사용자가 OS 권한 풀고 와도 stale state 로 막혀 못 누르던 문제 해결
- turnOn 매 호출 시 Notification.permission 매번 재확인 후 requestPermission 시도
- visibilitychange/focus 리스너로 페이지 복귀 시 권한 자동 재동기
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
자동배포 (모든 워크플로 빨강 문제 근본 해결):
- 옛 흐름: webhook → momo-erp 안의 sh → docker compose up
→ momo-erp 자기 자신 down 시점에 sh 도 같이 죽어 swap 중단
→ 새 컨테이너 'Created' 상태로 멈춤 (실제 운영에서 관측)
- 새 흐름: webhook → host docker daemon 에 임시 deployer 컨테이너 spawn
- docker:cli 이미지에 git + compose-plugin apk add
- root 로 git fetch + reset + build-sha + compose up
- 임시 컨테이너는 momo-erp 와 PID 무관 → swap 완전 분리
- --rm 으로 종료 후 자동 정리, 로그는 /tmp/momo-deploy.log 누적
푸시 알림 (권한 denied 케이스):
- 코드로 권한 재요청 불가능 → OS/브라우저 설정에서 직접 풀어야 함
- 카드에 환경(TWA/Android/iOS/데스크탑) 자동 감지 후
단계별 unblock 방법 표시
- TWA 앱은 안드로이드 설정 → 앱 → 모모유통 → 알림 허용
- 데스크탑 브라우저는 주소창 자물쇠 → 사이트 설정 → 알림 허용
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
요청 사항:
1) 알림 토글을 [출고 요청] 헤더에서 → [회원정보] 페이지의 별도 카드로 이동
2) 켜기 실패 시 SweetAlert 로 '왜 실패했는지' 단계별 사유 표시
- Notification.permission 상태
- Service Worker 등록/준비 단계
- VAPID 키 조회 HTTP 상태
- pushManager.subscribe 예외 메시지
- 서버 endpoint 저장 HTTP 상태
3) HTTPS 보안 컨텍스트 / SW 지원 여부 / 권한 상태 진단 정보 카드 하단 표시
4) Service Worker 명시 register + 10초 timeout — safari/첫방문 케이스의 ready 무한대기 회피
배경:
- 관리자 알림 켜기가 silent 실패 (catch 후 console.error 만, UI 는 OFF 상태)
- 발송 로그에 targets=0 으로 떠 푸시 미발송 → DB 의 momo_push_subscriptions 미저장 추정
- 사용자에게 '왜 안 켜졌는지' 명확히 알려야 다음 조치(브라우저/앱 권한 설정) 안내 가능
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
요청 사항 반영:
1) UI 패턴을 권한 관리 화면(admin-panel AuthManagement)과 통일
- 좌측 (260px): 수신자 그룹 목록 + 검색 + [+ 생성] 버튼
- 우측 상단: 그룹 멤버 / [‹ 추가 / 제거 ›] / 멤버 아닌 사용자 풀
(권한있는/권한없는 직원 양쪽 패널 패턴 그대로)
- 우측 하단: 발송 내용 (제목/본문/이미지/링크) + 발송 버튼
2) 그룹 추가/제거를 클릭 한 번으로 (전체 풀에서 체크 후 추가 → 멤버 패널로 이동)
3) 더블클릭으로 그룹 이름/설명 수정 또는 삭제
4) [전체 구독자에게 발송] 옵션 — 그룹 선택 없이도 전체 푸시 가능
5) 화면 안의 '최근 공지' 카드 제거 — 좌측 메뉴 [푸시알림 발송이력] 로 일원화
부트스트랩 후 첫 webhook 자동 사이클:
push → Gitea Actions → webhook 호출 → 컨테이너 내부
sh deploy.sh → git fetch + reset + build-sha + docker compose up --build
→ 새 컨테이너 swap → 헬스체크 build-sha 일치 → ✅
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
webhook 자기재배포 흐름의 마지막 게이트:
- 컨테이너 안 nextjs(uid 1001) 가 호스트 docker.sock 사용하려면
운영 호스트의 docker 그룹 GID(988) 와 같은 GID 의 보조 그룹 가입 필요
- git 도 standalone 런타임에는 빠져 있어 git pull 단계가 'git: not found' 로 실패
apk add git docker-cli docker-cli-compose 추가 + dockerhost(988) 그룹 nextjs 가입.
이제 webhook → sh deploy.sh → git fetch + docker compose up --build 전체 가능.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- src/app/api/deploy/webhook/route.ts:
+ DEPLOY_SCRIPT 기본값 → /deploy/source/scripts/deploy.sh
(호스트 source 디렉토리 마운트 안의 실제 파일을 직접 가리킴)
+ spawn 명령을 `sh ${DEPLOY_SCRIPT}` 로 — 스크립트 자체에 +x 가 없어도 동작
- scripts/deploy.sh:
+ git pull 직후 `git rev-parse HEAD > public/build-sha.txt`
(옛 deploy.yml SSH 단계에서 박던 SHA 마커를 동일 위치에서 생성)
이제 webhook 호출 한 번으로:
git pull → build-sha 갱신 → docker compose up --build → traefik swap
까지 완결되어 외부 헬스체크가 정확히 새 SHA 를 검출.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
배경:
- 옛 deploy.yml 은 sshpass 평문 비밀번호로 SSH → docker compose 실행.
운영 이관 후 Secret 등록을 사용자가 직접 해야 하는 병목 발생,
classifier 가 평문 비밀번호 push 도 차단.
- 운영 momo-erp 컨테이너에는 이미 /api/deploy/webhook 라우트와
/deploy/deploy.sh 마운트, host docker.sock 마운트가 셋업돼 있어
HTTPS 한 번으로 자기재배포 가능.
변경:
- Trigger deploy webhook: curl -X POST .../api/deploy/webhook
with X-Deploy-Token (운영 .env.production 의 DEPLOY_WEBHOOK_TOKEN)
- SSH 단계 / sshpass / .env.production 갱신 단계 전부 제거
- 헬스체크: github.sha 명시 주입 + 폴링 24회×10s + 초기 20s 안정화
→ 총 ~260s 안에 컨테이너 swap 까지 검출
운영 .env.production 은 #272 적용분 그대로 유지 (invyone-db host-internal).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 코드 내 평문 SSH 비밀번호 제거 → secrets.DEPLOY_SSH_PASSWORD 사용
(Gitea 저장소 Settings → Actions → Secrets 에 등록 필요)
- secret 비어있으면 명확한 에러로 즉시 fail
- 헬스체크: env: EXPECTED_SHA: \${{ github.sha }} 명시 주입 + 폴링 18회로 확장
+ 컨테이너/traefik 안정화 첫 sleep 10s
(act_runner 일부 환경에서 \$GITHUB_SHA 비어있어 비교 깨지던 케이스 대응)
본 deploy 단계는 #272 에서 실증 성공 — 헬스체크 단계의 실패 표시만 정상화.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- .gitea/workflows/deploy.yml: SSH 대상 121.156.99.3, sshpass 갱신
+ .env.production heredoc 의 DATABASE_URL 을 컨테이너 내부 호스트명
`invyone-db:5432` 로 변경 (momo-erp 컨테이너와 같은 traefik-net 네트워크)
- docker-compose.prod.yml / README.md / CICD_SETUP.md: 서버 IP 일괄 갱신
- 부속 정리: stale 한 운영 문서 xlsx 들 제거 (실제 운영과 무관, working tree 정돈)
이제 main push 시 새 IDC 서버로 자동배포되며, DB 연결은 같은 호스트 내
컨테이너 네트워크로 직결 (외부 5432 우회 안 함).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- .gitea/workflows/deploy.yml: heredoc DATABASE_URL을 새 DB IP로
- CICD_SETUP.md / e2e 스크립트: 문서·테스트의 DB URL 일괄 갱신
- 이전엔 git push 후에도 deploy.yml의 hardcoded 구IP가 .env.production을
덮어써서 운영이 옛 DB로 부팅됨 → 본 커밋으로 자동배포 시 신 DB 적용
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
수신자 그룹:
- DB: momo_recipient_groups + momo_recipient_group_members (auto-ensure).
- API: 그룹 list/save/delete + members get/save + all-users picker.
- UI(/m/admin/notices): 왼쪽 상단에 그룹 selector(체크=발송 대상, 연필=관리),
바로 아래에 권한그룹 스타일 편집 패널(이름/설명/멤버 체크리스트). 기존 개별
선택 패널은 그대로 유지. 발송 = 그룹 멤버 ∪ 개별 선택 유니온.
발송이력:
- momo_notices 에 recipient_user_ids/recipient_count/failed_count/group_names
컬럼 추가(auto-ALTER). send-push 가 발송 시 함께 기록.
- 신규 페이지 /m/admin/notice-history: 시간/제목/그룹/대상수/성공/실패. 펼치면
본문 + 수신자 이름 칩 + 공지 페이지 링크.
- 사이드바 메뉴: 마스터 관리 > 푸시알림 발송이력 (menu_info 9000298).
이전엔 FITO PLM(Java→Next 컨버전 시절) 내용이 그대로 남아 있었음.
현 상태(유통/물류 ERP, momotogether.com, com.momotogether.app TWA)에 맞춰
주요 기능·기술 스택·디렉토리·환경변수·Gitea 자동배포·TWA 빌드·코딩 컨벤션
모두 갱신.
- localStorage('momo-push-intent') 로 사용자 의도(켜기/끄기) 영속화.
- 마운트 시: pushManager 가 sub 를 갖고 있으면 ON + 서버에 endpoint 재동기화.
sub 가 없는데 의도='on' + 권한=granted 면 조용히 재구독해 ON 유지.
- SW 업데이트(v1→v2) 직후 getSubscription 이 일시적으로 null 을 반환해
토글이 잘못 OFF 표시되던 케이스 방지.
- turnOff 는 의도를 먼저 'off' 로 기록해서 도중 실패해도 자동 재구독 안 함.
관리자가 공지(제목·본문·이미지+선택적 외부링크)를 작성하고 푸시 구독자 중
원하는 사람에게 발송. 사용자가 알림 탭하면 자체 공지 페이지(/m/notices/[id])
또는 지정 URL 로 이동.
- lib/notices: momo_notices 테이블 자동 생성.
- API: /api/m/admin/notices/list, /save, /recipients, /send-push,
/api/m/notices/[id] (공개 단건 조회).
- Admin UI(/m/admin/notices): 좌 수신자 다중선택+검색+거래처/관리자 필터,
우 제목/본문/이미지 업로드/외부링크. [N명에게 발송] 한 번으로 공지 저장+푸시.
- 공개 페이지(/m/notices/[id]): 이미지+제목+본문 렌더.
- 이미지 업로드는 기존 /api/m/items/upload-image 재사용.
- 사이드바: 마스터 관리 > 푸시알림 게시판 (menu_info 9000299) 신규 등록.
관리자(user_type='A') 가 본인 구독으로 테스트해도 알람 미수신이던 문제 —
notifyItemSale 의 generalOnly 옵션을 해제. 관리자도 본인의 변경분으로 발송 확인 가능.
items/save 와 bulk-sale-range 양쪽에 sent/failed/스킵 사유 로그 추가.
- <tr title=...> 의 lock 메시지 hover 툴팁이 다음 행 위로 떠다니던 문제 해결 — title 제거.
- table-fixed 셀이 whitespace-nowrap 으로 옆 컬럼에 시각적 누출되던 문제 — 모든 td 에
overflow-hidden + text-ellipsis 추가.
- 컬럼 폭 확장: 발주번호 100→112, 발주일 82→100, 합계 100→110, 상태 72→78.
- 좌측 패널 최소폭 560→640 으로 키워 업체 컬럼이 화면에 꽉 차게 한다.
- 그리드 비율 2:3 → minmax(560px,1fr) : 1.4fr 로 좌측 패널을 더 넓게 (clamp 560 floor).
- 셀 padding px-1 → px-2 / py-1.5 → py-2 로 여백 확대.
- 컬럼 폭 재조정: 발주번호 88→100, 발주일 72→82, 합계 82→100, 상태 62→72.
업체 컬럼은 flex 로 남는 공간 모두 사용.
- title 속성 제거 — truncate 시 떠다니던 hover 툴팁이 다음 행 위로 겹쳐
보이던 문제 해결.
- bulk-sale-range API: alwaysSale 모드 추가 — 선택 품목들을 is_always_sale='Y'
로 설정하면서 날짜는 모두 NULL 로 초기화.
- 품목 관리 일괄 패널: [상시 판매로 설정] 버튼 추가. 안내 문구도 갱신
(상시=항상 노출/날짜 초기화, 해제=미노출/날짜 초기화).
- 목록 판매기간 컬럼: 상시(초록 배지)/날짜범위/미노출(빨강 배지) 3종 명확 표시.
요구 정정: 기존엔 날짜 없으면 자동 "상시" 였으나, 이제 명시적 [상시 판매] 체크가
있어야 출고요청에 노출되고 날짜 없으면 미노출(=거래처 화면에서 안 보임).
- DB: momo_items.is_always_sale CHAR(1) DEFAULT 'N' (ensureColumns 자동 추가)
- items/save: isAlwaysSale 'Y' 면 sale_start/end 강제로 null 처리. INSERT/UPDATE
에 is_always_sale 컬럼 반영. 알림 트리거에 상시 플래그 변경도 포함.
- items/list forSale 필터: is_always_sale='Y' 이거나, 시작/종료 중 하나 설정 +
현재 기간 안 일 때만 노출. (둘 다 NULL + 상시 미체크 = 미노출)
- orders/save on_sale 재판정: items/list 와 동일 규칙으로 마감 차단.
- 품목 관리 편집 폼: [상시 판매] 체크박스 + 체크 시 날짜 입력 비활성/비움.
- 품목 관리 목록: 상시(초록) / 날짜범위 / 미노출(빨강) 3가지 구분 표시.
체크 / 발주번호 / 발주일 / 업체 / 합계 / 상태 6개 컬럼 분리.
table-fixed + 고정 너비(88/72/82/62) 로 가로 스크롤 없이 480px 컨테이너에 맞춤.
업체명: text-sm font-bold(검정) — 가장 크고 굵게.
발주번호/발주일: text-xs tabular-nums 정렬.
부분입고(1000개 중 999개 입고) 시, 입금해야 할 금액은 입고분(999×단가)이지
발주 총액(1000×단가)이 아님. 이를 정확히 반영:
- list API: 발주/입고/미입고 수량(qty)·금액(price) 4개 필드 추가.
- 목록 표시(데스크탑/모바일): 발주 ₩총액(수량) / 입고 ₩입고금액(입고수량) /
미입고 ₩잔액(미입고수량) 3행 정리.
- 입금처리 모달 기본 금액 = 입고금액(있으면)으로 자동 채움 (입고 전이면 발주금액 폴백).
- 입금수정 모달도 동일 정보 표시 + 권장 금액 안내.
데스크탑 좌측 발주 리스트가 좁아서 가로 스크롤이 보이던 문제 + 업체명이 작던 문제.
- 발주번호/발주일을 업체 셀로 합쳐 4열 구조(체크/업체·발주/합계/상태)로 단순화
- 업체명: font-bold text-sm (강조), 그 아래 작은 회색으로 날짜·발주번호
- table-fixed + overflow-x-hidden 로 가로 스크롤 없이 한 화면에
기준 창고에 재고 row 가 없는 품목 출고 시 INSERT INTO momo_stocks(...regdate)
가 실패('column regdate does not exist')해 트랜잭션 롤백 → "승인 중 오류".
momo_stocks 실제 컬럼은 update_date 뿐이라 update_date 로 수정.
(기존 row 가 있던 품목은 UPDATE 경로라 정상 → 그래서 일부만 실패했음)
html-to-image 가 높이를 약간 짧게 잡아 발주서 하단 날짜 줄이 잘리던 문제.
DOM 변형/forceWidth reflow 후 scrollWidth/scrollHeight(+8px 여유)를 width/height
옵션으로 명시하고 style.overflow=visible 로 클립 방지.
입금완료(PAID) 행 동작에 [수정] 버튼 추가 — 입금일/입금액/방법/메모 수정,
또는 [입금 취소]로 입금완료 해제(입고 진행 상태로 복원 + 입금정보 삭제).
- 신규 /api/m/admin/proc-payments/update (action: edit | cancel).
- REQUESTED/PARTIAL/RECEIVED 행 동작은 기존 그대로 유지.