Files
deadman-switch/README.md
zenn aeaa1b37c2 feat: rename app to Last Signal
Rename service, UI, storage, and target-path configuration; clarify expiration cleanup behavior.
2026-09-30 11:55:58 +09:00

10 KiB

Last Signal

개인 NAS 사용자를 위한 초경량 생존 확인 웹앱입니다.

  • 현재 버전: v1.0.0
  • Git 저장소: https://git.sori.studio/zenn/collection.sori.studio.git
  • 상세 설치 문서: docs/DEPLOY.md

가끔 방문해서 중앙 버튼을 누르면 확인 시간이 갱신되고, 일정 기간 동안 아무도 누르지 않으면 사용자가 지정한 스크립트가 실행됩니다.

마지막 생존 확인 후 설정된 스크립트를 실행합니다. 기본 scripts/cleanup.sh는 TARGET_PATH로 지정한 폴더의 내용 전체를 삭제하며, 폴더 자체는 남깁니다. 기본 대상은 /home/zenn/Collection입니다.

주요 기능

  • 중앙 버튼 한 개로 생존 확인
  • SQLite 기반 통계 및 방문 흔적 저장
  • 1초 쿨다운 스팸 방지
  • 랜덤 이모지 클릭 효과
  • 배경 이미지 교체 및 랜덤 선택
  • threshold 초과 시 사용자 스크립트 실행

로컬 실행 가이드

개발하거나 먼저 동작을 확인할 때는 로컬에서 실행합니다.

필요한 것:

  • Node.js 20 이상
  • npm

설치:

npm install

실행:

npm start

브라우저에서 접속:

http://localhost:40100

개발 중 파일 변경을 감지해서 다시 실행하려면:

npm run dev

로컬 실행 시 SQLite 파일은 기본적으로 data/last-signal.sqlite에 생성됩니다.

Docker 실행 가이드

Docker로 한 번에 빌드하고 실행합니다.

필요한 것:

  • Docker
  • Docker Compose

실행:

docker compose up -d --build

브라우저에서 접속:

http://localhost:44092

상태 확인:

docker compose ps

로그 확인:

docker compose logs -f

중지:

docker compose down

다시 시작:

docker compose restart

컨테이너와 네트워크를 중지해도 last-signal-data 볼륨에 SQLite 데이터가 남습니다.

상용/NAS 설치 가이드

NAS에서 오래 켜둘 때는 Docker Compose 실행을 권장합니다.

  1. 프로젝트 폴더를 NAS에 둡니다.
cd /volume1/docker/last-signal
  1. 설정을 확인합니다.
vi config/default.json

자주 바꾸는 항목:

  • threshold_days: 며칠 동안 버튼을 누르지 않으면 스크립트를 실행할지
  • cooldown_seconds: 생존 확인으로 인정되는 최소 클릭 간격
  • trigger_script: 실행할 사용자 스크립트 경로
  • background: none, 배경 파일명, 또는 random
  1. 사용자 스크립트를 수정합니다.
