Add editable weather captions and manage Threads scheduling with PM2
This commit is contained in:
+51
-12
@@ -2,7 +2,7 @@
|
||||
|
||||
## 동작
|
||||
|
||||
Linux systemd 타이머가 매일 **한국 시간 03:00**에 작업을 시작합니다. 기존 `calculateSun` / SunCalc 2.x로 당일 관악산 일출을 계산하고, 5분 전에 Chromium을 실행합니다. 배포 페이지의 실제 관측 위치·고도로 계산한 일출 시각까지 기다린 뒤, UI 없는 **1080×1728 세로 JPEG 두 장**을 같은 시각의 장면에서 촬영하고 **한 개의 Threads 캐러셀 게시물**로 올립니다. 사진은 실제 풍경이 아니라 시뮬레이션임을 게시 문구에 표시합니다. 날씨는 페이지의 기존 기상자료를 사용합니다.
|
||||
PM2가 상시 실행하는 워커가 매일 **한국 시간 03:00**에 작업을 시작합니다. 기존 `calculateSun` / SunCalc 2.x로 당일 관악산 일출을 계산하고, 5분 전에 Chromium을 실행합니다. 배포 페이지의 실제 관측 위치·고도로 계산한 일출 시각까지 기다린 뒤, UI 없는 **1080×1728 세로 JPEG 두 장**을 같은 시각의 장면에서 촬영하고 **한 개의 Threads 캐러셀 게시물**로 올립니다. 게시 문구는 별도 JSON 파일에서 수정하며, 촬영에 사용된 날씨에 따라 선택합니다. 날씨는 페이지의 기존 기상자료를 사용합니다.
|
||||
|
||||
사진 순서는 ① 일출 방향을 바라보는 캐릭터 뒷모습과 풍경 ② 캐릭터 얼굴과 하늘입니다. 캐릭터는 일출을 향한 자세를 유지하고 두 번째 카메라만 방위각 +200°, 위쪽 20°로 회전합니다. 기존 3인칭 카메라의 지형 충돌 보정을 사용합니다. 구도와 해상도는 `src/constants/threads.ts`의 `angles`, `viewport`에서 조정합니다. 두 사진 모두 준비된 뒤 캐러셀을 만들며, 한 장이라도 실패하면 일부만 게시하지 않습니다.
|
||||
|
||||
@@ -63,7 +63,7 @@ npm run threads:run -- --dry-run
|
||||
npm run threads:post-now
|
||||
```
|
||||
|
||||
이 명령은 **일출까지 기다리지 않고 현재 시각의 두 앵글을 실제 Threads 계정에 게시**합니다. 게시 문구에는 테스트 게시임과 실제 촬영 시각을 표시합니다. 실행할 때마다 새 테스트 게시물이 생성되며, `test-날짜-UUID.json` 기록을 사용하므로 당일 일출 예약 게시를 막지 않습니다. 예약 서비스와 동일한 일반 사용자로 실행하세요. root에서는 Chromium sandbox가 동작하지 않습니다.
|
||||
이 명령은 **일출까지 기다리지 않고 현재 시각의 두 앵글을 실제 Threads 계정에 게시**합니다. 게시 문구에는 설정된 testPrefix(기본 [테스트])를 붙입니다. 촬영 시각은 템플릿에 {time}을 넣으면 표시됩니다. 실행할 때마다 새 테스트 게시물이 생성되며, `test-날짜-UUID.json` 기록을 사용하므로 당일 일출 예약 게시를 막지 않습니다. 예약 서비스와 동일한 일반 사용자로 실행하세요. root에서는 Chromium sandbox가 동작하지 않습니다.
|
||||
|
||||
게시 ID를 받은 즉시 두 원본 JPEG를 삭제합니다. `Threads published: ...` 다음 `Deleted both source images after publication` 로그를 확인하세요. 삭제 실패 시 게시 기록은 유지하고 다음 게시 작업 시작 때 삭제를 재시도합니다. 게시 실패 또는 결과 불명확 상태에서는 이미지를 보관하며, 촬영 전용 preview 파일도 확인용으로 유지합니다. 서버 원본 삭제는 Threads에 게시된 사진을 삭제하는 작업이 아닙니다. 별도 CDN을 사용한다면 캐시 사본은 CDN의 만료 정책을 따릅니다.
|
||||
|
||||
@@ -73,19 +73,58 @@ npm run threads:post-now
|
||||
|
||||
`npm run build`는 dist를 다시 생성합니다. 운영에서는 **배포와 독립적인 이미지 디렉터리**를 권장합니다. 예를 들어 `"imageDir": "/var/lib/sunrise/captures"`로 지정하고 Nginx에서 `/threads-captures/`를 해당 디렉터리에 연결하세요. Nginx 사용자에게 디렉터리 탐색·파일 읽기 권한을 부여하세요. `imageBaseUrl`도 실제 URL과 일치해야 합니다. 이미지에는 토큰이 포함되지 않습니다. 예약 게시와 테스트 게시 모두 성공 후 원본 이미지를 즉시 삭제합니다.
|
||||
|
||||
## 5. 자동 실행 활성화
|
||||
## 5. 문구 수정과 날씨 조건
|
||||
|
||||
`deploy/sunrise-threads.service`의 사용자, 작업 디렉터리, Node 경로를 수정한 뒤:
|
||||
프로젝트 루트에서 샘플을 복사합니다. 실제 문구 파일은 Git에서 제외되므로 배포로 덮어쓰지 않습니다. 파일이 없으면 샘플을 사용합니다.
|
||||
|
||||
```bash
|
||||
sudo cp deploy/sunrise-threads.service deploy/sunrise-threads.timer /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now sunrise-threads.timer
|
||||
systemctl list-timers sunrise-threads.timer
|
||||
journalctl -u sunrise-threads.service -n 100
|
||||
cp -n .threads-captions.sample.json .threads-captions.json
|
||||
nano .threads-captions.json
|
||||
npm run threads:captions
|
||||
```
|
||||
|
||||
타이머는 03:00에 준비 작업을 시작하고 프로세스가 계산된 일출까지 기다립니다. 서버의 기본 시간대와 관계없이 `Asia/Seoul` 기준입니다. 일출을 놓친 뒤 서버가 켜지면 그날 촬영은 건너뜁니다. 촬영 허용 지연은 최대 30초이며, 로딩 지연이나 절전 등으로 이를 초과하면 게시하지 않습니다. 장애 시 journal에 오류가 남습니다. 별도 알림 채널은 연결하지 않았습니다.
|
||||
- `messages`: 날씨별 문구입니다. 배열의 각 항목은 한 줄이며, 여러 줄을 쓰면 모두 순서대로 표시됩니다(무작위 선택 아님).
|
||||
- `template`: 전체 게시글의 줄 구성입니다. 기본은 문구, 빈 줄, 사이트 링크입니다.
|
||||
- `testPrefix`: 즉시 테스트 게시 앞에 붙일 표시입니다.
|
||||
- 치환값: `{message}`, `{date}`(한국 날짜), `{time}`(촬영 HH:mm), `{sunrise}`(일출 HH:mm), `{url}`.
|
||||
- `thresholds`: 낮은 시정 기준(m), 흐림·구름 조금 운량 기준(%)입니다.
|
||||
|
||||
날씨 우선순위는 눈 → 비 → 낮은 시정 → 흐림 → 구름 조금 → 맑음입니다. 기본 시정 기준은 1000m 미만, 흐림은 운량 80% 이상, 구름 조금은 30% 이상입니다. 강수 정보가 없거나 날씨 로딩/조회가 실패하면 `unknown`을 사용합니다. 낮은 시정은 안개로 단정하지 않도록 기본 문구에 표현했습니다. 웹페이지에 적용된 기상 데이터를 캡처 기록에 함께 저장하며, 재시도도 그 사진의 날씨를 사용합니다.
|
||||
|
||||
문구는 게시 직전에 다시 읽으므로 수정 후 빌드나 PM2 재시작이 필요 없습니다. `npm run threads:captions`는 모든 날씨별 문구를 출력만 하며 게시하지 않습니다. 실제 촬영 시 선택된 문구도 `threads:preview` 로그로 확인할 수 있습니다. 완성된 문구가 500자를 넘거나 JSON이 잘못됐으면 게시를 중단합니다.
|
||||
|
||||
## 6. PM2로 자동 실행
|
||||
|
||||
기존 systemd 작업을 설정했다면 먼저 중지합니다. 두 예약 작업을 동시에 실행하지 마세요.
|
||||
|
||||
```bash
|
||||
sudo systemctl disable --now sunrise-threads.timer
|
||||
sudo systemctl stop sunrise-threads.service
|
||||
```
|
||||
|
||||
이전 systemd 작업이 없다면 위 명령은 생략합니다. PM2는 Chromium을 설치한 **동일한 일반 사용자**로 실행하세요. PM2가 없다면 관리자 권한으로 `npm install -g pm2`를 한 번 실행합니다. 이후 일반 사용자 터미널에서:
|
||||
|
||||
```bash
|
||||
cd /opt/sunrise
|
||||
pm2 start deploy/ecosystem.config.cjs
|
||||
pm2 save
|
||||
pm2 status
|
||||
pm2 logs sunrise-threads --lines 100
|
||||
```
|
||||
|
||||
설정은 프로젝트 위치를 자동으로 계산하고 워커를 단일 fork 프로세스로 실행합니다. 기존 웹 서버 PM2 프로세스는 건드리지 않습니다. 날짜 계산은 서버 시간대와 무관하게 Asia/Seoul 기준이며, 03:00 이후 새로 시작하면 오늘 작업을 바로 확인합니다. 이미 게시했다면 중복 게시하지 않고, 일출을 놓쳤고 저장된 사진도 없다면 다음 날짜를 기다립니다. 실행 중에는 실제 촬영 5분 전에만 Chromium을 엽니다.
|
||||
|
||||
```bash
|
||||
pm2 restart sunrise-threads # 코드/실행 설정 변경 후
|
||||
pm2 stop sunrise-threads # 중지
|
||||
pm2 delete sunrise-threads # PM2에서 제거
|
||||
pm2 save # 변경된 목록 저장
|
||||
```
|
||||
|
||||
재부팅 후 복구는 일반 사용자로 `pm2 startup`을 실행하고, 출력되는 관리자 명령을 그대로 실행한 뒤 `pm2 save`합니다. Linux 배포판에 따라 PM2 자체 부팅 등록에는 systemd가 사용될 수 있지만, 일출 예약과 프로세스 관리는 PM2 워커가 담당합니다.
|
||||
|
||||
- [PM2 프로세스 설정](https://pm2.keymetrics.io/docs/usage/application-declaration/)
|
||||
- [PM2 재부팅 복구](https://pm2.keymetrics.io/docs/usage/startup/)
|
||||
|
||||
## 게시 기록과 재시도
|
||||
|
||||
@@ -93,6 +132,6 @@ journalctl -u sunrise-threads.service -n 100
|
||||
|
||||
`publishing` 상태가 남았다면 요청이 성공했지만 응답이 유실됐을 수 있습니다. 이때 자동으로 재게시하지 않습니다. 계정과 기록의 컨테이너 ID를 확인한 뒤 수동으로 처리하세요. 기존 기록을 무조건 지우면 중복 게시될 수 있습니다.
|
||||
|
||||
촬영 성공 시 별도 `YYYY-MM-DD.capture.json`을 저장합니다. 토큰 갱신·공개 URL 확인·컨테이너 준비가 실패하면 systemd가 60초 뒤 재시도합니다(1시간 내 최대 5회 시작). 같은 날 재실행은 저장된 일출 사진을 재사용하므로 게시 준비 중 오류가 나도 촬영을 잃지 않습니다. 촬영 자체를 놓쳤다면 그날은 건너뜁니다. 다음 날짜에 전날 사진을 자동 게시하지 않습니다.
|
||||
촬영 성공 시 별도 `YYYY-MM-DD.capture.json`을 저장합니다. 토큰 갱신·공개 URL 확인·컨테이너 준비가 실패하면 워커가 60초 뒤 재시도합니다(하루 작업 최대 5회 시도). 같은 날 재실행은 저장된 일출 사진을 재사용하므로 게시 준비 중 오류가 나도 촬영을 잃지 않습니다. 촬영 자체를 놓쳤다면 그날은 건너뜁니다. 다음 날짜에 전날 사진을 자동 게시하지 않습니다.
|
||||
|
||||
정지: `sudo systemctl disable --now sunrise-threads.timer` (이미 실행 중인 작업도 중지하려면 `sudo systemctl stop sunrise-threads.service`).
|
||||
정지: `pm2 stop sunrise-threads`. 게시 결과가 불명확한 작업은 재실행 전 계정과 기록을 확인하세요.
|
||||
|
||||
Reference in New Issue
Block a user