이 페이지에서
JunkBox apps/message-api 가이드
1. 애플리케이션 역할
apps/message-api는 홈서버 애플리케이션과 셸 스크립트의 메시지 발송 공통 진입점임.
외부 요청, 웹 즉시 발송, 예약 발송, 정기 발송, 수동 재발송은 하나의 메시지 발송 서비스 계층을 사용함.
메시지 요청을 SQLite 이력에 먼저 저장하고 Apprise API 호출 결과를 같은 이력에 반영함.
Apprise API는 실제 채널 전달 엔진이며 Telegram, Discord 등의 채널 연결 정보는 Message 앱이 소유하지 않음.
Message 앱은 셸 스크립트 실행, crontab 관리, 대형 메시지 브로커, 직접 Telegram/Discord 연동을 소유하지 않음.
2. 런타임 구성
FastAPI 엔트리포인트는 junkbox_message_api.main:app임.
기본 루트 /는 /messages로 리다이렉트됨.
상태 확인 경로는 /health임.
apps/message-ui/dist는 /message-ui 경로로 직접 마운트함.
libs/shared-ui 산출 자산은 /shared-ui 경로로 직접 마운트함.
웹 화면은 shared-ui base.html을 상속한 message.html 앱 셸 위에 React SPA를 마운트함.
템플릿은 shared_ui_asset()으로 공통 CSS/JS에 파일 수정 시각 기반 버전 쿼리를 붙이고, stylesheet 매크로로 hub-ui-base.css + hub-ui-mobile.css 조합을 로드함.
페이지 전용 CSS, inline <style>, inline style은 사용하지 않음.
2.1 로컬 개발 및 운영 포트
로컬 비컨테이너 기본 포트는 API 18004, Vite 15004임.
운영 컨테이너 포트는 8004이며 Docker Compose 서비스명은 junkbox-message임.
개발·운영 Compose는 junkbox-hub 이후에 시작하며 /sorc001/junkbox/db를 /app/db로 마운트함.
운영 환경은 /sorc001/junkbox/env/.env.shared, /sorc001/junkbox/env/.env.prod를 사용하고 컨테이너 내부 ENV_FILE=/app/env/.env.prod 기준으로 로드함.
운영 로그는 /logs001/junkbox/message-api를 컨테이너 /app/logs에 마운트하며 공통 앱 로그 경로는 /app/logs/app.log임.
Dockerfile은 shared-ui 자산과 message-ui Vite 산출물을 빌드한 뒤 런타임 이미지에 포함함.
GitHub Actions 배포 감지는 apps/message-api/**, apps/message-ui/**, libs/shared-core/**, libs/shared-ui/**, docker-compose.prod.yml을 junkbox-message 이미지 빌드·배포 대상으로 연결함.
3. 설정 계약
MESSAGE_DATABASE_URL은 Message SQLite DSN임.
로컬 기본 DSN은 sqlite+aiosqlite:////sorc001/junkbox/db/message.sqlite3임.
Docker 기준 DSN은 sqlite+aiosqlite:////app/db/message.sqlite3임.
Apprise 연동은 settings.modules.message 설정을 사용함.
Apprise 기본 주소, 기본 설정 키, 연결 제한시간은 Message 모듈 환경설정으로 관리함.
요청별 config_key가 있으면 기본 설정 키보다 우선함.
현재 Apprise 요청은 POST /notify/{config_key} 형식이며 JSON 본문 title, body, type을 전달함.
메시지 유형은 info, success, warning, failure만 허용함.
4. 데이터 저장소와 스케줄러
SQLite 파일은 /sorc001/junkbox/db/message.sqlite3이며 Message 앱이 단일 writer임.
tb_message_log_n은 모든 발송 요청과 발송 시도를 저장함.
출처, 제목, 본문, 메시지 유형, 설정 키, 요청 방식, 요청·예정·시도·완료 시각, 처리 상태, Apprise 응답·오류, 원본 메시지 식별자, 외부 요청 식별자, JSON 메타데이터를 보존함.
정기 실행 이력은 recurrence_id + recurrence_scheduled_at 유니크 제약으로 같은 실행 시각의 중복 이력을 방지함.
tb_message_schedule_n은 단일 예약 정의와 연결된 발송 이력을 분리해 저장함.
tb_message_recurrence_n은 정기 일정 정의를 저장하며 실제 실행마다 새 tb_message_log_n 행을 생성함.
메시지 상태는 PENDING, PROCESSING, SUCCESS, FAILED, CANCELED, EXPIRED를 사용함.
요청 방식은 발송 진입 경로를 구분하는 이력 필드로 저장함.
앱 lifespan에서 스키마를 보장하고 MessageScheduler를 시작함.
스케줄러는 SQLite의 상태 전이와 선점 갱신으로 due 예약과 정기 실행을 처리하며, 처리 중 또는 완료 이력을 덮어쓰지 않음.
Apprise 네트워크 오류, 시간 초과, HTTP 오류, 비정상 응답은 메시지 이력의 실패 상태와 오류 내용으로 남김.
5. 웹 라우트
GET /messages: 즉시 발송 화면임.
GET /message-history: 발송 이력 화면임.
GET /message-schedules, GET /message-schedules/new: 예약 메시지 목록 및 등록 화면임.
GET /message-recurrences, GET /message-recurrences/new: 정기 메시지 목록 및 등록 화면임.
모든 웹 라우트는 같은 Message 앱 셸을 반환하며 실제 본문은 Message React Router가 선택함.
앱 셸 메뉴는 즉시 발송, 발송 이력, 예약 메시지, 정기 메시지로 구성함.
6. HTTP API
모든 업무 API의 접두사는 /api/message임.
POST /messages: 즉시 메시지 발송 요청을 저장하고 Apprise로 전달함.
GET /messages: 페이지, 크기, 출처, 상태, 메시지 유형, 요청 방식, 검색어 기준 이력을 조회함.
GET /messages/{message_id}: 메시지 상세와 발송 결과를 조회함.
POST /messages/{message_id}/resend: 원본 이력을 보존한 새 발송 이력을 생성해 재발송함.
POST /schedules, GET /schedules, PUT /schedules/{schedule_id}, DELETE /schedules/{schedule_id}: 예약 메시지를 관리함.
POST /recurrences, GET /recurrences, PUT /recurrences/{recurrence_id}, POST /recurrences/{recurrence_id}/active, DELETE /recurrences/{recurrence_id}: 정기 메시지를 관리함.
정기 메시지 반복 유형은 DAILY, WEEKLY, MONTHLY, CRON임.
CRON 반복 설정은 저장 전에 유효성을 검증함.
7. 인증과 입력 경계
현재 Message 웹 라우트와 발송 API는 자체 JWT 인증·allowed_modules 권한 검증을 적용하지 않음.
외부 발송 API에는 현재 별도 인증 헤더를 요구하지 않음.
인증 추가 시 웹 관리자와 외부 발송 API의 의존성을 분리하고 shared-core JWT/Cookie/Header 계약 및 Hub 권한 구조를 사용함.
요청 모델은 출처, 제목, 본문, 설정 키, 외부 요청 식별자, 메타데이터의 길이와 메시지 유형을 검증함.
Apprise 주소와 설정 키는 API 응답이나 화면에 노출하지 않음.
8. 공통 UI 및 문구 규칙
앱 셸, 브랜드 헤더, 모바일 메뉴, 드로어, 카드, 입력창, 바텀 액션바, 바텀시트는 shared-ui 자산을 사용함.
화면 폰트는 PretendardVariable.woff2를 공급하는 shared-ui 중앙 폰트 선언만 사용함.
일반 UI 문구와 사용자 메시지는 DB 메시지 마스터로 관리하지 않고 소스의 한국어 문구로 작성함.
문장형 사용자 노출 메시지는 하십시오체와 마침표를 사용함.
공통코드나 메타의 데이터값은 기존 Hub 마스터데이터 계약을 사용하며 화면 문구 목적으로 신규 키를 만들지 않음.