vi scripts/cleanup.sh
chmod +x scripts/*.sh
  1. docker-compose.yml의 NAS 경로를 확인합니다.

기본값은 Synology 스타일 경로입니다.

- /home/zenn/Collection:/home/zenn/Collection

다른 경로를 쓴다면 왼쪽 경로를 실제 Collection 폴더로 바꿉니다.

  1. 실행합니다.
docker compose up -d --build
  1. 로그를 확인합니다.
docker compose logs -f last-signal
  1. NAS 주소로 접속합니다.
http://NAS_IP:44092

예시:

http://192.168.0.10:44092

업데이트 가이드

코드를 새 버전으로 바꾼 뒤 다시 빌드합니다.

docker compose down --remove-orphans
docker compose up -d --build

--remove-orphans는 이전 서비스 이름으로 남은 컨테이너를 정리합니다. 데이터를 유지하려면 docker compose down -v는 사용하지 마세요. -v 옵션은 SQLite 데이터가 들어 있는 Docker 볼륨까지 삭제합니다.

백업 가이드

Docker Compose 기본 설정에서는 SQLite DB가 last-signal-data 볼륨에 저장됩니다.

볼륨 이름 확인:

docker volume ls | grep last-signal

로컬 실행에서는 data/last-signal.sqlite 파일을 백업하면 됩니다.

cp data/last-signal.sqlite last-signal.backup.sqlite

멀티 아키텍처 이미지 빌드

ARM64와 x86_64 이미지를 함께 빌드하려면 Docker Buildx를 사용합니다.

docker buildx build --platform linux/amd64,linux/arm64 -t last-signal:latest .

문제 해결

EADDRINUSE: address already in use :::40100

이미 40100 포트를 쓰는 서버가 떠 있을 때 발생합니다.

포트를 쓰는 프로세스 확인:

lsof -nP -iTCP:40100 -sTCP:LISTEN

예시 출력에서 PID가 30383이라면 종료:

kill 30383

다시 실행:

npm start

기존 서버를 끄고 싶지 않다면 다른 포트로 실행할 수도 있습니다.

PORT=40101 npm start

이 경우 브라우저에서는 다음 주소로 접속합니다.

http://localhost:40101

설정

기본 설정은 config/default.json 에 있습니다.

항목 기본값 설명
threshold_days 90 생존 확인 허용 기간
threshold_seconds null 테스트용 초 단위 생존 확인 기간. 값이 있으면 threshold_days보다 우선합니다.
cooldown_seconds 1 생존 확인 인정 쿨다운
check_interval_seconds 60 스크립트 검사 주기
database_path ./data/last-signal.sqlite SQLite 경로
trigger_script ./scripts/cleanup.sh 조건 충족 시 실행할 스크립트
background random none, 배경 파일명, 또는 random
target_path /home/zenn/Collection 만료 시 스크립트가 처리할 대상 폴더
recent_traces_limit 5 최근 흔적 저장 조회 개수
reset_token "" 초기화 API 보호용 토큰. 비어 있으면 토큰 없이 초기화 가능합니다.
public_base_url https://collection.sori.studio 메일 확인 링크에 사용할 공개 URL
mail_enabled false Gmail 알림 메일 사용 여부
mail_smtp_user "" Gmail SMTP 계정
mail_smtp_pass "" Gmail 앱 비밀번호
mail_to zenn.message@gmail.com 알림 수신 주소
mail_confirm_token "" 메일 확인 링크 보호 토큰

환경 변수로도 덮어쓸 수 있습니다.

  • THRESHOLD_DAYS
  • COOLDOWN_SECONDS
  • CHECK_INTERVAL_SECONDS
  • DATABASE_PATH
  • TRIGGER_SCRIPT
  • TARGET_PATH
  • BACKGROUND
  • PORT
  • THRESHOLD_SECONDS
  • RESET_TOKEN
  • PUBLIC_BASE_URL
  • MAIL_ENABLED
  • MAIL_SMTP_USER
  • MAIL_SMTP_PASS
  • MAIL_TO
  • MAIL_CONFIRM_TOKEN

이전 설정과의 호환을 위해 COLLECTION_PATH와 collection_path도 읽습니다. 새 설정에는 TARGET_PATH와 target_path를 사용하세요.

화면 문구 커스터마이징

화면에 보이는 글, 클릭 이모지, 클릭 메시지는 public/custom.js 한 파일에서 바꿉니다.

코드를 몰라도 이 파일의 따옴표 안 문장과 이모지만 수정하면 됩니다.

window.LAST_SIGNAL_CUSTOM = {
  keeperTextLines: [
    '가끔은 생각해.',
    '',
    '누군가 버튼을 누른다는 건',
    '이곳을 기억하고 있다는 뜻일까,',
  ],

  farewellTextLines: [
    '안녕.',
    '',
    '고마웠어.',
  ],

  emojis: ['🐱', '🐰', '🌸', '✨'],

  clickMessages: [
    '기록은 남겨둘게.',
    '아직 이곳을 기억하고 있어.',
  ],
};
  • keeperTextLines: 오른쪽 아래에 표시되는 글입니다. 빈 줄은 ''로 넣습니다.
  • farewellTextLines: 남은 기간이 0이 되었을 때 표시되는 작별 인사입니다.
  • emojis: 클릭할 때 기록되고 표시되는 이모지입니다.
  • clickMessages: 클릭할 때 이모지 뒤에 붙는 랜덤 문장입니다.

만료 테스트와 초기화

만료 화면을 빠르게 확인하려면 초 단위 타이머를 사용합니다.

THRESHOLD_SECONDS=10 npm start

10초가 지나면 화면이 작별 인사로 전환되고, 더 이상 클릭해도 확인 시간이 갱신되지 않습니다.

다시 처음 상태로 초기화:

curl -X POST http://localhost:40100/api/reset

초기화 API에 토큰을 걸고 싶으면 RESET_TOKEN을 설정합니다. Docker 운영 환경에서는 Git에 올리지 않는 .env 파일을 사용하세요.

RESET_TOKEN=my-secret THRESHOLD_SECONDS=10 npm start

Docker Compose:

cat > .env <<'EOF'
RESET_TOKEN=my-secret
EOF

docker compose up -d --build

Gmail 알림 메일

마지막 확인 이후 60일, 75일, 89일이 지나면 Gmail SMTP로 알림 메일을 보낼 수 있습니다.

.env 예시:

PUBLIC_BASE_URL=https://collection.sori.studio
MAIL_ENABLED=true
MAIL_SMTP_USER=zenn.message@gmail.com
MAIL_SMTP_PASS=구글-앱-비밀번호
MAIL_TO=zenn.message@gmail.com
MAIL_CONFIRM_TOKEN=긴-랜덤-확인-토큰

메일의 [ ❤️ 생존 확인하기 ] 버튼은 사이트를 다시 열 필요 없이 /confirm?token=... 링크로 바로 생존 확인을 갱신합니다.

Google 계정에서는 일반 비밀번호가 아니라 앱 비밀번호를 사용해야 합니다.

토큰이 있을 때 초기화:

curl -X POST http://localhost:40100/api/reset \
  -H 'Content-Type: application/json' \
  -d '{"token":"my-secret"}'

배경 이미지

assets/backgrounds/ 폴더에 이미지를 넣고 설정에서 지정합니다. 기본값은 random입니다.

예시:

{
  "background": "bg01.jpg"
}

랜덤 사용:

{
  "background": "random"
}

배경 없이 사용:

{
  "background": "none"
}

사용자 스크립트

scripts/ 폴더에 실행할 스크립트를 둡니다.

예시:

  • scripts/warning.sh
  • scripts/encrypt.sh
  • scripts/cleanup.sh

스크립트에는 다음 환경 변수가 전달됩니다.

  • TARGET_PATH
  • TRIGGER_SCRIPT

기존 스크립트 호환을 위해 COLLECTION_PATH와 KEEPER_SCRIPT도 같은 값으로 전달됩니다. 새 스크립트에는 새 이름을 사용하세요.

안전 원칙

Last Signal은 다음 역할을 수행합니다.

  1. 마지막 확인 시간 관리
  2. 통계 관리
  3. 버튼 이벤트 처리
  4. 조건 충족 여부 판단
  5. 사용자 지정 스크립트 실행

파일 처리 동작은 실행 스크립트에 달려 있습니다. 기본 scripts/cleanup.sh는 TARGET_PATH 폴더의 내용을 전부 삭제합니다. 다른 작업을 원하면 해당 스크립트를 수정하고, 실제 데이터가 아닌 임시 폴더에서 먼저 검증하세요.

API

  • GET /api/status
  • POST /api/check

버튼 클릭은 반드시 POST /api/check 로 처리됩니다. 단순 방문만으로는 생존 확인이 갱신되지 않습니다.

NAS 볼륨 예시

volumes:
  - last-signal-data:/data
  - ./scripts:/scripts:ro
  - /home/zenn/Collection:/home/zenn/Collection