Use visible NAS upload storage and reconcile missing files

This commit is contained in:
2026-10-13 15:54:27 +09:00
parent 50e54bc4c5
commit 9e0f493228
26 changed files with 1075 additions and 119 deletions

View File

@@ -6,7 +6,7 @@
아래 코드 블록을 하나씩 복사하고 Enter를 누르세요. 오류가 나오면 다음 단계로 넘어가지 마세요.
이미 배포했다면 **7. 업데이트**부터 진행하세요. 이번 업데이트는 **자동 근처 검색·기종 구분·연결 방식 선택·4자리 초대 코드·모바일 입력/진행률 개선**를 추가하며 기존 DB·업로드 볼륨을 유지합니다. 새 도메인이나 외부 포트는 추가하지 않습니다.
이미 배포했다면 이번에는 **7-1. 업로드 폴더 전환과 테스트 자료 초기화**부터 진행하세요. 파일은 hop 폴더 안의 `uploads`에 저장합니다. 사용자가 기존 테스트 자료 삭제를 요청했으므로 최초 전환 때만 DB·기존 업로드를 초기화합니다. `.env`, 도메인·포트·메일 설정은 유지합니다. 전환 후 일반 업데이트는 7단계만 진행합니다.
**‘허용되지 않은 접속 경로’가 나오면:** 주소창을 `https://hop.noname.company`로 바꾸세요. `http://`는 운영 `APP_URL`과 달라 차단됩니다. 5-1단계에서 HTTP를 HTTPS로 자동 이동시킵니다.
@@ -47,7 +47,7 @@ git branch --show-current
## 2. NAS 설정 만들기
```sh
sh scripts/configure-nas.sh
sudo sh scripts/configure-nas.sh
```
질문이 나오면 순서대로 입력합니다.
@@ -56,6 +56,8 @@ sh scripts/configure-nas.sh
2. **Resend API 키**: 기존 `hello@noname.company` 발신에 사용하는 키 붙여넣기 → Enter. 입력 문자는 숨겨집니다. 메일 기능 없이 시작하려면 Enter만 누릅니다.
3. **회원가입 활성화**: 가입까지 테스트하려면 `y` → Enter. 비활성화하려면 Enter만 누릅니다. 키를 생략하면 이 질문 없이 가입이 비활성화됩니다.
`uploads` 폴더를 만들고 NAS 로그인 사용자의 UID/GID로 저장 권한을 준비합니다. Docker 앱도 같은 UID/GID로 실행하므로 새로 받은 파일을 NAS 파일 관리자에서 읽을 수 있습니다. 처음에는 권한 준비용 작은 Alpine 이미지가 다운로드될 수 있습니다. 기존 프로젝트가 실행 중이면 먼저 중지하세요.
`SESSION_SECRET`과 `DISCOVERY_SECRET`은 자동 생성됩니다. NAS 포트와 컨테이너 포트는 모두 **50189**를 사용합니다.
마지막에 표시된 `compose.nas.yaml` 경로를 복사해 둡니다. `.env`는 NAS에만 남고 Git에 올라가지 않습니다. 기존 `.env`가 있으면 덮어쓰지 않습니다.
@@ -209,17 +211,15 @@ https://hop.noname.company
현재 흐름은 **수락 → 서버로 업로드 → NAS에 원본 저장 → 수신자가 다운로드 → 보관 기간 종료 후 정리**입니다. 다운로드 완료 직후 삭제하는 방식이 아닙니다. 임시 저장과 받은 항목도 같은 파일 저장소를 사용하며 DB의 접근 권한으로 사용자별 목록을 구분합니다.
| 데이터 | Docker 볼륨 | 컨테이너 내부 위치 |
| --------------------------------- | ------------------ | --------------------------------------------------- |
| 업로드 완료된 파일 원본 | `hop_hop_uploads` | `/data/uploads/originals/` |
| 업로드 중인 파일·조각 | `hop_hop_uploads` | `/data/uploads/temporary/`, `/data/uploads/chunks/` |
| 계정·파일명·소유자·만료·접근 권한 | `hop_hop_database` | `/data/database/hop.sqlite` |
| 데이터 | NAS 저장 위치 | 컨테이너 내부 위치 |
| --------------------------------- | ----------------------------------------------- | --------------------------------------------------- |
| 업로드 완료된 파일 원본 | `hop/uploads/originals/` | `/data/uploads/originals/` |
| 업로드 중인 파일·조각 | `hop/uploads/temporary/`, `hop/uploads/chunks/` | `/data/uploads/temporary/`, `/data/uploads/chunks/` |
| 계정·파일명·소유자·만료·접근 권한 | `hop_hop_database` Docker 볼륨 | `/data/database/hop.sqlite` |
원본 파일은 무작위 식별자로 저장합니다. 원래 파일명과 사용자 연결 정보는 DB에 있습니다. NAS의 실제 저장 경로는 아래 명령으로 확인합니다.
NAS 파일 관리자에서 **공유 폴더 → docker → projects → apps → hop → uploads → originals**로 들어갑니다. 전체 경로는 `/volume1/docker/projects/apps/hop/uploads/originals`입니다. 원본은 무작위 식별자로 저장하고 원래 파일명과 사용자 연결 정보는 DB에 둡니다. 업로드 폴더는 Git과 Docker 이미지에서 제외합니다. NAS 공유 폴더를 컨테이너에 연결하는 방식은 [Docker 공식 설명](https://docs.docker.com/engine/storage/bind-mounts/)과 [UGREEN 공식 안내](https://support.ugnas.com/detail/article/en-US/611)를 참고했습니다.
```sh
sudo docker volume inspect hop_hop_uploads --format '{{.Mountpoint}}'
```
파일 관리자로 원본을 수동 삭제하면 다음 보관함·용량 조회, 다운로드·재전송 시 확인하거나 약 1분 간격의 정리 작업에서 감지합니다. 같은 원본의 모든 계정·기기 항목은 만료 처리해 목록과 전송 선택에서 제외하고 사용량을 회수합니다. 열린 화면에는 갱신 이벤트를 보내며 이미 열린 다운로드·전송 화면에서는 삭제 안내를 표시합니다. 권한 오류는 503 안내를 반환하고 DB 기록을 삭제하지 않습니다. 원본을 수동으로 넣거나 이름을 바꿔도 새 보관함 항목을 자동 생성하지 않습니다. `temporary`·`chunks`는 진행 중 업로드에 사용하는 폴더입니다.
- **다운로드 실패·수신자 이탈:** 이미 저장된 원본은 바로 삭제되지 않습니다. 받은 항목에 저장된 파일은 로그인 후 만료 전에 다시 다운로드할 수 있습니다. 개인 용량 초과로 받은 항목에 저장되지 않았거나 비로그인으로 받은 경우, 사이트를 떠난 뒤 이전 다운로드를 다시 찾는 UI는 제공하지 않습니다. 이때는 다시 전송하는 방법을 사용합니다. 다운로드 시작 표시는 기기에 저장이 끝났다는 확인을 의미하지 않습니다.
- **업로드 도중 이탈:** 완료되지 않은 업로드는 받은 항목에 생성되지 않습니다. 취소된 조각은 제거하며, 남은 미완료·고아 업로드는 기본 24시간 후 정리합니다. 브라우저를 떠난 뒤 자동 전송·자동 다운로드 재개는 보장하지 않습니다.
@@ -252,9 +252,67 @@ git pull --ff-only origin main
앱의 재배포 버튼이 이미지 빌드까지 함께 실행한다면 그 버튼으로 진행해도 됩니다. 기존 이미지를 그대로 쓰는 단순 재시작은 새 코드를 반영하지 않습니다.
**이번 업데이트도 이 순서로 진행하면 됩니다.** `sh scripts/configure-nas.sh`를 다시 실행하거나 YAML을 다시 불러올 필요가 없습니다. NPM·Cloudflare Tunnel 설정도 그대로 유지합니다. 위치 검색은 양쪽 브라우저에서 위치 권한을 허용하면 자동으로 동작합니다.
**저장소 전환을 완료한 뒤의 코드·UI 업데이트는 이 순서만 진행합니다.** 설정 스크립트를 다시 실행하거나 YAML을 다시 불러올 필요가 없습니다. NPM·Cloudflare Tunnel 설정도 그대로 유지합니다. 위치 검색은 양쪽 브라우저에서 위치 권한을 허용하면 자동으로 동작합니다.
DB와 파일은 `hop_hop_database`, `hop_hop_uploads` 볼륨에 있고 `.env`와 생성된 `compose.nas.yaml`은 Git에서 제외되어 있으므로 일반 코드 업데이트로 덮어쓰지 않습니다. 같은 볼륨을 사용하는 재배포에서 데이터는 유지됩니다.
DB는 `hop_hop_database` 볼륨, 파일은 hop 폴더의 `uploads`에 있습니다. `uploads`, `.env`, 생성된 `compose.nas.yaml`은 Git에서 제외하므로 일반 코드 업데이트로 덮어쓰지 않습니다. 같은 저장 경로를 사용하는 재배포에서 데이터는 유지됩니다.
### 7-1. 업로드 폴더 전환과 테스트 자료 초기화
**이번에 한 번만 실행합니다. 기존 테스트 계정·기기 이름·방·전송 기록·보관 파일은 모두 삭제합니다. `.env`와 소스는 유지합니다.** 다음 업데이트에서 반복하지 마세요.
1. Docker 앱 → 프로젝트 → hop → **중지**.
2. SSH에서 hop 폴더로 이동:
```sh
cd /volume1/docker/projects/apps/hop
```
3. 최신 코드 받기:
```sh
git pull --ff-only origin main
```
4. 중지된 hop 컨테이너 제거. 프로젝트 소스 폴더는 남습니다:
```sh
sudo docker rm hop
```
5. 기존 테스트 DB와 업로드 볼륨 삭제:
```sh
sudo docker volume rm hop_hop_database hop_hop_uploads
```
6. 새 업로드 폴더·권한·YAML 준비. 기존 `.env`의 비밀값은 재발급하지 않습니다:
```sh
sudo sh scripts/configure-nas.sh
```
7. Docker 앱 → 기존 hop 프로젝트의 **편집 / Compose 설정**에서 새 `compose.nas.yaml`을 다시 불러옵니다. 파일 가져오기가 없으면 아래 내용을 Compose 편집기에 붙여넣습니다:
```sh
cat compose.nas.yaml
```
8. **이미지 재빌드 → 재배포 / 시작**. `healthy` 확인.
9. SSH에서 실제 파일 연결 경로 확인:
```sh
sudo docker inspect --format '{{range .Mounts}}{{if eq .Destination "/data/uploads"}}{{.Type}} → {{.Source}}{{end}}{{end}}' hop
```
다음처럼 나오면 완료입니다:
```text
bind → /volume1/docker/projects/apps/hop/uploads
```
10. 브라우저/PWA 새로고침 → 필요하면 다시 회원가입 → 작은 파일 전송 → NAS 파일 관리자에서 **hop → uploads → originals** 확인.
이후에는 Git pull → 이미지 재빌드 → 재배포만 진행합니다. `uploads` 폴더와 DB는 그대로 유지합니다.
### 추가 작업이 필요한 업데이트
@@ -287,11 +345,11 @@ chmod 700 backups
```
```sh
sudo docker volume inspect hop_hop_database hop_hop_uploads >/dev/null
sudo docker volume inspect hop_hop_database >/dev/null
```
```sh
sudo docker run --rm -v hop_hop_database:/source/database:ro -v hop_hop_uploads:/source/uploads:ro -v "$PWD/backups:/backup" alpine:3.22 sh -c 'tar -czf "/backup/hop-$(date +%Y%m%d-%H%M%S).tar.gz" -C /source database uploads'
sudo docker run --rm -v hop_hop_database:/source/database:ro -v "$PWD/uploads:/source/uploads:ro" -v "$PWD/backups:/backup" alpine:3.22 sh -c 'tar -czf "/backup/hop-$(date +%Y%m%d-%H%M%S).tar.gz" -C /source database uploads'
```
```sh
@@ -343,7 +401,7 @@ git pull --ff-only origin main
```
```sh
sh scripts/configure-nas.sh
sudo sh scripts/configure-nas.sh
```
Docker 앱에서 **새 compose.nas.yaml 다시 불러오기 → 저장 → 재빌드 → 시작 / 재배포** 순서로 진행합니다. 새 YAML은 `pull_policy: never`를 사용합니다. 기존 YAML을 그대로 쓰면 수정이 적용되지 않습니다.
@@ -365,3 +423,7 @@ Docker 앱에서 **새 compose.nas.yaml 다시 불러오기 → 저장 → 재
일반 텍스트·단일 링크 자동 판별과 텍스트 수신 미리보기·복사를 추가했습니다. 이번 변경은 기존 텍스트 컬럼을 재사용하므로 DB 마이그레이션·YAML 변경 없이 **Git pull → 이미지 재빌드 → 재배포**로 적용합니다. 기존 `.env`에 새 설정이 없어도 텍스트 한도는 256KiB입니다. 한도를 별도로 조정할 때만 `MAX_TEXT_PAYLOAD_SIZE_KB`를 변경하고 환경설정을 재배포하세요.
브라우저마다 클립보드 권한 방식이 다릅니다. 첫 읽기가 제한되어도 직접 입력하거나 ‘다시 가져오기’를 사용할 수 있습니다. ‘다시 가져오기’에서 브라우저의 붙여넣기 확인이 뜨는 것은 권한 확인 절차입니다. 기기 카드 자체의 텍스트 선택은 차단하고 입력창·받은 텍스트의 직접 선택과 붙여넣기는 유지합니다.
## 화면 안내 정리
보관함 탭은 **받은 항목 → 임시 저장** 순서입니다. 항목의 날짜는 **보관 만료 · 날짜/시간까지**로 표시합니다. 계정 설정에서 이메일은 표시만 하고 변경 버튼은 숨깁니다. 비밀번호 변경은 유지합니다. 오류 토스트는 텍스트와 닫기 아이콘의 양쪽 여백을 맞추며 기본 5초 뒤 닫힙니다. 네트워크 연결 실패는 한국어로 안내하고 닫기 버튼으로 즉시 닫을 수도 있습니다.