From e50ff3fb635660d4fd0a9a2edc35510a9663df2c Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Tue, 17 Mar 2026 10:24:09 +0900 Subject: [PATCH] =?UTF-8?q?feat(ntfy):=20=ED=91=B8=EC=8B=9C=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EC=84=9C=EB=B2=84=20Phase=201=20=EC=9D=B8=ED=94=84?= =?UTF-8?q?=EB=9D=BC=20=EA=B5=AC=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docker-compose.yml에 ntfy 서비스 추가 (포트 30750) - ntfy/etc/server.yml 서버 설정 (인증 deny-all, 72h 캐시) - ntfy/README.md 운영 매뉴얼 Co-Authored-By: Claude Opus 4.6 --- docker-compose.yml | 21 ++++++ ntfy/README.md | 167 ++++++++++++++++++++++++++++++++++++++++++++ ntfy/etc/server.yml | 23 ++++++ 3 files changed, 211 insertions(+) create mode 100644 ntfy/README.md create mode 100644 ntfy/etc/server.yml diff --git a/docker-compose.yml b/docker-compose.yml index 2681e8c..ab8d5ab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -481,6 +481,25 @@ services: networks: - tk-network + # ================================================================= + # ntfy — 푸시 알림 서버 + # ================================================================= + + ntfy: + image: binwiederhier/ntfy + container_name: tk-ntfy + restart: unless-stopped + command: serve + ports: + - "30750:80" + environment: + - TZ=Asia/Seoul + volumes: + - ./ntfy/etc:/etc/ntfy + - ntfy_cache:/var/cache/ntfy + networks: + - tk-network + # ================================================================= # AI Service — 맥미니로 이전됨 (~/docker/tk-ai-service/) # ================================================================= @@ -544,6 +563,7 @@ services: - tksafety-web - tksupport-web - tkeg-web + - ntfy networks: - tk-network @@ -563,6 +583,7 @@ volumes: name: tkqc-package_uploads tkeg_postgres_data: tkeg_uploads: + ntfy_cache: networks: tk-network: driver: bridge diff --git a/ntfy/README.md b/ntfy/README.md new file mode 100644 index 0000000..1b9fdf6 --- /dev/null +++ b/ntfy/README.md @@ -0,0 +1,167 @@ +# ntfy 푸시 알림 서버 — 운영 매뉴얼 + +## 개요 + +ntfy는 Web Push(VAPID)의 iOS 제한, 전송 보장 부재 등을 보완하는 푸시 알림 채널이다. +모바일 ntfy 앱으로 알림을 수신하고, 탭하면 딥링크로 해당 페이지로 이동한다. + +- **Docker 서비스명**: `ntfy` +- **내부 URL**: `http://ntfy:80` (Docker 네트워크) +- **외부 URL**: `https://ntfy.technicalkorea.net` (Cloudflare Tunnel) +- **호스트 포트**: `30750` +- **설정 파일**: `ntfy/etc/server.yml` +- **데이터**: `ntfy_cache` Docker 볼륨 (`/var/cache/ntfy`) + +## 초기 설정 (최초 1회) + +### 1. Cloudflare Tunnel 설정 + +Zero Trust 대시보드 → Tunnels → Public Hostname 추가: + +| Subdomain | Domain | Service | +|-----------|--------|---------| +| ntfy | technicalkorea.net | http://ntfy:80 | + +### 2. 컨테이너 기동 + +```bash +docker compose up -d ntfy +``` + +### 3. 관리자 계정 + 토큰 발급 + +```bash +# 관리자 계정 생성 (비밀번호 입력 프롬프트) +docker exec -it tk-ntfy ntfy user add --role=admin admin + +# API 토큰 발급 +docker exec -it tk-ntfy ntfy token add admin +# 출력 예: tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2 +``` + +### 4. .env에 토큰 설정 + +```env +NTFY_PUBLISH_TOKEN=<위에서 발급받은 토큰> +``` + +## 메시지 발행 (서버 → 사용자) + +### curl로 테스트 + +```bash +# 기본 메시지 +curl -H "Authorization: Bearer $NTFY_PUBLISH_TOKEN" \ + -d "테스트 메시지입니다." \ + http://localhost:30750/tkfactory-test + +# 제목 + 딥링크 포함 +curl -H "Authorization: Bearer $NTFY_PUBLISH_TOKEN" \ + -H "Title: 설비수리 요청" \ + -H "Tags: wrench" \ + -H "Click: https://tkfb.technicalkorea.net/pages/admin/repair-management.html" \ + -d "A동 CNC 설비 수리 요청이 접수되었습니다." \ + http://localhost:30750/tkfactory-user-1 + +# 우선순위 높은 알림 (5=max, 3=default, 1=min) +curl -H "Authorization: Bearer $NTFY_PUBLISH_TOKEN" \ + -H "Title: 긴급 안전 알림" \ + -H "Priority: 5" \ + -H "Tags: warning" \ + -d "즉시 확인이 필요합니다." \ + http://localhost:30750/tkfactory-user-1 +``` + +### 토픽 네이밍 규칙 + +| 토픽 | 용도 | +|------|------| +| `tkfactory-user-{userId}` | 사용자별 개인 알림 (Phase 2에서 사용) | +| `tkfactory-test` | 테스트용 | + +### 주요 헤더 + +| 헤더 | 설명 | 예시 | +|------|------|------| +| `Title` | 알림 제목 | `설비수리 요청` | +| `Click` | 탭 시 열릴 URL (딥링크) | `https://tkfb.technicalkorea.net/pages/work/tbm.html` | +| `Tags` | 이모지 태그 ([목록](https://docs.ntfy.sh/emojis/)) | `wrench`, `warning`, `white_check_mark` | +| `Priority` | 1(min) ~ 5(max) | `5` | +| `Authorization` | 인증 토큰 | `Bearer tk_...` | + +## 사용자 관리 + +```bash +# 사용자 목록 +docker exec tk-ntfy ntfy user list + +# 일반 사용자 추가 +docker exec -it tk-ntfy ntfy user add username + +# 사용자 삭제 +docker exec tk-ntfy ntfy user del username + +# 특정 토픽 접근 권한 부여 (read-write / read-only / write-only) +docker exec tk-ntfy ntfy access username 'tkfactory-user-*' read-only + +# 토큰 발급 +docker exec tk-ntfy ntfy token add username + +# 토큰 목록 +docker exec tk-ntfy ntfy token list + +# 토큰 삭제 +docker exec tk-ntfy ntfy token remove username tk_... +``` + +## 모바일 앱 설정 (수신자용) + +### Android +1. Play Store에서 **ntfy** 설치 +2. 설정(⚙️) → **Default server** → `https://ntfy.technicalkorea.net` 입력 +3. 우측 상단 사용자 아이콘 → 로그인 (발급받은 계정/비밀번호 또는 토큰) +4. **+** → 토픽 `tkfactory-user-{본인userId}` 구독 + +### iOS +1. App Store에서 **ntfy** 설치 +2. Settings → **Default server** → `https://ntfy.technicalkorea.net` 입력 +3. 로그인 후 토픽 구독 (Android와 동일) + +## 서버 설정 (server.yml) + +| 항목 | 현재 값 | 설명 | +|------|---------|------| +| `auth-default-access` | `deny-all` | 인증 없이 접근 불가 | +| `cache-duration` | `72h` | 메시지 보관 기간 (주말 포함 3일) | +| `visitor-request-limit-burst` | `60` | 버스트 요청 한도 | +| `visitor-request-limit-replenish` | `5s` | 요청 한도 보충 주기 | + +설정 변경 후 컨테이너 재시작: +```bash +docker compose restart ntfy +``` + +## 트러블슈팅 + +### 401 Unauthorized +- 토큰이 맞는지 확인: `docker exec tk-ntfy ntfy token list` +- `auth-default-access: deny-all` 상태에서 토큰 없이 요청하면 발생 + +### 모바일 앱에서 알림이 안 옴 +- Cloudflare Tunnel Public Hostname에 `ntfy.technicalkorea.net` 등록되었는지 확인 +- 앱의 Default server URL이 `https://ntfy.technicalkorea.net`인지 확인 +- 앱에서 로그인했는지, 토픽을 구독했는지 확인 +- 폰 설정에서 ntfy 앱 알림 권한이 켜져 있는지 확인 + +### 컨테이너 로그 확인 +```bash +docker logs tk-ntfy --tail 50 +docker logs tk-ntfy -f # 실시간 +``` + +## Phase 2 예정 사항 (참고) + +- `system1-factory/api/models/notificationModel.js`의 `sendPushToUsers()`에서 ntfy 발송 연동 +- `push_subscriptions` 테이블에 `channel` 컬럼 추가 (`web_push` | `ntfy`) +- ntfy 구독 사용자에게는 Web Push 미발송 (중복 방지) +- notification-bell.js에서 ntfy 구독 토글 UI 추가 diff --git a/ntfy/etc/server.yml b/ntfy/etc/server.yml new file mode 100644 index 0000000..6cf2129 --- /dev/null +++ b/ntfy/etc/server.yml @@ -0,0 +1,23 @@ +# ntfy server configuration +# Docs: https://docs.ntfy.sh/config/ + +# Base URL (Cloudflare Tunnel 경유) +base-url: "https://ntfy.technicalkorea.net" + +# Listen on port 80 inside the container +listen-http: ":80" + +# Cache — 72시간 (금요일 알림 → 월요일 확인 가능) +cache-duration: "72h" +cache-file: "/var/cache/ntfy/cache.db" + +# Auth — 기본 접근 거부, 토큰 인증 필수 +auth-default-access: "deny-all" +auth-file: "/var/cache/ntfy/user.db" + +# Attachment (비활성화 — Phase 1에서는 텍스트 알림만) +# attachment-cache-dir: "/var/cache/ntfy/attachments" + +# Rate limiting +visitor-request-limit-burst: 60 +visitor-request-limit-replenish: "5s"