404 lines
8.4 KiB
Markdown
404 lines
8.4 KiB
Markdown
# Collection Keeper
|
|
|
|
개인 NAS 사용자를 위한 초경량 생존 확인 웹앱입니다.
|
|
|
|
- 현재 버전: `v1.0.0`
|
|
- Git 저장소: `https://git.sori.studio/zenn/collection.sori.studio.git`
|
|
- 상세 설치 문서: [`docs/DEPLOY.md`](docs/DEPLOY.md)
|
|
|
|
가끔 방문해서 중앙 버튼을 누르면 확인 시간이 갱신되고, 일정 기간 동안 아무도 누르지 않으면 사용자가 지정한 스크립트가 실행됩니다.
|
|
|
|
Collection Keeper는 Collection 폴더를 직접 삭제하지 않습니다.
|
|
|
|
## 주요 기능
|
|
|
|
- 중앙 버튼 한 개로 생존 확인
|
|
- SQLite 기반 통계 및 방문 흔적 저장
|
|
- 1초 쿨다운 스팸 방지
|
|
- 랜덤 이모지 클릭 효과
|
|
- 배경 이미지 교체 및 랜덤 선택
|
|
- threshold 초과 시 사용자 스크립트 실행
|
|
|
|
## 로컬 실행 가이드
|
|
|
|
개발하거나 먼저 동작을 확인할 때는 로컬에서 실행합니다.
|
|
|
|
필요한 것:
|
|
|
|
- Node.js 20 이상
|
|
- npm
|
|
|
|
설치:
|
|
|
|
```bash
|
|
npm install
|
|
```
|
|
|
|
실행:
|
|
|
|
```bash
|
|
npm start
|
|
```
|
|
|
|
브라우저에서 접속:
|
|
|
|
```text
|
|
http://localhost:40100
|
|
```
|
|
|
|
개발 중 파일 변경을 감지해서 다시 실행하려면:
|
|
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
로컬 실행 시 SQLite 파일은 기본적으로 `data/keeper.sqlite`에 생성됩니다.
|
|
|
|
## Docker 실행 가이드
|
|
|
|
Docker로 한 번에 빌드하고 실행합니다.
|
|
|
|
필요한 것:
|
|
|
|
- Docker
|
|
- Docker Compose
|
|
|
|
실행:
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
```
|
|
|
|
브라우저에서 접속:
|
|
|
|
```text
|
|
http://localhost:40100
|
|
```
|
|
|
|
상태 확인:
|
|
|
|
```bash
|
|
docker compose ps
|
|
```
|
|
|
|
로그 확인:
|
|
|
|
```bash
|
|
docker compose logs -f
|
|
```
|
|
|
|
중지:
|
|
|
|
```bash
|
|
docker compose down
|
|
```
|
|
|
|
다시 시작:
|
|
|
|
```bash
|
|
docker compose restart
|
|
```
|
|
|
|
컨테이너와 네트워크를 중지해도 `keeper-data` 볼륨에 SQLite 데이터가 남습니다.
|
|
|
|
## 상용/NAS 설치 가이드
|
|
|
|
NAS에서 오래 켜둘 때는 Docker Compose 실행을 권장합니다.
|
|
|
|
1. 프로젝트 폴더를 NAS에 둡니다.
|
|
|
|
```bash
|
|
cd /volume1/docker/collection-keeper
|
|
```
|
|
|
|
2. 설정을 확인합니다.
|
|
|
|
```bash
|
|
vi config/default.json
|
|
```
|
|
|
|
자주 바꾸는 항목:
|
|
|
|
- `threshold_days`: 며칠 동안 버튼을 누르지 않으면 스크립트를 실행할지
|
|
- `cooldown_seconds`: 생존 확인으로 인정되는 최소 클릭 간격
|
|
- `trigger_script`: 실행할 사용자 스크립트 경로
|
|
- `background`: `none`, 배경 파일명, 또는 `random`
|
|
|
|
3. 사용자 스크립트를 수정합니다.
|
|
|
|
```bash
|
|
vi scripts/cleanup.sh
|
|
chmod +x scripts/*.sh
|
|
```
|
|
|
|
4. `docker-compose.yml`의 NAS 경로를 확인합니다.
|
|
|
|
기본값은 Synology 스타일 경로입니다.
|
|
|
|
```yaml
|
|
- /home/zenn/Collection:/home/zenn/Collection
|
|
```
|
|
|
|
다른 경로를 쓴다면 왼쪽 경로를 실제 Collection 폴더로 바꿉니다.
|
|
|
|
5. 실행합니다.
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
```
|
|
|
|
6. 로그를 확인합니다.
|
|
|
|
```bash
|
|
docker compose logs -f collection-keeper
|
|
```
|
|
|
|
7. NAS 주소로 접속합니다.
|
|
|
|
```text
|
|
http://NAS_IP:40100
|
|
```
|
|
|
|
예시:
|
|
|
|
```text
|
|
http://192.168.0.10:40100
|
|
```
|
|
|
|
## 업데이트 가이드
|
|
|
|
코드를 새 버전으로 바꾼 뒤 다시 빌드합니다.
|
|
|
|
```bash
|
|
docker compose down
|
|
docker compose up -d --build
|
|
```
|
|
|
|
데이터를 유지하려면 `docker compose down -v`는 사용하지 마세요. `-v` 옵션은 SQLite 데이터가 들어 있는 Docker 볼륨까지 삭제합니다.
|
|
|
|
## 백업 가이드
|
|
|
|
Docker Compose 기본 설정에서는 SQLite DB가 `keeper-data` 볼륨에 저장됩니다.
|
|
|
|
볼륨 이름 확인:
|
|
|
|
```bash
|
|
docker volume ls | grep keeper
|
|
```
|
|
|
|
로컬 실행에서는 `data/keeper.sqlite` 파일을 백업하면 됩니다.
|
|
|
|
```bash
|
|
cp data/keeper.sqlite keeper.backup.sqlite
|
|
```
|
|
|
|
## 멀티 아키텍처 이미지 빌드
|
|
|
|
ARM64와 x86_64 이미지를 함께 빌드하려면 Docker Buildx를 사용합니다.
|
|
|
|
```bash
|
|
docker buildx build --platform linux/amd64,linux/arm64 -t collection-keeper:latest .
|
|
```
|
|
|
|
## 문제 해결
|
|
|
|
### `EADDRINUSE: address already in use :::40100`
|
|
|
|
이미 `40100` 포트를 쓰는 서버가 떠 있을 때 발생합니다.
|
|
|
|
포트를 쓰는 프로세스 확인:
|
|
|
|
```bash
|
|
lsof -nP -iTCP:40100 -sTCP:LISTEN
|
|
```
|
|
|
|
예시 출력에서 `PID`가 `30383`이라면 종료:
|
|
|
|
```bash
|
|
kill 30383
|
|
```
|
|
|
|
다시 실행:
|
|
|
|
```bash
|
|
npm start
|
|
```
|
|
|
|
기존 서버를 끄고 싶지 않다면 다른 포트로 실행할 수도 있습니다.
|
|
|
|
```bash
|
|
PORT=40101 npm start
|
|
```
|
|
|
|
이 경우 브라우저에서는 다음 주소로 접속합니다.
|
|
|
|
```text
|
|
http://localhost:40101
|
|
```
|
|
|
|
## 설정
|
|
|
|
기본 설정은 [`config/default.json`](config/default.json) 에 있습니다.
|
|
|
|
| 항목 | 기본값 | 설명 |
|
|
| --- | --- | --- |
|
|
| `threshold_days` | `90` | 생존 확인 허용 기간 |
|
|
| `threshold_seconds` | `null` | 테스트용 초 단위 생존 확인 기간. 값이 있으면 `threshold_days`보다 우선합니다. |
|
|
| `cooldown_seconds` | `1` | 생존 확인 인정 쿨다운 |
|
|
| `check_interval_seconds` | `60` | 스크립트 검사 주기 |
|
|
| `database_path` | `./data/keeper.sqlite` | SQLite 경로 |
|
|
| `trigger_script` | `./scripts/cleanup.sh` | 조건 충족 시 실행할 스크립트 |
|
|
| `background` | `random` | `none`, 배경 파일명, 또는 `random` |
|
|
| `collection_path` | `/home/zenn/Collection` | Collection 폴더 경로 |
|
|
| `recent_traces_limit` | `5` | 최근 흔적 저장 조회 개수 |
|
|
| `reset_token` | `""` | 초기화 API 보호용 토큰. 비어 있으면 토큰 없이 초기화 가능합니다. |
|
|
|
|
환경 변수로도 덮어쓸 수 있습니다.
|
|
|
|
- `THRESHOLD_DAYS`
|
|
- `COOLDOWN_SECONDS`
|
|
- `CHECK_INTERVAL_SECONDS`
|
|
- `DATABASE_PATH`
|
|
- `TRIGGER_SCRIPT`
|
|
- `COLLECTION_PATH`
|
|
- `BACKGROUND`
|
|
- `PORT`
|
|
- `THRESHOLD_SECONDS`
|
|
- `RESET_TOKEN`
|
|
|
|
## 화면 문구 커스터마이징
|
|
|
|
화면에 보이는 글, 클릭 이모지, 클릭 메시지는 [`public/custom.js`](public/custom.js) 한 파일에서 바꿉니다.
|
|
|
|
코드를 몰라도 이 파일의 따옴표 안 문장과 이모지만 수정하면 됩니다.
|
|
|
|
```js
|
|
window.COLLECTION_KEEPER_CUSTOM = {
|
|
keeperTextLines: [
|
|
'가끔은 생각해.',
|
|
'',
|
|
'누군가 버튼을 누른다는 건',
|
|
'이곳을 기억하고 있다는 뜻일까,',
|
|
],
|
|
|
|
farewellTextLines: [
|
|
'안녕.',
|
|
'',
|
|
'고마웠어.',
|
|
],
|
|
|
|
emojis: ['🐱', '🐰', '🌸', '✨'],
|
|
|
|
clickMessages: [
|
|
'기록은 남겨둘게.',
|
|
'아직 이곳을 기억하고 있어.',
|
|
],
|
|
};
|
|
```
|
|
|
|
- `keeperTextLines`: 오른쪽 아래에 표시되는 글입니다. 빈 줄은 `''`로 넣습니다.
|
|
- `farewellTextLines`: 남은 기간이 0이 되었을 때 표시되는 작별 인사입니다.
|
|
- `emojis`: 클릭할 때 기록되고 표시되는 이모지입니다.
|
|
- `clickMessages`: 클릭할 때 이모지 뒤에 붙는 랜덤 문장입니다.
|
|
|
|
## 만료 테스트와 초기화
|
|
|
|
만료 화면을 빠르게 확인하려면 초 단위 타이머를 사용합니다.
|
|
|
|
```bash
|
|
THRESHOLD_SECONDS=10 npm start
|
|
```
|
|
|
|
10초가 지나면 화면이 작별 인사로 전환되고, 더 이상 클릭해도 확인 시간이 갱신되지 않습니다.
|
|
|
|
다시 처음 상태로 초기화:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:40100/api/reset
|
|
```
|
|
|
|
초기화 API에 토큰을 걸고 싶으면 `RESET_TOKEN`을 설정합니다.
|
|
|
|
```bash
|
|
RESET_TOKEN=my-secret THRESHOLD_SECONDS=10 npm start
|
|
```
|
|
|
|
토큰이 있을 때 초기화:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:40100/api/reset \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"token":"my-secret"}'
|
|
```
|
|
|
|
## 배경 이미지
|
|
|
|
`assets/backgrounds/` 폴더에 이미지를 넣고 설정에서 지정합니다. 기본값은 `random`입니다.
|
|
|
|
예시:
|
|
|
|
```json
|
|
{
|
|
"background": "bg01.jpg"
|
|
}
|
|
```
|
|
|
|
랜덤 사용:
|
|
|
|
```json
|
|
{
|
|
"background": "random"
|
|
}
|
|
```
|
|
|
|
배경 없이 사용:
|
|
|
|
```json
|
|
{
|
|
"background": "none"
|
|
}
|
|
```
|
|
|
|
## 사용자 스크립트
|
|
|
|
`scripts/` 폴더에 실행할 스크립트를 둡니다.
|
|
|
|
예시:
|
|
|
|
- `scripts/warning.sh`
|
|
- `scripts/encrypt.sh`
|
|
- `scripts/cleanup.sh`
|
|
|
|
스크립트에는 다음 환경 변수가 전달됩니다.
|
|
|
|
- `COLLECTION_PATH`
|
|
- `KEEPER_SCRIPT`
|
|
|
|
## 안전 원칙
|
|
|
|
Collection Keeper는 다음 역할만 수행합니다.
|
|
|
|
1. 마지막 확인 시간 관리
|
|
2. 통계 관리
|
|
3. 버튼 이벤트 처리
|
|
4. 조건 충족 여부 판단
|
|
5. 사용자 지정 스크립트 실행
|
|
|
|
파일 삭제, 암호화, 정리 같은 실제 작업은 반드시 사용자 스크립트에서 구현해야 합니다.
|
|
|
|
## API
|
|
|
|
- `GET /api/status`
|
|
- `POST /api/check`
|
|
|
|
버튼 클릭은 반드시 `POST /api/check` 로 처리됩니다. 단순 방문만으로는 생존 확인이 갱신되지 않습니다.
|
|
|
|
## NAS 볼륨 예시
|
|
|
|
```yaml
|
|
volumes:
|
|
- keeper-data:/data
|
|
- ./scripts:/scripts:ro
|
|
- /home/zenn/Collection:/home/zenn/Collection
|
|
```
|