| public | ||
| scripts | ||
| src | ||
| systemd | ||
| .env.example | ||
| .gitignore | ||
| bun.lock | ||
| eslint.config.mjs | ||
| LICENSE | ||
| next.config.ts | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
TPR Mirror
개인이 운영하는 비영리 미러서버 웹사이트
기술 스택
- Framework: Next.js 16.0.0 (App Router)
- Runtime: Bun
- Language: TypeScript
- Styling: SCSS
- UI: React 19.2.0
설치 및 실행
1. 의존성 설치
bun install
2. 환경 변수 설정
.env.example 파일을 복사하여 .env 파일을 생성하고 필요한 값을 설정합니다.
cp .env.example .env
주요 환경 변수:
SERVER_HOST: 서버 호스트 (기본값: 0.0.0.0)SERVER_PORT: 서버 포트 (기본값: 3000)NODE_ENV: 환경 (development/production/test)SERVER_SECRET: API 인증 시크릿 키 (프로덕션에서 반드시 변경!)CORS_ORIGIN: CORS 허용 도메인CONFIG_FILE: 미러 설정 파일 경로 (기본값: mirrors.json)
3. 개발 서버 실행
bun run dev
브라우저에서 http://localhost:3000을 열어 확인합니다.
4. 프로덕션 빌드
bun run build
bun run start
API 엔드포인트
GET /api/mirrors
미러 목록을 조회합니다.
응답 예시:
[
{
"id": "1234567890",
"name": "Fedora",
"path": "/fedora",
"last_sync_at": "2025-10-23T00:00:00.000Z",
"status": 0
}
]
상태 코드:
0: SUCCESS (성공)1: FAILED (실패)2: SYNCING (동기화 중)
POST /api/mirrors
미러 정보를 생성 또는 업데이트합니다.
헤더:
Authorization: your-secret-key
요청 본문:
{
"id": "1234567890",
"name": "Fedora",
"path": "/fedora",
"last_sync_at": "2025-10-23T00:00:00.000Z",
"status": 0
}
응답:
{
"ok": 1
}
미러 데이터 관리
초기화
서버가 처음 시작될 때, CONFIG_FILE에 지정된 파일(기본값: mirrors.json)이 없으면 자동으로 빈 배열로 생성됩니다.
데이터 구조
interface MirrorData {
id: string; // 고유 ID
name: string; // 표시 이름 (예: "Fedora", "Linux Mint Packages")
path: string; // 미러 경로 (예: "/fedora", "/linuxmint-packages")
last_sync_at: Date; // 마지막 동기화 시간
status: MirrorStatus; // 상태 (0: SUCCESS, 1: FAILED, 2: SYNCING)
}
동작 방식
- 자동 생성:
mirrors.json파일이 없으면 서버 시작 시 자동으로 빈 배열([])로 생성됩니다. - 업데이트: POST 요청으로 미러 정보를 전송하면, 동일한
id를 가진 미러의 정보가 업데이트됩니다. - 유동적 관리: 파일 시스템 기반으로 동작하여, 직접
mirrors.json을 편집하거나 API를 통해 관리할 수 있습니다.
프로젝트 구조
tpr-mirror/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── api/ # API 라우트
│ │ │ └── mirrors/ # 미러 API
│ │ ├── layout.tsx # 루트 레이아웃
│ │ ├── page.tsx # 메인 페이지
│ │ └── globals.scss # 전역 스타일
│ ├── components/ # React 컴포넌트
│ │ ├── Card.tsx
│ │ ├── Header.tsx
│ │ └── Mirror.tsx
│ ├── lib/ # 유틸리티 및 타입
│ │ ├── env.ts # 환경 변수 검증
│ │ ├── snowflake_id.ts
│ │ └── types.ts # TypeScript 타입
│ └── services/ # 비즈니스 로직
│ └── mirror.ts # 미러 서비스
├── public/ # 정적 파일
├── .env.example # 환경 변수 예제
└── mirrors.json.example # 미러 설정 예제
반응형 디자인
- 데스크톱 (1024px+): 2열 그리드
- 태블릿 (768px - 1023px): 2열 그리드, 최적화된 간격
- 모바일 (767px 이하): 1열 그리드
- 소형 모바일 (560px 이하): 폰트 크기 및 간격 조정
프로덕션 배포
Standalone 모드로 배포하기
이 프로젝트는 Next.js의 Standalone 출력 모드를 사용하여 최적화된 프로덕션 배포를 지원합니다.
자동 설치 (권장)
제공된 설치 스크립트를 사용하여 자동으로 배포할 수 있습니다:
sudo bash scripts/install.sh
설치 스크립트가 수행하는 작업:
- Next.js 애플리케이션을 프로덕션 모드로 빌드
- 기존 설치 백업 (있는 경우)
/usr/share/tpr-mirror/에 애플리케이션 파일 복사- 환경 변수 설정
- systemd 서비스 설치 및 활성화
- 서비스 시작
수동 설치
수동으로 배포하려면 다음 단계를 따르세요:
1. 프로덕션 빌드
bun run build
빌드가 완료되면 .next/standalone 폴더가 생성됩니다.
2. 파일 복사
sudo mkdir -p /usr/share/tpr-mirror
sudo cp -r .next/standalone/* /usr/share/tpr-mirror/
sudo cp -r public /usr/share/tpr-mirror/
sudo cp -r .next/static /usr/share/tpr-mirror/.next/
sudo cp .env.production /usr/share/tpr-mirror/.env
3. 환경 변수 설정
sudo nano /usr/share/tpr-mirror/.env
중요: SERVER_SECRET을 강력한 랜덤 문자열로 변경하세요!
# 랜덤 시크릿 생성 예시
openssl rand -base64 32
4. systemd 서비스 설치
서비스 파일은 템플릿이므로, 사용자 정보를 치환하여 설치해야 합니다:
# 현재 사용자와 그룹 정보로 서비스 파일 생성
CURRENT_USER=$(whoami)
CURRENT_GROUP=$(id -gn)
sudo sed -e "s/{{USER}}/$CURRENT_USER/g" \
-e "s/{{GROUP}}/$CURRENT_GROUP/g" \
systemd/tpr-mirror.service > /tmp/tpr-mirror.service
sudo mv /tmp/tpr-mirror.service /etc/systemd/system/tpr-mirror.service
sudo systemctl daemon-reload
sudo systemctl enable tpr-mirror
sudo systemctl start tpr-mirror
또는 간단하게:
# 설치 스크립트 사용 (자동으로 사용자 정보 치환)
sudo npm run install
5. 서비스 상태 확인
sudo systemctl status tpr-mirror
nginx 리버스 프록시 설정
Next.js 애플리케이션은 포트 3000에서 실행됩니다. nginx를 리버스 프록시로 설정하여 80/443 포트에서 서빙할 수 있습니다.
nginx 설정 예시
/etc/nginx/conf.d/tpr-mirror.conf:
upstream tpr_mirror {
server localhost:3000;
keepalive 64;
}
server {
listen 80;
server_name your-domain.com; # 도메인으로 변경
# Next.js 애플리케이션
location / {
proxy_pass http://tpr_mirror;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# 미러 파일 직접 서빙 (예시)
location /fedora {
alias /srv/mirror/fedora/;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}
설정 후 nginx 재시작:
sudo nginx -t # 설정 검증
sudo systemctl reload nginx
서비스 관리
# 서비스 시작
sudo systemctl start tpr-mirror
# 서비스 중지
sudo systemctl stop tpr-mirror
# 서비스 재시작
sudo systemctl restart tpr-mirror
# 서비스 상태 확인
sudo systemctl status tpr-mirror
# 로그 확인
sudo journalctl -u tpr-mirror -f
# 최근 50줄 로그
sudo journalctl -u tpr-mirror -n 50
제거
애플리케이션을 완전히 제거하려면:
sudo bash scripts/uninstall.sh
제거 스크립트는 다음을 수행합니다:
- 서비스 중지 및 비활성화
- 설치 디렉토리 백업 및 제거
- 로그 제거 (선택 사항)
- 이전 백업 정리 (선택 사항)
업데이트
새 버전으로 업데이트하려면:
# 1. 최신 코드 가져오기
git pull
# 2. 의존성 업데이트 (필요시)
bun install
# 3. 재설치
sudo bash scripts/install.sh
기존 설치는 자동으로 백업되며, 문제 발생 시 /usr/share/tpr-mirror.backup.YYYYMMDD_HHMMSS/에서 복원할 수 있습니다.
배포 구조
/usr/share/tpr-mirror/ # 애플리케이션 루트
├── server.js # Standalone 서버
├── .next/ # Next.js 빌드 출력
│ └── static/ # 정적 리소스
├── public/ # Public 파일
├── package.json # 최소 의존성
├── node_modules/ # 필수 모듈만 포함
├── .env # 환경 변수
└── mirrors.json # 미러 데이터 (자동 생성)
/var/log/tpr-mirror/ # 로그 디렉토리
/etc/systemd/system/
└── tpr-mirror.service # systemd 서비스
문제 해결
서비스가 시작되지 않을 때
# 상세 로그 확인
sudo journalctl -u tpr-mirror -n 100 --no-pager
# 환경 변수 확인
sudo cat /usr/share/tpr-mirror/.env
# 권한 확인
ls -la /usr/share/tpr-mirror/
# 수동 실행 테스트
cd /usr/share/tpr-mirror
node server.js
포트 충돌
다른 애플리케이션이 3000 포트를 사용 중이면:
.env파일에서SERVER_PORT변경- nginx 설정에서
proxy_pass포트 변경 - 서비스 재시작
mirrors.json 권한 문제
sudo chown devproje:devproje /usr/share/tpr-mirror/mirrors.json
sudo chmod 644 /usr/share/tpr-mirror/mirrors.json
미러 동기화 스크립트
개요
scripts/sync-mirror.sh는 rsync를 사용하여 미러를 동기화하고, API를 통해 웹 UI에 실시간 상태를 업데이트하는 스크립트입니다.
설치
# 스크립트를 /usr/bin으로 복사 (또는 심볼릭 링크)
sudo cp scripts/sync-mirror.sh /usr/bin/sync-mirror.sh
sudo chmod +x /usr/bin/sync-mirror.sh
# 또는 심볼릭 링크 사용
sudo ln -s /usr/share/tpr-mirror/scripts/sync-mirror.sh /usr/bin/sync-mirror.sh
사용법
sync-mirror.sh <mirror_url> <target_mirror_path> <mirror_name> [mirror_id]
인자:
mirror_url: rsync URL (예:rsync://mirrors.kernel.org/fedora/)target_mirror_path: 디렉토리 이름 (예:fedora)mirror_name: 표시 이름 (예:"Fedora")mirror_id: 고유 ID (선택, 환경 변수MIRROR_ID로도 설정 가능)
환경 변수:
MIRROR_ID: 미러 고유 ID (필수, 인자 또는 환경 변수로 제공)API_URL: API 서버 주소 (기본값:http://localhost:3000)API_SECRET: API 인증 키 (기본값:your-secret-key-here,.env의SERVER_SECRET과 동일하게 설정)
예시
# 기본 사용 (ID를 인자로 전달)
sync-mirror.sh rsync://mirrors.kernel.org/fedora/ fedora "Fedora" 1234567890
# 환경 변수로 ID 전달
MIRROR_ID=1234567890 sync-mirror.sh rsync://mirrors.kernel.org/fedora/ fedora "Fedora"
# API 설정과 함께
API_URL=http://localhost:3000 \
API_SECRET=your-production-secret \
MIRROR_ID=1234567890 \
sync-mirror.sh rsync://mirrors.kernel.org/fedora/ fedora "Fedora"
Crontab 설정
# /etc/cron.d/tpr-mirror 또는 crontab -e
# 환경 변수 설정
API_URL=http://localhost:3000
API_SECRET=your-production-secret-here
# Linux Mint Packages (매 2시간마다)
MIRROR_ID=1234567890
0 */2 * * * root /usr/bin/sync-mirror.sh "rsync-packages.linuxmint.com::packages/" "linuxmint-packages" "Linux Mint Packages" >> /var/log/rsync_sync_l_mint_pkg.log 2>&1
# Fedora (매일 새벽 2시)
MIRROR_ID=1234567891
0 2 * * * root /usr/bin/sync-mirror.sh "rsync://mirrors.kernel.org/fedora/" "fedora" "Fedora" >> /var/log/rsync_sync_fedora.log 2>&1
# Kali Linux (매일 새벽 3시)
MIRROR_ID=1234567892
0 3 * * * root /usr/bin/sync-mirror.sh "rsync://archive-4.kali.org/kali/" "kali" "Kali Linux" >> /var/log/rsync_sync_kali.log 2>&1
# Ubuntu (매일 새벽 4시)
MIRROR_ID=1234567893
0 4 * * * root /usr/bin/sync-mirror.sh "rsync://archive.ubuntu.com/ubuntu/" "ubuntu" "Ubuntu" >> /var/log/rsync_sync_ubuntu.log 2>&1
동작 흐름
- 동기화 시작: API에
status: 2 (SYNCING)전송 - rsync 실행: 미러 데이터 동기화
- 결과 전송: 성공 시
status: 0 (SUCCESS), 실패 시status: 1 (FAILED)전송 - 웹 UI 업데이트: 실시간으로 상태가 화면에 반영됨
로그
모든 동기화 로그는 /mnt/drive/logs/<mirror_path>.log에 저장됩니다.
# 로그 확인
tail -f /mnt/drive/logs/fedora.log
ID 관리
각 미러는 고유한 ID를 가져야 하며, 한 번 설정한 ID는 변경하지 마세요.
ID 생성 방법:
# Timestamp 기반 ID 생성
echo $(date +%s)000
# 예: 1730000000000
# 또는 간단히 순차적으로
1234567890 # linuxmint-packages
1234567891 # fedora
1234567892 # kali
1234567893 # ubuntu
라이선스
해당 미러의 소스코드 라이선스는 GPL-2.0 License를 따릅니다. 그 이외의 이미지 에셋(파비콘, 이미지 등)은 Project_IO에게 저작권이 있습니다.